`;
+
+exports[`Storyshots Table|Table Default 1`] = `
+
+`;
+
+exports[`Storyshots Table|Table Empty 1`] = `
+
+
+ No data found.
+
+`;
+
+exports[`Storyshots Table|Table TextColumn 1`] = `
+
+
+
+ |
+ Id
+
+ |
+
+ Name
+
+ |
+
+ Description
+
+ |
+
+
+
+
+ |
+ 21
+ |
+
+ Pommes
+ |
+
+ Fried potato sticks
+ |
+
+
+ |
+ 42
+ |
+
+ Quarter-Pounder
+ |
+
+ Big burger
+ |
+
+
+ |
+ -84
+ |
+
+ Icecream
+ |
+
+ Cold dessert
+ |
+
+
+
+`;
diff --git a/scm-ui/ui-components/src/buttons/DownloadButton.tsx b/scm-ui/ui-components/src/buttons/DownloadButton.tsx
index 0e63d34073..98860cecc6 100644
--- a/scm-ui/ui-components/src/buttons/DownloadButton.tsx
+++ b/scm-ui/ui-components/src/buttons/DownloadButton.tsx
@@ -2,8 +2,8 @@ import React from "react";
type Props = {
displayName: string;
- url: string;
- disabled: boolean;
+ url?: string;
+ disabled?: boolean;
onClick?: () => void;
};
diff --git a/scm-ui/ui-components/src/config/Configuration.tsx b/scm-ui/ui-components/src/config/Configuration.tsx
index 967feabb2b..453fac421e 100644
--- a/scm-ui/ui-components/src/config/Configuration.tsx
+++ b/scm-ui/ui-components/src/config/Configuration.tsx
@@ -1,8 +1,7 @@
-import React from "react";
+import React, { FormEvent } from "react";
import { WithTranslation, withTranslation } from "react-i18next";
import { Links, Link } from "@scm-manager/ui-types";
-import { apiClient, SubmitButton, Loading, ErrorNotification } from "../";
-import { FormEvent } from "react";
+import { apiClient, Level, SubmitButton, Loading, ErrorNotification } from "../";
type RenderProps = {
readOnly: boolean;
@@ -179,7 +178,9 @@ class Configuration extends React.Component
{
>
);
diff --git a/scm-ui/ui-components/src/forms/AddEntryToTableField.tsx b/scm-ui/ui-components/src/forms/AddEntryToTableField.tsx
index d1e1b77cb6..3e18674244 100644
--- a/scm-ui/ui-components/src/forms/AddEntryToTableField.tsx
+++ b/scm-ui/ui-components/src/forms/AddEntryToTableField.tsx
@@ -1,7 +1,8 @@
import React, { MouseEvent } from "react";
-
-import { AddButton } from "../buttons";
+import styled from "styled-components";
+import Level from "../layout/Level";
import InputField from "./InputField";
+import AddButton from "../buttons/AddButton";
type Props = {
addEntry: (p: string) => void;
@@ -17,6 +18,22 @@ type State = {
entryToAdd: string;
};
+const StyledLevel = styled(Level)`
+ align-items: stretch;
+ margin-bottom: 1rem !important; // same margin as field
+`;
+
+const StyledInputField = styled(InputField)`
+ width: 100%;
+ margin-right: 1.5rem;
+`;
+
+const StyledField = styled.div.attrs(props => ({
+ className: "field"
+}))`
+ align-self: flex-end;
+`;
+
class AddEntryToTableField extends React.Component {
constructor(props: Props) {
super(props);
@@ -37,23 +54,29 @@ class AddEntryToTableField extends React.Component {
render() {
const { disabled, buttonLabel, fieldLabel, errorMessage, helpText } = this.props;
return (
- <>
-
-
- >
+
+ }
+ right={
+
+
+
+ }
+ />
);
}
diff --git a/scm-ui/ui-components/src/forms/AutocompleteAddEntryToTableField.tsx b/scm-ui/ui-components/src/forms/AutocompleteAddEntryToTableField.tsx
index e09b0d1ef2..8287249858 100644
--- a/scm-ui/ui-components/src/forms/AutocompleteAddEntryToTableField.tsx
+++ b/scm-ui/ui-components/src/forms/AutocompleteAddEntryToTableField.tsx
@@ -1,6 +1,7 @@
import React, { MouseEvent } from "react";
-
-import { AutocompleteObject, SelectValue } from "@scm-manager/ui-types";
+import styled from "styled-components";
+import { SelectValue } from "@scm-manager/ui-types";
+import Level from "../layout/Level";
import Autocomplete from "../Autocomplete";
import AddButton from "../buttons/AddButton";
@@ -20,6 +21,11 @@ type State = {
selectedValue?: SelectValue;
};
+const StyledAutocomplete = styled(Autocomplete)`
+ width: 100%;
+ margin-right: 1.5rem;
+`;
+
class AutocompleteAddEntryToTableField extends React.Component {
constructor(props: Props) {
super(props);
@@ -41,21 +47,26 @@ class AutocompleteAddEntryToTableField extends React.Component {
const { selectedValue } = this.state;
return (
-
+
+ }
+ right={
+
+ }
+ />
);
}
diff --git a/scm-ui/ui-components/src/forms/InputField.tsx b/scm-ui/ui-components/src/forms/InputField.tsx
index a0fd1c631c..d832d8a7d6 100644
--- a/scm-ui/ui-components/src/forms/InputField.tsx
+++ b/scm-ui/ui-components/src/forms/InputField.tsx
@@ -15,6 +15,7 @@ type Props = {
errorMessage?: string;
disabled?: boolean;
helpText?: string;
+ className?: string;
};
class InputField extends React.Component {
@@ -47,11 +48,21 @@ class InputField extends React.Component {
};
render() {
- const { type, placeholder, value, validationError, errorMessage, disabled, label, helpText } = this.props;
+ const {
+ type,
+ placeholder,
+ value,
+ validationError,
+ errorMessage,
+ disabled,
+ label,
+ helpText,
+ className
+ } = this.props;
const errorView = validationError ? "is-danger" : "";
const helper = validationError ? {errorMessage}
: "";
return (
-
+
{
render() {
- const { className, left, right } = this.props;
+ const { className, left, children, right } = this.props;
+ let child = null;
+ if (children) {
+ child =
{children}
;
+ }
+
return (
);
diff --git a/scm-ui/ui-components/src/table/Column.tsx b/scm-ui/ui-components/src/table/Column.tsx
new file mode 100644
index 0000000000..7f95535c36
--- /dev/null
+++ b/scm-ui/ui-components/src/table/Column.tsx
@@ -0,0 +1,18 @@
+import React, { FC, ReactNode } from "react";
+import { ColumnProps } from "./types";
+
+type Props = ColumnProps & {
+ children: (row: any, columnIndex: number) => ReactNode;
+};
+
+const Column: FC
= ({ row, columnIndex, children }) => {
+ if (row === undefined) {
+ throw new Error("missing row, use column only as child of Table");
+ }
+ if (columnIndex === undefined) {
+ throw new Error("missing row, use column only as child of Table");
+ }
+ return <>{children(row, columnIndex)}>;
+};
+
+export default Column;
diff --git a/scm-ui/ui-components/src/table/SortIcon.tsx b/scm-ui/ui-components/src/table/SortIcon.tsx
new file mode 100644
index 0000000000..0d553041c6
--- /dev/null
+++ b/scm-ui/ui-components/src/table/SortIcon.tsx
@@ -0,0 +1,19 @@
+import React, { FC } from "react";
+import styled from "styled-components";
+import Icon from "../Icon";
+
+type Props = {
+ name: string;
+ isVisible: boolean;
+};
+
+const IconWithMarginLeft = styled(Icon)`
+ visibility: ${(props: Props) => (props.isVisible ? "visible" : "hidden")};
+ margin-left: 0.25em;
+`;
+
+const SortIcon: FC = (props: Props) => {
+ return ;
+};
+
+export default SortIcon;
diff --git a/scm-ui/ui-components/src/table/Table.stories.tsx b/scm-ui/ui-components/src/table/Table.stories.tsx
new file mode 100644
index 0000000000..b4df2a1cb8
--- /dev/null
+++ b/scm-ui/ui-components/src/table/Table.stories.tsx
@@ -0,0 +1,53 @@
+import React from "react";
+import { storiesOf } from "@storybook/react";
+import Table from "./Table";
+import Column from "./Column";
+import TextColumn from "./TextColumn";
+
+storiesOf("Table|Table", module)
+ .add("Default", () => (
+
+ {(row: any) => {row.firstname}
}
+ {
+ return (a: any, b: any) => {
+ if (a.lastname > b.lastname) {
+ return -1;
+ } else if (a.lastname < b.lastname) {
+ return 1;
+ } else {
+ return 0;
+ }
+ };
+ }}
+ >
+ {(row: any) => {row.lastname}}
+
+ {(row: any) => {row.email}}
+
+ ))
+ .add("TextColumn", () => (
+
+ ))
+ .add("Empty", () => (
+
+ ));
diff --git a/scm-ui/ui-components/src/table/Table.tsx b/scm-ui/ui-components/src/table/Table.tsx
new file mode 100644
index 0000000000..4d12e1c681
--- /dev/null
+++ b/scm-ui/ui-components/src/table/Table.tsx
@@ -0,0 +1,120 @@
+import React, { FC, ReactElement, useEffect, useState } from "react";
+import styled from "styled-components";
+import { Comparator } from "./types";
+import SortIcon from "./SortIcon";
+import Notification from "../Notification";
+
+const StyledTable = styled.table.attrs(() => ({
+ className: "table content is-hoverable"
+}))``;
+
+type Props = {
+ data: any[];
+ sortable?: boolean;
+ emptyMessage?: string;
+ children: Array;
+};
+
+const Table: FC = ({ data, sortable, children, emptyMessage }) => {
+ const [tableData, setTableData] = useState(data);
+ useEffect(() => {
+ setTableData(data);
+ }, [data]);
+ const [ascending, setAscending] = useState(false);
+ const [lastSortBy, setlastSortBy] = useState();
+ const [hoveredColumnIndex, setHoveredColumnIndex] = useState();
+
+ const isSortable = (child: ReactElement) => {
+ return sortable && child.props.createComparator;
+ };
+
+ const sortFunctions: Comparator | undefined[] = [];
+ React.Children.forEach(children, (child, index) => {
+ if (child && isSortable(child)) {
+ sortFunctions.push(child.props.createComparator(child.props, index));
+ } else {
+ sortFunctions.push(undefined);
+ }
+ });
+
+ const mapDataToColumns = (row: any) => {
+ return (
+
+ {React.Children.map(children, (child, columnIndex) => {
+ return | {React.cloneElement(child, { ...child.props, columnIndex, row })} | ;
+ })}
+
+ );
+ };
+
+ const sortDescending = (sortAscending: (a: any, b: any) => number) => {
+ return (a: any, b: any) => {
+ return sortAscending(a, b) * -1;
+ };
+ };
+
+ const tableSort = (index: number) => {
+ const sortFn = sortFunctions[index];
+ if (!sortFn) {
+ throw new Error(`column with index ${index} is not sortable`);
+ }
+ const sortableData = [...tableData];
+ let sortOrder = ascending;
+ if (lastSortBy !== index) {
+ setAscending(true);
+ sortOrder = true;
+ }
+ const sortFunction = sortOrder ? sortFn : sortDescending(sortFn);
+ sortableData.sort(sortFunction);
+ setTableData(sortableData);
+ setAscending(!sortOrder);
+ setlastSortBy(index);
+ };
+
+ const shouldShowIcon = (index: number) => {
+ return index === lastSortBy || index === hoveredColumnIndex;
+ };
+
+ if (!tableData || tableData.length <= 0) {
+ if (emptyMessage) {
+ return {emptyMessage};
+ } else {
+ return null;
+ }
+ }
+
+ return (
+
+
+
+ {React.Children.map(children, (child, index) => (
+ | tableSort(index) : undefined}
+ onMouseEnter={() => setHoveredColumnIndex(index)}
+ onMouseLeave={() => setHoveredColumnIndex(undefined)}
+ >
+ {child.props.header}
+ {isSortable(child) && renderSortIcon(child, ascending, shouldShowIcon(index))}
+ |
+ ))}
+
+
+ {tableData.map(mapDataToColumns)}
+
+ );
+};
+
+Table.defaultProps = {
+ sortable: true
+};
+
+const renderSortIcon = (child: ReactElement, ascending: boolean, showIcon: boolean) => {
+ if (child.props.ascendingIcon && child.props.descendingIcon) {
+ return ;
+ } else {
+ return ;
+ }
+};
+
+export default Table;
diff --git a/scm-ui/ui-components/src/table/TextColumn.tsx b/scm-ui/ui-components/src/table/TextColumn.tsx
new file mode 100644
index 0000000000..0ad5375503
--- /dev/null
+++ b/scm-ui/ui-components/src/table/TextColumn.tsx
@@ -0,0 +1,28 @@
+import React, { FC } from "react";
+import { ColumnProps } from "./types";
+
+type Props = ColumnProps & {
+ dataKey: string;
+};
+
+const TextColumn: FC = ({ row, dataKey }) => {
+ return row[dataKey];
+};
+
+TextColumn.defaultProps = {
+ createComparator: (props: Props) => {
+ return (a: any, b: any) => {
+ if (a[props.dataKey] < b[props.dataKey]) {
+ return -1;
+ } else if (a[props.dataKey] > b[props.dataKey]) {
+ return 1;
+ } else {
+ return 0;
+ }
+ };
+ },
+ ascendingIcon: "sort-alpha-down-alt",
+ descendingIcon: "sort-alpha-down"
+};
+
+export default TextColumn;
diff --git a/scm-ui/ui-components/src/table/index.ts b/scm-ui/ui-components/src/table/index.ts
new file mode 100644
index 0000000000..c99a4e64c1
--- /dev/null
+++ b/scm-ui/ui-components/src/table/index.ts
@@ -0,0 +1,4 @@
+export { default as Table } from "./Table";
+export { default as Column } from "./Column";
+export { default as TextColumn } from "./TextColumn";
+export { default as SortIcon } from "./SortIcon";
diff --git a/scm-ui/ui-components/src/table/types.ts b/scm-ui/ui-components/src/table/types.ts
new file mode 100644
index 0000000000..9af3e872cb
--- /dev/null
+++ b/scm-ui/ui-components/src/table/types.ts
@@ -0,0 +1,12 @@
+import { ReactNode } from "react";
+
+export type Comparator = (a: any, b: any) => number;
+
+export type ColumnProps = {
+ header: ReactNode;
+ row?: any;
+ columnIndex?: number;
+ createComparator?: (props: any, columnIndex: number) => Comparator;
+ ascendingIcon?: string;
+ descendingIcon?: string;
+};
diff --git a/scm-ui/ui-scripts/bin/ui-scripts.js b/scm-ui/ui-scripts/bin/ui-scripts.js
index 41549ac27a..28f5ef3315 100755
--- a/scm-ui/ui-scripts/bin/ui-scripts.js
+++ b/scm-ui/ui-scripts/bin/ui-scripts.js
@@ -1,7 +1,7 @@
#!/usr/bin/env node
const { spawnSync } = require("child_process");
-const commands = ["plugin", "plugin-watch", "publish"];
+const commands = ["plugin", "plugin-watch", "publish", "version"];
const args = process.argv.slice(2);
diff --git a/scm-ui/ui-scripts/src/commands/publish.js b/scm-ui/ui-scripts/src/commands/publish.js
index d3cdc60724..2e5ad90440 100644
--- a/scm-ui/ui-scripts/src/commands/publish.js
+++ b/scm-ui/ui-scripts/src/commands/publish.js
@@ -4,7 +4,7 @@ const versions = require("../versions");
const args = process.argv.slice(2);
if (args.length < 1) {
- console.log("usage ui-scripts publish version");
+ console.log("usage ui-scripts publish ");
process.exit(1);
}
diff --git a/scm-ui/ui-scripts/src/commands/version.js b/scm-ui/ui-scripts/src/commands/version.js
new file mode 100644
index 0000000000..c1feb85318
--- /dev/null
+++ b/scm-ui/ui-scripts/src/commands/version.js
@@ -0,0 +1,12 @@
+const lerna = require("../lerna");
+
+const args = process.argv.slice(2);
+
+if (args.length < 1) {
+ console.log("usage ui-scripts version ");
+ process.exit(1);
+}
+
+const version = args[0];
+
+lerna.version(version);
diff --git a/scm-ui/ui-styles/src/scm.scss b/scm-ui/ui-styles/src/scm.scss
index 01d8d3e55a..cc62fa5dd6 100644
--- a/scm-ui/ui-styles/src/scm.scss
+++ b/scm-ui/ui-styles/src/scm.scss
@@ -622,6 +622,10 @@ form .field:not(.is-grouped) {
}
}
+.help {
+ position: absolute;
+}
+
// label with help-icon compensation
.label-icon-spacing {
margin-top: 30px;
diff --git a/scm-ui/ui-webapp/public/locales/de/admin.json b/scm-ui/ui-webapp/public/locales/de/admin.json
index af34c5b759..364fa5a5b7 100644
--- a/scm-ui/ui-webapp/public/locales/de/admin.json
+++ b/scm-ui/ui-webapp/public/locales/de/admin.json
@@ -6,7 +6,7 @@
"settingsNavLink": "Einstellungen",
"generalNavLink": "Generell"
},
- "info": {
+ "info": {
"currentAppVersion": "Aktuelle Software-Versionsnummer",
"communityTitle": "Community Support",
"communityIconAlt": "Community Support Icon",
@@ -91,8 +91,7 @@
"submit": "Speichern"
},
"delete": {
- "button": "Löschen",
- "subtitle": "Berechtigungsrolle löschen",
+ "button": "Berechtigungsrolle löschen",
"confirmAlert": {
"title": "Berechtigungsrolle löschen?",
"message": "Wollen Sie diese Rolle wirklich löschen? Alle Benutzer mit dieser Rolle verlieren die entsprechenden Berechtigungen.",
diff --git a/scm-ui/ui-webapp/public/locales/de/groups.json b/scm-ui/ui-webapp/public/locales/de/groups.json
index 04b368c56b..2e8c7aeb88 100644
--- a/scm-ui/ui-webapp/public/locales/de/groups.json
+++ b/scm-ui/ui-webapp/public/locales/de/groups.json
@@ -60,8 +60,7 @@
}
},
"deleteGroup": {
- "subtitle": "Gruppe löschen",
- "button": "Löschen",
+ "button": "Gruppe löschen",
"confirmAlert": {
"title": "Gruppe löschen",
"message": "Soll die Gruppe wirklich gelöscht werden?",
diff --git a/scm-ui/ui-webapp/public/locales/de/repos.json b/scm-ui/ui-webapp/public/locales/de/repos.json
index 4dfba695ab..df6c534ece 100644
--- a/scm-ui/ui-webapp/public/locales/de/repos.json
+++ b/scm-ui/ui-webapp/public/locales/de/repos.json
@@ -114,7 +114,7 @@
"size": "Größe"
},
"noSources": "Keine Sources in diesem Branch gefunden.",
- "extension" : {
+ "extension": {
"notBound": "Keine Erweiterung angebunden."
}
},
@@ -166,8 +166,7 @@
}
},
"deleteRepo": {
- "subtitle": "Repository löschen",
- "button": "Löschen",
+ "button": "Repository löschen",
"confirmAlert": {
"title": "Repository löschen",
"message": "Soll das Repository wirklich gelöscht werden?",
diff --git a/scm-ui/ui-webapp/public/locales/de/users.json b/scm-ui/ui-webapp/public/locales/de/users.json
index 25b6858c8b..60f9f64423 100644
--- a/scm-ui/ui-webapp/public/locales/de/users.json
+++ b/scm-ui/ui-webapp/public/locales/de/users.json
@@ -45,8 +45,7 @@
"subtitle": "Erstellen eines neuen Benutzers"
},
"deleteUser": {
- "subtitle": "Benutzer löschen",
- "button": "Löschen",
+ "button": "Benutzer löschen",
"confirmAlert": {
"title": "Benutzer löschen",
"message": "Soll der Benutzer wirklich gelöscht werden?",
diff --git a/scm-ui/ui-webapp/public/locales/en/admin.json b/scm-ui/ui-webapp/public/locales/en/admin.json
index c315d1d142..d1360846f7 100644
--- a/scm-ui/ui-webapp/public/locales/en/admin.json
+++ b/scm-ui/ui-webapp/public/locales/en/admin.json
@@ -11,7 +11,7 @@
"communityTitle": "Community Support",
"communityIconAlt": "Community Support Icon",
"communityInfo": "Contact the SCM-Manager support team for questions about SCM-Manager, to report bugs or to request features through the official channels.",
- "communityButton": "Contact our team",
+ "communityButton": "Contact our Team",
"enterpriseTitle": "Enterprise Support",
"enterpriseIconAlt": "Enterprise Support Icon",
"enterpriseInfo": "You require support with the integration of SCM-Manager into your processes, with the customization of the tool or simply a service level agreement (SLA)?",
@@ -76,8 +76,8 @@
"createSubtitle": "Create Permission Role",
"editSubtitle": "Edit Permission Role",
"overview": {
- "title": "Overview of all permission roles",
- "noPermissionRoles": "No permission roles found.",
+ "title": "Overview of all Permission Roles",
+ "noPermissionRoles": "No Permission Roles found.",
"createButton": "Create Permission Role"
},
"editButton": "Edit",
@@ -91,10 +91,9 @@
"submit": "Save"
},
"delete": {
- "button": "Delete",
- "subtitle": "Delete permission role",
+ "button": "Delete Permission Role",
"confirmAlert": {
- "title": "Delete permission role",
+ "title": "Delete Permission Role",
"message": "Do you really want to delete this permission role? All users will lose their corresponding permissions.",
"submit": "Yes",
"cancel": "No"
diff --git a/scm-ui/ui-webapp/public/locales/en/groups.json b/scm-ui/ui-webapp/public/locales/en/groups.json
index 0153f30d30..6911f897f8 100644
--- a/scm-ui/ui-webapp/public/locales/en/groups.json
+++ b/scm-ui/ui-webapp/public/locales/en/groups.json
@@ -60,8 +60,7 @@
}
},
"deleteGroup": {
- "subtitle": "Delete Group",
- "button": "Delete",
+ "button": "Delete Group",
"confirmAlert": {
"title": "Delete Group",
"message": "Do you really want to delete the group?",
diff --git a/scm-ui/ui-webapp/public/locales/en/repos.json b/scm-ui/ui-webapp/public/locales/en/repos.json
index e2ccf8326e..103e30b825 100644
--- a/scm-ui/ui-webapp/public/locales/en/repos.json
+++ b/scm-ui/ui-webapp/public/locales/en/repos.json
@@ -114,7 +114,7 @@
"size": "Size"
},
"noSources": "No sources found for this branch.",
- "extension" : {
+ "extension": {
"notBound": "No extension bound."
}
},
@@ -166,8 +166,7 @@
}
},
"deleteRepo": {
- "subtitle": "Delete Repository",
- "button": "Delete",
+ "button": "Delete Repository",
"confirmAlert": {
"title": "Delete repository",
"message": "Do you really want to delete the repository?",
@@ -178,7 +177,7 @@
"diff": {
"changes": {
"add": "added",
- "delete": "deleted",
+ "delete": "deleted",
"modify": "modified",
"rename": "renamed",
"copy": "copied"
diff --git a/scm-ui/ui-webapp/public/locales/en/users.json b/scm-ui/ui-webapp/public/locales/en/users.json
index b492923a3f..0fab330e6f 100644
--- a/scm-ui/ui-webapp/public/locales/en/users.json
+++ b/scm-ui/ui-webapp/public/locales/en/users.json
@@ -45,17 +45,16 @@
"subtitle": "Create a new user"
},
"deleteUser": {
- "subtitle": "Delete User",
- "button": "Delete",
+ "button": "Delete User",
"confirmAlert": {
- "title": "Delete user",
+ "title": "Delete User",
"message": "Do you really want to delete the user?",
"submit": "Yes",
"cancel": "No"
}
},
"singleUserPassword": {
- "button": "Set password",
+ "button": "Set Password",
"setPasswordSuccessful": "Password successfully set"
},
"userForm": {
diff --git a/scm-ui/ui-webapp/public/locales/es/admin.json b/scm-ui/ui-webapp/public/locales/es/admin.json
index 155313ed99..4c78d32620 100644
--- a/scm-ui/ui-webapp/public/locales/es/admin.json
+++ b/scm-ui/ui-webapp/public/locales/es/admin.json
@@ -86,8 +86,7 @@
"submit": "Guardar"
},
"delete": {
- "button": "Borrar",
- "subtitle": "Eliminar el rol",
+ "button": "Eliminar el rol",
"confirmAlert": {
"title": "Eliminar el rol",
"message": "¿Realmente desea borrar el rol? Todos los usuarios de este rol perderń sus permisos.",
diff --git a/scm-ui/ui-webapp/public/locales/es/groups.json b/scm-ui/ui-webapp/public/locales/es/groups.json
index 0ce3e7765a..f62cfc62cb 100644
--- a/scm-ui/ui-webapp/public/locales/es/groups.json
+++ b/scm-ui/ui-webapp/public/locales/es/groups.json
@@ -60,8 +60,7 @@
}
},
"deleteGroup": {
- "subtitle": "Borrar grupo",
- "button": "Borrar",
+ "button": "Borrar grupo",
"confirmAlert": {
"title": "Borrar grupo",
"message": "¿Realmente desea borrar el grupo?",
diff --git a/scm-ui/ui-webapp/public/locales/es/repos.json b/scm-ui/ui-webapp/public/locales/es/repos.json
index c3bf57c630..3b2451cceb 100644
--- a/scm-ui/ui-webapp/public/locales/es/repos.json
+++ b/scm-ui/ui-webapp/public/locales/es/repos.json
@@ -114,7 +114,7 @@
"size": "tamaño"
},
"noSources": "No se han encontrado fuentes para esta rama.",
- "extension" : {
+ "extension": {
"notBound": "Sin extensión conectada."
}
},
@@ -166,8 +166,7 @@
}
},
"deleteRepo": {
- "subtitle": "Borrar repositorio",
- "button": "Borrar",
+ "button": "Borrar repositorio",
"confirmAlert": {
"title": "Borrar repositorio",
"message": "¿Realmente desea borrar el repositorio?",
@@ -178,7 +177,7 @@
"diff": {
"changes": {
"add": "añadido",
- "delete": "borrado",
+ "delete": "borrado",
"modify": "modificado",
"rename": "renombrado",
"copy": "copiado"
diff --git a/scm-ui/ui-webapp/public/locales/es/users.json b/scm-ui/ui-webapp/public/locales/es/users.json
index d5ea97b95a..7c8fa4d35e 100644
--- a/scm-ui/ui-webapp/public/locales/es/users.json
+++ b/scm-ui/ui-webapp/public/locales/es/users.json
@@ -45,8 +45,7 @@
"subtitle": "Crear un nuevo usuario"
},
"deleteUser": {
- "subtitle": "Borrar usuario",
- "button": "Borrar",
+ "button": "Borrar usuario",
"confirmAlert": {
"title": "Borrar usuario",
"message": "¿Realmente desea borrar el usuario?",
diff --git a/scm-ui/ui-webapp/src/admin/components/form/ConfigForm.tsx b/scm-ui/ui-webapp/src/admin/components/form/ConfigForm.tsx
index f7f8f67659..6c87f4359b 100644
--- a/scm-ui/ui-webapp/src/admin/components/form/ConfigForm.tsx
+++ b/scm-ui/ui-webapp/src/admin/components/form/ConfigForm.tsx
@@ -1,7 +1,7 @@
import React from "react";
import { WithTranslation, withTranslation } from "react-i18next";
import { NamespaceStrategies, Config } from "@scm-manager/ui-types";
-import { SubmitButton, Notification } from "@scm-manager/ui-components";
+import { Level, SubmitButton, Notification } from "@scm-manager/ui-components";
import ProxySettings from "./ProxySettings";
import GeneralSettings from "./GeneralSettings";
import BaseUrlSettings from "./BaseUrlSettings";
@@ -151,10 +151,14 @@ class ConfigForm extends React.Component {
hasUpdatePermission={configUpdatePermission}
/>
-
+ }
/>
);
diff --git a/scm-ui/ui-webapp/src/admin/roles/components/AvailableVerbs.tsx b/scm-ui/ui-webapp/src/admin/roles/components/AvailableVerbs.tsx
index b230444de5..ec0e1fda29 100644
--- a/scm-ui/ui-webapp/src/admin/roles/components/AvailableVerbs.tsx
+++ b/scm-ui/ui-webapp/src/admin/roles/components/AvailableVerbs.tsx
@@ -13,15 +13,13 @@ class AvailableVerbs extends React.Component {
let verbs = null;
if (role.verbs.length > 0) {
verbs = (
-
-
-
- {role.verbs.map(verb => {
- return - {t("verbs.repository." + verb + ".displayName")}
;
- })}
-
- |
-
+
+
+ {role.verbs.map((verb, key) => {
+ return - {t("verbs.repository." + verb + ".displayName")}
;
+ })}
+
+ |
);
}
return verbs;
diff --git a/scm-ui/ui-webapp/src/admin/roles/components/PermissionRoleDetails.tsx b/scm-ui/ui-webapp/src/admin/roles/components/PermissionRoleDetails.tsx
index e4a2a0f05c..f6e0bf35d6 100644
--- a/scm-ui/ui-webapp/src/admin/roles/components/PermissionRoleDetails.tsx
+++ b/scm-ui/ui-webapp/src/admin/roles/components/PermissionRoleDetails.tsx
@@ -2,7 +2,7 @@ import React from "react";
import { WithTranslation, withTranslation } from "react-i18next";
import { ExtensionPoint } from "@scm-manager/ui-extensions";
import { RepositoryRole } from "@scm-manager/ui-types";
-import { Button } from "@scm-manager/ui-components";
+import { Level, Button } from "@scm-manager/ui-components";
import PermissionRoleDetailsTable from "./PermissionRoleDetailsTable";
type Props = WithTranslation & {
@@ -14,7 +14,12 @@ class PermissionRoleDetails extends React.Component {
renderEditButton() {
const { t, url } = this.props;
if (!!this.props.role._links.update) {
- return ;
+ return (
+ <>
+
+ } />
+ >
+ );
}
return null;
}
@@ -25,7 +30,6 @@ class PermissionRoleDetails extends React.Component {
return (
<>
-
{this.renderEditButton()}
{
return (
<>
-
-
+
+
+ } />
>
);
}
diff --git a/scm-ui/ui-webapp/src/admin/roles/containers/EditRepositoryRole.tsx b/scm-ui/ui-webapp/src/admin/roles/containers/EditRepositoryRole.tsx
index f09d5c603e..7114a8efc7 100644
--- a/scm-ui/ui-webapp/src/admin/roles/containers/EditRepositoryRole.tsx
+++ b/scm-ui/ui-webapp/src/admin/roles/containers/EditRepositoryRole.tsx
@@ -44,7 +44,6 @@ class EditRepositoryRole extends React.Component {
<>
this.updateRepositoryRole(role)} />
-
>
);
diff --git a/scm-ui/ui-webapp/src/admin/roles/containers/RepositoryRoleForm.tsx b/scm-ui/ui-webapp/src/admin/roles/containers/RepositoryRoleForm.tsx
index f5a9c19549..3dd47b0feb 100644
--- a/scm-ui/ui-webapp/src/admin/roles/containers/RepositoryRoleForm.tsx
+++ b/scm-ui/ui-webapp/src/admin/roles/containers/RepositoryRoleForm.tsx
@@ -2,10 +2,10 @@ import React from "react";
import { connect } from "react-redux";
import { WithTranslation, withTranslation } from "react-i18next";
import { RepositoryRole } from "@scm-manager/ui-types";
-import { InputField, SubmitButton } from "@scm-manager/ui-components";
+import { InputField, Level, SubmitButton } from "@scm-manager/ui-components";
import { getRepositoryRolesLink, getRepositoryVerbsLink } from "../../../modules/indexResource";
import { fetchAvailableVerbs, getFetchVerbsFailure, getVerbsFromState, isFetchVerbsPending } from "../modules/roles";
-import PermissionCheckbox from "../../../repos/permissions/components/PermissionCheckbox";
+import PermissionsWrapper from "../../../permissions/components/PermissionsWrapper";
type Props = WithTranslation & {
role?: RepositoryRole;
@@ -89,16 +89,9 @@ class RepositoryRoleForm extends React.Component {
const { loading, availableVerbs, t } = this.props;
const { role } = this.state;
- const verbSelectBoxes = !availableVerbs
- ? null
- : availableVerbs.map(verb => (
-
- ));
+ const selectedVerbs = availableVerbs
+ ? availableVerbs.reduce((obj, verb) => ({ ...obj, [verb]: role.verbs.includes(verb) }), {})
+ : {};
return (
);
}
}
-const mapStateToProps = (state) => {
+const mapStateToProps = state => {
const loading = isFetchVerbsPending(state);
const error = getFetchVerbsFailure(state);
const verbsLink = getRepositoryVerbsLink(state);
diff --git a/scm-ui/ui-webapp/src/containers/ChangeUserPassword.tsx b/scm-ui/ui-webapp/src/containers/ChangeUserPassword.tsx
index 9b8275aa0e..46682d6183 100644
--- a/scm-ui/ui-webapp/src/containers/ChangeUserPassword.tsx
+++ b/scm-ui/ui-webapp/src/containers/ChangeUserPassword.tsx
@@ -4,6 +4,7 @@ import {
InputField,
Notification,
PasswordConfirmation,
+ Level,
SubmitButton
} from "@scm-manager/ui-components";
import { WithTranslation, withTranslation } from "react-i18next";
@@ -124,11 +125,7 @@ class ChangeUserPassword extends React.Component {
passwordChanged={this.passwordChanged}
key={this.state.passwordChanged ? "changed" : "unchanged"}
/>
-
+ } />
);
}
diff --git a/scm-ui/ui-webapp/src/groups/components/GroupForm.tsx b/scm-ui/ui-webapp/src/groups/components/GroupForm.tsx
index b0c41df5c8..47c4414190 100644
--- a/scm-ui/ui-webapp/src/groups/components/GroupForm.tsx
+++ b/scm-ui/ui-webapp/src/groups/components/GroupForm.tsx
@@ -8,9 +8,9 @@ import {
InputField,
SubmitButton,
Textarea,
+ Level,
Checkbox
} from "@scm-manager/ui-components";
-
import * as validator from "./groupValidation";
type Props = WithTranslation & {
@@ -154,7 +154,7 @@ class GroupForm extends React.Component {
/>
{this.renderExternalField(group)}
{this.renderMemberfields(group)}
-
+ } />
>
);
diff --git a/scm-ui/ui-webapp/src/groups/components/table/GroupRow.tsx b/scm-ui/ui-webapp/src/groups/components/table/GroupRow.tsx
index 2674811ae1..dbc54fbf35 100644
--- a/scm-ui/ui-webapp/src/groups/components/table/GroupRow.tsx
+++ b/scm-ui/ui-webapp/src/groups/components/table/GroupRow.tsx
@@ -17,9 +17,9 @@ class GroupRow extends React.Component {
const { group, t } = this.props;
const to = `/group/${group.name}`;
const iconType = group.external ? (
-
+
) : (
-
+
);
return (
diff --git a/scm-ui/ui-webapp/src/groups/containers/DeleteGroup.tsx b/scm-ui/ui-webapp/src/groups/containers/DeleteGroup.tsx
index 8c2034f09e..d9e8e4e6b5 100644
--- a/scm-ui/ui-webapp/src/groups/containers/DeleteGroup.tsx
+++ b/scm-ui/ui-webapp/src/groups/containers/DeleteGroup.tsx
@@ -4,7 +4,7 @@ import { withRouter } from "react-router-dom";
import { WithTranslation, withTranslation } from "react-i18next";
import { History } from "history";
import { Group } from "@scm-manager/ui-types";
-import { Subtitle, DeleteButton, confirmAlert, ErrorNotification } from "@scm-manager/ui-components";
+import { Level, DeleteButton, confirmAlert, ErrorNotification } from "@scm-manager/ui-components";
import { deleteGroup, getDeleteGroupFailure, isDeleteGroupPending } from "../modules/groups";
type Props = WithTranslation & {
@@ -64,13 +64,9 @@ export class DeleteGroup extends React.Component {
return (
<>
-
+
-
+ } />
>
);
}
diff --git a/scm-ui/ui-webapp/src/groups/containers/EditGroup.tsx b/scm-ui/ui-webapp/src/groups/containers/EditGroup.tsx
index e3307274bd..f34faaac26 100644
--- a/scm-ui/ui-webapp/src/groups/containers/EditGroup.tsx
+++ b/scm-ui/ui-webapp/src/groups/containers/EditGroup.tsx
@@ -63,7 +63,6 @@ class EditGroup extends React.Component {
loading={loading}
loadUserSuggestions={this.loadUserAutocompletion}
/>
-
);
diff --git a/scm-ui/ui-webapp/src/permissions/components/PermissionCheckbox.tsx b/scm-ui/ui-webapp/src/permissions/components/PermissionCheckbox.tsx
index 505d65d7d1..3223336099 100644
--- a/scm-ui/ui-webapp/src/permissions/components/PermissionCheckbox.tsx
+++ b/scm-ui/ui-webapp/src/permissions/components/PermissionCheckbox.tsx
@@ -3,24 +3,34 @@ import { WithTranslation, withTranslation } from "react-i18next";
import { Checkbox } from "@scm-manager/ui-components";
type Props = WithTranslation & {
- permission: string;
+ name: string;
checked: boolean;
- onChange: (value: boolean, name: string) => void;
+ onChange?: (value: boolean, name?: string) => void;
disabled: boolean;
+ role?: boolean;
};
class PermissionCheckbox extends React.Component
{
render() {
- const { t, permission, checked, onChange, disabled } = this.props;
- const key = permission.split(":").join(".");
+ const { name, checked, onChange, disabled, role, t } = this.props;
+
+ const key = name.split(":").join(".");
+ const label = role
+ ? t("verbs.repository." + name + ".displayName")
+ : this.translateOrDefault("permissions." + key + ".displayName", key);
+ const helpText = role
+ ? t("verbs.repository." + name + ".description")
+ : this.translateOrDefault("permissions." + key + ".description", t("permissions.unknown"));
+
return (
);
}
diff --git a/scm-ui/ui-webapp/src/permissions/components/PermissionsWrapper.tsx b/scm-ui/ui-webapp/src/permissions/components/PermissionsWrapper.tsx
new file mode 100644
index 0000000000..c5d0a90ae0
--- /dev/null
+++ b/scm-ui/ui-webapp/src/permissions/components/PermissionsWrapper.tsx
@@ -0,0 +1,63 @@
+import React from "react";
+import classNames from "classnames";
+import styled from "styled-components";
+import PermissionCheckbox from "./PermissionCheckbox";
+import { Loading } from "@scm-manager/ui-components";
+
+type Props = {
+ permissions: {
+ [key: string]: boolean;
+ };
+ onChange: (value: boolean, name: string) => void;
+ disabled: boolean;
+ role?: boolean;
+};
+
+const StyledWrapper = styled.div`
+ padding-bottom: 0;
+
+ & .field .control {
+ width: 100%;
+ word-wrap: break-word;
+ }
+`;
+
+export default class PermissionsWrapper extends React.Component {
+ render() {
+ const { permissions, onChange, disabled, role } = this.props;
+
+ if (!permissions) {
+ return ;
+ }
+
+ const permissionArray = Object.keys(permissions);
+ return (
+
+
+ {permissionArray.slice(0, permissionArray.length / 2 + 1).map(p => (
+
+ ))}
+
+
+ {permissionArray.slice(permissionArray.length / 2 + 1, permissionArray.length).map(p => (
+
+ ))}
+
+
+ );
+ }
+}
diff --git a/scm-ui/ui-webapp/src/permissions/components/SetPermissions.tsx b/scm-ui/ui-webapp/src/permissions/components/SetPermissions.tsx
index e9db6cac17..83c3b2e284 100644
--- a/scm-ui/ui-webapp/src/permissions/components/SetPermissions.tsx
+++ b/scm-ui/ui-webapp/src/permissions/components/SetPermissions.tsx
@@ -1,13 +1,11 @@
import React from "react";
import { connect } from "react-redux";
import { WithTranslation, withTranslation } from "react-i18next";
-import classNames from "classnames";
-import styled from "styled-components";
import { Link } from "@scm-manager/ui-types";
-import { Notification, ErrorNotification, SubmitButton } from "@scm-manager/ui-components";
+import { Notification, ErrorNotification, SubmitButton, Level } from "@scm-manager/ui-components";
import { getLink } from "../../modules/indexResource";
import { loadPermissionsForEntity, setPermissions } from "./handlePermissions";
-import PermissionCheckbox from "./PermissionCheckbox";
+import PermissionsWrapper from "./PermissionsWrapper";
type Props = WithTranslation & {
availablePermissionLink: string;
@@ -25,15 +23,6 @@ type State = {
overwritePermissionsLink?: Link;
};
-const PermissionsWrapper = styled.div`
- padding-bottom: 0;
-
- & .field .control {
- width: 100%;
- word-wrap: break-word;
- }
-`;
-
class SetPermissions extends React.Component {
constructor(props: Props) {
super(props);
@@ -43,7 +32,6 @@ class SetPermissions extends React.Component {
loading: true,
permissionsChanged: false,
permissionsSubmitted: false,
- modifiable: false,
overwritePermissionsLink: undefined
};
}
@@ -125,39 +113,23 @@ class SetPermissions extends React.Component {
);
}
renderPermissions = () => {
const { overwritePermissionsLink, permissions } = this.state;
- const permissionArray = Object.keys(permissions);
return (
-
-
- {permissionArray.slice(0, permissionArray.length / 2 + 1).map(p => (
-
- ))}
-
-
- {permissionArray.slice(permissionArray.length / 2 + 1, permissionArray.length).map(p => (
-
- ))}
-
-
+
);
};
diff --git a/scm-ui/ui-webapp/src/repos/components/form/RepositoryForm.tsx b/scm-ui/ui-webapp/src/repos/components/form/RepositoryForm.tsx
index 681b2c91f2..37fd072880 100644
--- a/scm-ui/ui-webapp/src/repos/components/form/RepositoryForm.tsx
+++ b/scm-ui/ui-webapp/src/repos/components/form/RepositoryForm.tsx
@@ -2,7 +2,7 @@ import React from "react";
import { WithTranslation, withTranslation } from "react-i18next";
import { ExtensionPoint } from "@scm-manager/ui-extensions";
import { Repository, RepositoryType } from "@scm-manager/ui-types";
-import { Subtitle, InputField, Select, SubmitButton, Textarea } from "@scm-manager/ui-components";
+import { Subtitle, InputField, Select, Textarea, Level, SubmitButton } from "@scm-manager/ui-components";
import * as validator from "./repositoryValidation";
type Props = WithTranslation & {
@@ -52,9 +52,8 @@ class RepositoryForm extends React.Component {
}
}
- isFalsy(value) {
+ isFalsy(value: string) {
return !value;
-
}
isValid = () => {
@@ -91,7 +90,7 @@ class RepositoryForm extends React.Component {
const disabled = !this.isModifiable() && !this.isCreateMode();
const submitButton = disabled ? null : (
-
+ } />
);
let subtitle = null;
diff --git a/scm-ui/ui-webapp/src/repos/containers/DeleteRepo.tsx b/scm-ui/ui-webapp/src/repos/containers/DeleteRepo.tsx
index 83220b97b8..a8b8c97f84 100644
--- a/scm-ui/ui-webapp/src/repos/containers/DeleteRepo.tsx
+++ b/scm-ui/ui-webapp/src/repos/containers/DeleteRepo.tsx
@@ -4,7 +4,7 @@ import { withRouter } from "react-router-dom";
import { WithTranslation, withTranslation } from "react-i18next";
import { History } from "history";
import { Repository } from "@scm-manager/ui-types";
-import { Subtitle, DeleteButton, confirmAlert, ErrorNotification } from "@scm-manager/ui-components";
+import { Level, DeleteButton, confirmAlert, ErrorNotification } from "@scm-manager/ui-components";
import { deleteRepo, getDeleteRepoFailure, isDeleteRepoPending } from "../modules/repos";
type Props = WithTranslation & {
@@ -65,13 +65,8 @@ class DeleteRepo extends React.Component {
return (
<>
-
-
+ } />
>
);
}
diff --git a/scm-ui/ui-webapp/src/repos/permissions/components/PermissionCheckbox.tsx b/scm-ui/ui-webapp/src/repos/permissions/components/PermissionCheckbox.tsx
deleted file mode 100644
index 2f677d4deb..0000000000
--- a/scm-ui/ui-webapp/src/repos/permissions/components/PermissionCheckbox.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import React from "react";
-import { WithTranslation, withTranslation } from "react-i18next";
-import { Checkbox } from "@scm-manager/ui-components";
-
-type Props = WithTranslation & {
- disabled: boolean;
- name: string;
- checked: boolean;
- onChange?: (value: boolean, name?: string) => void;
-};
-
-class PermissionCheckbox extends React.Component {
- render() {
- const { t } = this.props;
- return (
-
- );
- }
-}
-
-export default withTranslation("plugins")(PermissionCheckbox);
diff --git a/scm-ui/ui-webapp/src/repos/permissions/containers/AdvancedPermissionsDialog.tsx b/scm-ui/ui-webapp/src/repos/permissions/containers/AdvancedPermissionsDialog.tsx
index aa4d7aaa07..5ab3461638 100644
--- a/scm-ui/ui-webapp/src/repos/permissions/containers/AdvancedPermissionsDialog.tsx
+++ b/scm-ui/ui-webapp/src/repos/permissions/containers/AdvancedPermissionsDialog.tsx
@@ -1,7 +1,7 @@
import React from "react";
import { WithTranslation, withTranslation } from "react-i18next";
import { ButtonGroup, Button, SubmitButton, Modal } from "@scm-manager/ui-components";
-import PermissionCheckbox from "../components/PermissionCheckbox";
+import PermissionCheckbox from "../../../permissions/components/PermissionCheckbox";
type Props = WithTranslation & {
readOnly: boolean;
@@ -33,7 +33,14 @@ class AdvancedPermissionsDialog extends React.Component {
const { verbs } = this.state;
const verbSelectBoxes = Object.entries(verbs).map(e => (
-
+
));
const submitButton = !readOnly ? : null;
diff --git a/scm-ui/ui-webapp/src/repos/permissions/containers/CreatePermissionForm.tsx b/scm-ui/ui-webapp/src/repos/permissions/containers/CreatePermissionForm.tsx
index ed9c79b65f..447ef5ec68 100644
--- a/scm-ui/ui-webapp/src/repos/permissions/containers/CreatePermissionForm.tsx
+++ b/scm-ui/ui-webapp/src/repos/permissions/containers/CreatePermissionForm.tsx
@@ -6,6 +6,7 @@ import {
GroupAutocomplete,
LabelWithHelpIcon,
Radio,
+ Level,
SubmitButton,
Subtitle,
UserAutocomplete
@@ -141,8 +142,8 @@ class CreatePermissionForm extends React.Component {
-
{this.renderAutocompletionField()}
-
+
{this.renderAutocompletionField()}
+
-
+ }
+ />
>
);
diff --git a/scm-ui/ui-webapp/src/repos/sources/components/content/FileButtonAddons.tsx b/scm-ui/ui-webapp/src/repos/sources/components/content/FileButtonAddons.tsx
index dd52604611..dd0183d51d 100644
--- a/scm-ui/ui-webapp/src/repos/sources/components/content/FileButtonAddons.tsx
+++ b/scm-ui/ui-webapp/src/repos/sources/components/content/FileButtonAddons.tsx
@@ -18,7 +18,7 @@ class FileButtonAddons extends React.Component
{
};
color = (selected: boolean) => {
- return selected ? "link is-selected" : null;
+ return selected ? "link is-selected" : "";
};
render() {
@@ -27,14 +27,14 @@ class FileButtonAddons extends React.Component {
return (
-
-
+
diff --git a/scm-ui/ui-webapp/src/repos/sources/containers/SourceExtensions.tsx b/scm-ui/ui-webapp/src/repos/sources/containers/SourceExtensions.tsx
index c75b0106a9..2ea57a65ba 100644
--- a/scm-ui/ui-webapp/src/repos/sources/containers/SourceExtensions.tsx
+++ b/scm-ui/ui-webapp/src/repos/sources/containers/SourceExtensions.tsx
@@ -7,24 +7,25 @@ import { fetchSources, getFetchSourcesFailure, getSources, isFetchSourcesPending
import { connect } from "react-redux";
import { Loading, ErrorNotification } from "@scm-manager/ui-components";
import Notification from "@scm-manager/ui-components/src/Notification";
-import {WithTranslation, withTranslation} from "react-i18next";
+import { WithTranslation, withTranslation } from "react-i18next";
-type Props = WithTranslation & RouteComponentProps & {
- repository: Repository;
+type Props = WithTranslation &
+ RouteComponentProps & {
+ repository: Repository;
- // url params
- extension: string;
- revision?: string;
- path?: string;
+ // url params
+ extension: string;
+ revision?: string;
+ path?: string;
- // redux state
- loading: boolean;
- error?: Error | null;
- sources?: File | null;
+ // redux state
+ loading: boolean;
+ error?: Error | null;
+ sources?: File | null;
- // dispatch props
- fetchSources: (repository: Repository, revision: string, path: string) => void;
-};
+ // dispatch props
+ fetchSources: (repository: Repository, revision: string, path: string) => void;
+ };
const extensionPointName = "repos.sources.extensions";
@@ -32,7 +33,7 @@ class SourceExtensions extends React.Component {
componentDidMount() {
const { fetchSources, repository, revision, path } = this.props;
// TODO get typing right
- fetchSources(repository,revision || "", path || "");
+ fetchSources(repository, revision || "", path || "");
}
render() {
diff --git a/scm-ui/ui-webapp/src/users/components/SetUserPassword.tsx b/scm-ui/ui-webapp/src/users/components/SetUserPassword.tsx
index f060c7494d..e5e3861fac 100644
--- a/scm-ui/ui-webapp/src/users/components/SetUserPassword.tsx
+++ b/scm-ui/ui-webapp/src/users/components/SetUserPassword.tsx
@@ -1,7 +1,7 @@
import React from "react";
import { WithTranslation, withTranslation } from "react-i18next";
import { User } from "@scm-manager/ui-types";
-import { SubmitButton, Notification, ErrorNotification, PasswordConfirmation } from "@scm-manager/ui-components";
+import { Level, SubmitButton, Notification, ErrorNotification, PasswordConfirmation } from "@scm-manager/ui-components";
import { setPassword } from "./setPassword";
type Props = WithTranslation & {
@@ -98,15 +98,15 @@ class SetUserPassword extends React.Component {
passwordChanged={this.passwordChanged}
key={this.state.passwordChanged ? "changed" : "unchanged"}
/>
-
+ }
+ />
);
}
diff --git a/scm-ui/ui-webapp/src/users/components/UserForm.tsx b/scm-ui/ui-webapp/src/users/components/UserForm.tsx
index db1fc50ea1..1efd2558cd 100644
--- a/scm-ui/ui-webapp/src/users/components/UserForm.tsx
+++ b/scm-ui/ui-webapp/src/users/components/UserForm.tsx
@@ -6,6 +6,7 @@ import {
Checkbox,
InputField,
PasswordConfirmation,
+ Level,
SubmitButton,
validation as validator
} from "@scm-manager/ui-components";
@@ -166,11 +167,7 @@ class UserForm extends React.Component {
/>
-
+
} />
>
);
diff --git a/scm-ui/ui-webapp/src/users/containers/DeleteUser.tsx b/scm-ui/ui-webapp/src/users/containers/DeleteUser.tsx
index 15beba3c49..6feafaaf91 100644
--- a/scm-ui/ui-webapp/src/users/containers/DeleteUser.tsx
+++ b/scm-ui/ui-webapp/src/users/containers/DeleteUser.tsx
@@ -4,7 +4,7 @@ import { withRouter } from "react-router-dom";
import { WithTranslation, withTranslation } from "react-i18next";
import { History } from "history";
import { User } from "@scm-manager/ui-types";
-import { Subtitle, DeleteButton, confirmAlert, ErrorNotification } from "@scm-manager/ui-components";
+import { Level, DeleteButton, confirmAlert, ErrorNotification } from "@scm-manager/ui-components";
import { deleteUser, getDeleteUserFailure, isDeleteUserPending } from "../modules/users";
type Props = WithTranslation & {
@@ -64,13 +64,9 @@ class DeleteUser extends React.Component
{
return (
<>
-
+
-
+ } />
>
);
}
diff --git a/scm-ui/ui-webapp/src/users/containers/EditUser.tsx b/scm-ui/ui-webapp/src/users/containers/EditUser.tsx
index e2594caa44..81f639535c 100644
--- a/scm-ui/ui-webapp/src/users/containers/EditUser.tsx
+++ b/scm-ui/ui-webapp/src/users/containers/EditUser.tsx
@@ -41,7 +41,6 @@ class EditUser extends React.Component {
this.modifyUser(user)} user={user} loading={loading} />
-
);
diff --git a/scm-webapp/src/main/java/sonia/scm/lifecycle/BootstrapContextListener.java b/scm-webapp/src/main/java/sonia/scm/lifecycle/BootstrapContextListener.java
index 5b9f40dbc2..e3fd5528eb 100644
--- a/scm-webapp/src/main/java/sonia/scm/lifecycle/BootstrapContextListener.java
+++ b/scm-webapp/src/main/java/sonia/scm/lifecycle/BootstrapContextListener.java
@@ -88,14 +88,16 @@ public class BootstrapContextListener extends GuiceServletContextListener {
protected Injector getInjector() {
Throwable startupError = SCMContext.getContext().getStartupError();
if (startupError != null) {
+ LOG.error("received unrecoverable error during startup", startupError);
return createStageOneInjector(SingleView.error(startupError));
} else if (Versions.isTooOld()) {
- LOG.error("Existing version is too old and cannot be migrated to new version. Please update to version {} first", Versions.MIN_VERSION);
+ LOG.error("existing version is too old and cannot be migrated to new version. Please update to version {} first", Versions.MIN_VERSION);
return createStageOneInjector(SingleView.view("/templates/too-old.mustache", HttpServletResponse.SC_CONFLICT));
} else {
try {
return createStageTwoInjector();
} catch (Exception ex) {
+ LOG.error("failed to create stage two injector", ex);
return createStageOneInjector(SingleView.error(ex));
}
}
diff --git a/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpClient.java b/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpClient.java
index df6c93515b..161abf54c6 100644
--- a/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpClient.java
+++ b/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpClient.java
@@ -38,39 +38,27 @@ import com.google.common.base.Strings;
import com.google.common.collect.Multimap;
import com.google.common.io.Closeables;
import com.google.inject.Inject;
-
import org.apache.shiro.codec.Base64;
-
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-
import sonia.scm.config.ScmConfiguration;
import sonia.scm.net.Proxies;
import sonia.scm.net.TrustAllHostnameVerifier;
import sonia.scm.net.TrustAllTrustManager;
import sonia.scm.util.HttpUtil;
-//~--- JDK imports ------------------------------------------------------------
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-import java.net.HttpURLConnection;
-import java.net.InetSocketAddress;
-import java.net.ProtocolException;
-import java.net.Proxy;
-import java.net.SocketAddress;
-import java.net.URL;
-
-import java.security.KeyManagementException;
-import java.security.NoSuchAlgorithmException;
-
-import java.util.Set;
import javax.inject.Provider;
-
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.*;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Set;
+
+//~--- JDK imports ------------------------------------------------------------
/**
* Default implementation of the {@link AdvancedHttpClient}. The default
@@ -324,11 +312,7 @@ public class DefaultAdvancedHttpClient extends AdvancedHttpClient
sc.init(null, trustAllCerts, new java.security.SecureRandom());
connection.setSSLSocketFactory(sc.getSocketFactory());
}
- catch (KeyManagementException ex)
- {
- logger.error("could not disable certificate validation", ex);
- }
- catch (NoSuchAlgorithmException ex)
+ catch (KeyManagementException | NoSuchAlgorithmException ex)
{
logger.error("could not disable certificate validation", ex);
}
diff --git a/scm-webapp/src/main/java/sonia/scm/net/ahc/XmlContentTransformer.java b/scm-webapp/src/main/java/sonia/scm/net/ahc/XmlContentTransformer.java
index 84dcda3593..71fbb489b1 100644
--- a/scm-webapp/src/main/java/sonia/scm/net/ahc/XmlContentTransformer.java
+++ b/scm-webapp/src/main/java/sonia/scm/net/ahc/XmlContentTransformer.java
@@ -34,20 +34,17 @@ package sonia.scm.net.ahc;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.io.ByteSource;
-
import sonia.scm.plugin.Extension;
import sonia.scm.util.IOUtil;
-//~--- JDK imports ------------------------------------------------------------
-
+import javax.ws.rs.core.MediaType;
+import javax.xml.bind.DataBindingException;
+import javax.xml.bind.JAXB;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import javax.ws.rs.core.MediaType;
-
-import javax.xml.bind.DataBindingException;
-import javax.xml.bind.JAXB;
+//~--- JDK imports ------------------------------------------------------------
/**
* {@link ContentTransformer} for xml. The {@link XmlContentTransformer} uses
@@ -96,15 +93,10 @@ public class XmlContentTransformer implements ContentTransformer
stream = content.openBufferedStream();
object = JAXB.unmarshal(stream, type);
}
- catch (IOException ex)
+ catch (IOException | DataBindingException ex)
{
throw new ContentTransformerException("could not unmarshall content", ex);
- }
- catch (DataBindingException ex)
- {
- throw new ContentTransformerException("could not unmarshall content", ex);
- }
- finally
+ } finally
{
IOUtil.close(stream);
}
diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java
index b74b17cd88..f57d932e1e 100644
--- a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java
+++ b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java
@@ -248,6 +248,7 @@ public class DefaultPluginManager implements PluginManager {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
}
eventBus.post(new RestartEvent(PluginManager.class, cause));
}).start();
diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/MultiParentClassLoader.java b/scm-webapp/src/main/java/sonia/scm/plugin/MultiParentClassLoader.java
index 5881652ec4..ebd8b6e24e 100644
--- a/scm-webapp/src/main/java/sonia/scm/plugin/MultiParentClassLoader.java
+++ b/scm-webapp/src/main/java/sonia/scm/plugin/MultiParentClassLoader.java
@@ -74,7 +74,7 @@ public class MultiParentClassLoader extends ClassLoader
public MultiParentClassLoader(Collection extends ClassLoader> parents)
{
super(null);
- this.parents = new CopyOnWriteArrayList(parents);
+ this.parents = new CopyOnWriteArrayList<>(parents);
}
//~--- get methods ----------------------------------------------------------
diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDto.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDto.java
index 1a18d696d5..d7b11b5ade 100644
--- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDto.java
+++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDto.java
@@ -53,7 +53,7 @@ public final class PluginCenterDto implements Serializable {
private String category;
private String author;
private String avatarUrl;
- private String sha256;
+ private String sha256sum;
@XmlElement(name = "conditions")
private Condition conditions;
diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDtoMapper.java
index 1b84bca147..56a27a73af 100644
--- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDtoMapper.java
+++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDtoMapper.java
@@ -19,7 +19,7 @@ public abstract class PluginCenterDtoMapper {
for (PluginCenterDto.Plugin plugin : pluginCenterDto.getEmbedded().getPlugins()) {
String url = plugin.getLinks().get("download").getHref();
AvailablePluginDescriptor descriptor = new AvailablePluginDescriptor(
- map(plugin), map(plugin.getConditions()), plugin.getDependencies(), url, plugin.getSha256()
+ map(plugin), map(plugin.getConditions()), plugin.getDependencies(), url, plugin.getSha256sum()
);
plugins.add(new AvailablePlugin(descriptor));
}
diff --git a/scm-webapp/src/main/java/sonia/scm/repository/HealthCheckContextListener.java b/scm-webapp/src/main/java/sonia/scm/repository/HealthCheckContextListener.java
index a0918c3e64..146382771e 100644
--- a/scm-webapp/src/main/java/sonia/scm/repository/HealthCheckContextListener.java
+++ b/scm-webapp/src/main/java/sonia/scm/repository/HealthCheckContextListener.java
@@ -128,15 +128,7 @@ public class HealthCheckContextListener implements ServletContextListener
{
// excute health checks for all repsitories asynchronous
- SecurityUtils.getSubject().execute(new Runnable()
- {
-
- @Override
- public void run()
- {
- healthChecker.checkAll();
- }
- });
+ SecurityUtils.getSubject().execute(healthChecker::checkAll);
}
//~--- fields -------------------------------------------------------------
diff --git a/scm-webapp/src/test/java/sonia/scm/cache/CacheTestBase.java b/scm-webapp/src/test/java/sonia/scm/cache/CacheTestBase.java
index 54cf7991c5..f0396741aa 100644
--- a/scm-webapp/src/test/java/sonia/scm/cache/CacheTestBase.java
+++ b/scm-webapp/src/test/java/sonia/scm/cache/CacheTestBase.java
@@ -35,18 +35,14 @@ package sonia.scm.cache;
//~--- non-JDK imports --------------------------------------------------------
-import com.google.common.base.Predicate;
-
import org.junit.After;
+import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
-
import sonia.scm.util.IOUtil;
-import static org.hamcrest.Matchers.*;
-
+import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.Assert.*;
-import org.junit.Assume;
/**
*
@@ -166,14 +162,7 @@ public abstract class CacheTestBase
cache.put("a-1", "test123");
cache.put("a-2", "test123");
- Iterable previous = cache.removeAll(new Predicate()
- {
- @Override
- public boolean apply(String item)
- {
- return item.startsWith("test");
- }
- });
+ Iterable previous = cache.removeAll(item -> item != null && item.startsWith("test"));
assertThat(previous, containsInAnyOrder("test123", "test456"));
assertNull(cache.get("test-1"));
@@ -188,8 +177,8 @@ public abstract class CacheTestBase
// skip test if implementation does not support stats
Assume.assumeTrue( stats != null );
assertEquals("test", stats.getName());
- assertEquals(0l, stats.getHitCount());
- assertEquals(0l, stats.getMissCount());
+ assertEquals(0L, stats.getHitCount());
+ assertEquals(0L, stats.getMissCount());
cache.put("test-1", "test123");
cache.put("test-2", "test456");
cache.get("test-1");
@@ -197,11 +186,11 @@ public abstract class CacheTestBase
cache.get("test-1");
cache.get("test-3");
// check that stats have not changed
- assertEquals(0l, stats.getHitCount());
- assertEquals(0l, stats.getMissCount());
+ assertEquals(0L, stats.getHitCount());
+ assertEquals(0L, stats.getMissCount());
stats = cache.getStatistics();
- assertEquals(3l, stats.getHitCount());
- assertEquals(1l, stats.getMissCount());
+ assertEquals(3L, stats.getHitCount());
+ assertEquals(1L, stats.getMissCount());
assertEquals(0.75d, stats.getHitRate(), 0.0d);
assertEquals(0.25d, stats.getMissRate(), 0.0d);
}
diff --git a/scm-webapp/src/test/java/sonia/scm/cache/GuavaConfigurationReaderTest.java b/scm-webapp/src/test/java/sonia/scm/cache/GuavaConfigurationReaderTest.java
index 7f7be94203..ff8b59cfc3 100644
--- a/scm-webapp/src/test/java/sonia/scm/cache/GuavaConfigurationReaderTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/cache/GuavaConfigurationReaderTest.java
@@ -63,7 +63,7 @@ public class GuavaConfigurationReaderTest
GuavaCacheConfiguration cfg =
readConfiguration("gcache.001.xml").getDefaultCache();
- assertCacheValues(cfg, 200l, 1200l, 2400l);
+ assertCacheValues(cfg, 200L, 1200L, 2400L);
}
/**
@@ -82,10 +82,10 @@ public class GuavaConfigurationReaderTest
//J+
// cache sonia.test.cache.001 override by cache.004.xml
- assertCacheValues(getCache(gcm, "sonia.test.cache.001"), 6l, 2l, 8l);
- assertCacheValues(getCache(gcm, "sonia.test.cache.002"), 1000l, 120l, 60l);
- assertCacheValues(getCache(gcm, "sonia.test.cache.003"), 3000l, 120l,
- 2400l);
+ assertCacheValues(getCache(gcm, "sonia.test.cache.001"), 6L, 2L, 8L);
+ assertCacheValues(getCache(gcm, "sonia.test.cache.002"), 1000L, 120L, 60L);
+ assertCacheValues(getCache(gcm, "sonia.test.cache.003"), 3000L, 120L,
+ 2400L);
}
/**
@@ -100,8 +100,8 @@ public class GuavaConfigurationReaderTest
// check default
- assertCacheValues(getCache(gcm, "sonia.test.cache.001"), 1000l, 60l, 30l);
- assertCacheValues(getCache(gcm, "sonia.test.cache.002"), 1000l, 120l, 60l);
+ assertCacheValues(getCache(gcm, "sonia.test.cache.001"), 1000L, 60L, 30L);
+ assertCacheValues(getCache(gcm, "sonia.test.cache.002"), 1000L, 120L, 60L);
}
/**
@@ -115,10 +115,10 @@ public class GuavaConfigurationReaderTest
Iterators.forArray("gcache.002.xml",
"gcache.003.xml"));
- assertCacheValues(getCache(gcm, "sonia.test.cache.001"), 1000l, 60l, 30l);
- assertCacheValues(getCache(gcm, "sonia.test.cache.002"), 1000l, 120l, 60l);
- assertCacheValues(getCache(gcm, "sonia.test.cache.003"), 3000l, 120l,
- 2400l);
+ assertCacheValues(getCache(gcm, "sonia.test.cache.001"), 1000L, 60L, 30L);
+ assertCacheValues(getCache(gcm, "sonia.test.cache.002"), 1000L, 120L, 60L);
+ assertCacheValues(getCache(gcm, "sonia.test.cache.003"), 3000L, 120L,
+ 2400L);
}
/**
@@ -131,7 +131,7 @@ public class GuavaConfigurationReaderTest
GuavaCacheConfiguration cfg =
readConfiguration("gcache.001.xml").getCaches().get(0);
- assertCacheValues(cfg, 1000l, 60l, 30l);
+ assertCacheValues(cfg, 1000L, 60L, 30L);
}
/**
@@ -144,7 +144,7 @@ public class GuavaConfigurationReaderTest
GuavaCacheConfiguration cfg =
readConfiguration("gcache.002.xml").getCaches().get(0);
- assertCacheValues(cfg, 1000l, 120l, 60l);
+ assertCacheValues(cfg, 1000L, 120L, 60L);
}
/**
diff --git a/scm-webapp/src/test/java/sonia/scm/security/ConfigurableLoginAttemptHandlerTest.java b/scm-webapp/src/test/java/sonia/scm/security/ConfigurableLoginAttemptHandlerTest.java
index 714438b8fe..01c4766396 100644
--- a/scm-webapp/src/test/java/sonia/scm/security/ConfigurableLoginAttemptHandlerTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/security/ConfigurableLoginAttemptHandlerTest.java
@@ -74,7 +74,7 @@ public class ConfigurableLoginAttemptHandlerTest {
handler.onUnsuccessfulAuthentication(token, new SimpleAuthenticationInfo());
handler.beforeAuthentication(token);
handler.onUnsuccessfulAuthentication(token, new SimpleAuthenticationInfo());
- Thread.sleep(TimeUnit.MILLISECONDS.toMillis(1200l));
+ Thread.sleep(TimeUnit.MILLISECONDS.toMillis(1200L));
handler.beforeAuthentication(token);
}
@@ -111,4 +111,4 @@ public class ConfigurableLoginAttemptHandlerTest {
return new ConfigurableLoginAttemptHandler(configuration);
}
-}
\ No newline at end of file
+}
diff --git a/scm-webapp/src/test/java/sonia/scm/security/DefaultKeyGeneratorTest.java b/scm-webapp/src/test/java/sonia/scm/security/DefaultKeyGeneratorTest.java
index edec43098c..57b038d5f1 100644
--- a/scm-webapp/src/test/java/sonia/scm/security/DefaultKeyGeneratorTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/security/DefaultKeyGeneratorTest.java
@@ -35,22 +35,15 @@ package sonia.scm.security;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.collect.Sets;
-
import org.junit.Test;
+import java.util.Set;
+import java.util.concurrent.*;
+
import static org.junit.Assert.*;
//~--- JDK imports ------------------------------------------------------------
-import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
/**
*
* @author Sebastian Sdorra
@@ -96,28 +89,22 @@ public class DefaultKeyGeneratorTest
for (int i = 0; i < 10; i++)
{
- Future> future = executor.submit(new Callable>()
- {
+ Future> future = executor.submit(() -> {
+ Set keys = Sets.newHashSet();
- @Override
- public Set call()
+ for (int i1 = 0; i1 < 1000; i1++)
{
- Set keys = Sets.newHashSet();
+ String key = generator.createKey();
- for (int i = 0; i < 1000; i++)
+ if (keys.contains(key))
{
- String key = generator.createKey();
-
- if (keys.contains(key))
- {
- fail("dublicate key");
- }
-
- keys.add(key);
+ fail("dublicate key");
}
- return keys;
+ keys.add(key);
}
+
+ return keys;
});
futureSet.add(future);
diff --git a/scm-webapp/src/test/java/sonia/scm/template/MustacheTemplateTest.java b/scm-webapp/src/test/java/sonia/scm/template/MustacheTemplateTest.java
index 186b809367..bfac40b4df 100644
--- a/scm-webapp/src/test/java/sonia/scm/template/MustacheTemplateTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/template/MustacheTemplateTest.java
@@ -89,15 +89,8 @@ public class MustacheTemplateTest extends TemplateTestBase
@Override
protected void prepareEnv(Map env)
{
- env.put("test", new Function()
- {
-
- @Override
- public String apply(String input)
- {
- throw new UnsupportedOperationException("Not supported yet.");
- }
-
+ env.put("test", (Function) input -> {
+ throw new UnsupportedOperationException("Not supported yet.");
});
}