This commit is contained in:
Florian Scholdei
2019-01-29 09:17:14 +01:00
28 changed files with 194 additions and 114 deletions

3
Jenkinsfile vendored
View File

@@ -26,7 +26,7 @@ node('docker') {
}
stage('Build') {
mvn 'clean install -DskipTests'
mvn 'clean install -Pdoc -DskipTests'
}
stage('Unit Test') {
@@ -53,6 +53,7 @@ node('docker') {
stage('Archive') {
archiveArtifacts 'scm-webapp/target/scm-webapp.war'
archiveArtifacts 'scm-server/target/scm-server-app.*'
archiveArtifacts 'scm-webapp/target/scm-webapp-restdocs.zip'
}
stage('Docker') {

View File

@@ -777,7 +777,7 @@
<jaxrs.version>2.0.1</jaxrs.version>
<resteasy.version>3.1.3.Final</resteasy.version>
<jersey-client.version>1.19.4</jersey-client.version>
<enunciate.version>2.9.1</enunciate.version>
<enunciate.version>2.11.1</enunciate.version>
<jackson.version>2.8.6</jackson.version>
<guice.version>4.0</guice.version>

View File

@@ -137,7 +137,7 @@
<profiles>
<profile>
<id>doc</id>
<id>plugin-doc</id>
<build>
<plugins>

View File

@@ -9,7 +9,7 @@
"flow": "flow check"
},
"dependencies": {
"@scm-manager/ui-extensions": "^0.1.1"
"@scm-manager/ui-extensions": "^0.1.2"
},
"devDependencies": {
"@scm-manager/ui-bundler": "^0.0.24"

View File

@@ -747,9 +747,10 @@
vinyl-source-stream "^2.0.0"
watchify "^3.11.0"
"@scm-manager/ui-extensions@^0.1.1":
version "0.1.1"
resolved "https://registry.yarnpkg.com/@scm-manager/ui-extensions/-/ui-extensions-0.1.1.tgz#966e62d89981e92a14adf7e674e646e76de96d45"
"@scm-manager/ui-extensions@^0.1.2":
version "0.1.2"
resolved "https://registry.yarnpkg.com/@scm-manager/ui-extensions/-/ui-extensions-0.1.2.tgz#0689427ca45c8e4e045b5b9dbc89036f1d2c45fc"
integrity sha512-oIkXcc/VWssnK/yjWKC/Wnq5DZ01rArsz76n4X/0DT0hkGNIKmwk/Fdp7OoXiUEb7+aaPjUX1VvDqlTwCNKPmA==
dependencies:
react "^16.4.2"
react-dom "^16.4.2"

View File

@@ -6,7 +6,7 @@
"build": "ui-bundler plugin"
},
"dependencies": {
"@scm-manager/ui-extensions": "^0.1.1"
"@scm-manager/ui-extensions": "^0.1.2"
},
"devDependencies": {
"@scm-manager/ui-bundler": "^0.0.24"

View File

@@ -681,9 +681,10 @@
vinyl-source-stream "^2.0.0"
watchify "^3.11.0"
"@scm-manager/ui-extensions@^0.1.1":
version "0.1.1"
resolved "https://registry.yarnpkg.com/@scm-manager/ui-extensions/-/ui-extensions-0.1.1.tgz#966e62d89981e92a14adf7e674e646e76de96d45"
"@scm-manager/ui-extensions@^0.1.2":
version "0.1.2"
resolved "https://registry.yarnpkg.com/@scm-manager/ui-extensions/-/ui-extensions-0.1.2.tgz#0689427ca45c8e4e045b5b9dbc89036f1d2c45fc"
integrity sha512-oIkXcc/VWssnK/yjWKC/Wnq5DZ01rArsz76n4X/0DT0hkGNIKmwk/Fdp7OoXiUEb7+aaPjUX1VvDqlTwCNKPmA==
dependencies:
react "^16.4.2"
react-dom "^16.4.2"

View File

@@ -6,7 +6,7 @@
"build": "ui-bundler plugin"
},
"dependencies": {
"@scm-manager/ui-extensions": "^0.1.1"
"@scm-manager/ui-extensions": "^0.1.2"
},
"devDependencies": {
"@scm-manager/ui-bundler": "^0.0.24"

View File

@@ -681,9 +681,10 @@
vinyl-source-stream "^2.0.0"
watchify "^3.11.0"
"@scm-manager/ui-extensions@^0.1.1":
version "0.1.1"
resolved "https://registry.yarnpkg.com/@scm-manager/ui-extensions/-/ui-extensions-0.1.1.tgz#966e62d89981e92a14adf7e674e646e76de96d45"
"@scm-manager/ui-extensions@^0.1.2":
version "0.1.2"
resolved "https://registry.yarnpkg.com/@scm-manager/ui-extensions/-/ui-extensions-0.1.2.tgz#0689427ca45c8e4e045b5b9dbc89036f1d2c45fc"
integrity sha512-oIkXcc/VWssnK/yjWKC/Wnq5DZ01rArsz76n4X/0DT0hkGNIKmwk/Fdp7OoXiUEb7+aaPjUX1VvDqlTwCNKPmA==
dependencies:
react "^16.4.2"
react-dom "^16.4.2"

View File

@@ -26,7 +26,7 @@
"react-router-enzyme-context": "^1.2.0"
},
"dependencies": {
"@scm-manager/ui-extensions": "^0.1.1",
"@scm-manager/ui-extensions": "^0.1.2",
"@scm-manager/ui-types": "2.0.0-SNAPSHOT",
"classnames": "^2.2.6",
"moment": "^2.22.2",

View File

@@ -25,13 +25,13 @@ class LinkPaginator extends React.Component<Props> {
);
}
renderPreviousButton(label?: string) {
renderPreviousButton(className: string, label?: string) {
const { page } = this.props;
const previousPage = page - 1;
return (
<Button
className={"pagination-previous"}
className={className}
label={label ? label : previousPage.toString()}
disabled={!this.hasLink("prev")}
link={`${previousPage}`}
@@ -44,12 +44,12 @@ class LinkPaginator extends React.Component<Props> {
return collection._links[name];
}
renderNextButton(label?: string) {
renderNextButton(className: string, label?: string) {
const { page } = this.props;
const nextPage = page + 1;
return (
<Button
className={"pagination-next"}
className={className}
label={label ? label : nextPage.toString()}
disabled={!this.hasLink("next")}
link={`${nextPage}`}
@@ -96,13 +96,13 @@ class LinkPaginator extends React.Component<Props> {
links.push(this.separator());
}
if (page > 2) {
links.push(this.renderPreviousButton());
links.push(this.renderPreviousButton("pagination-link"));
}
links.push(this.currentPage(page));
if (page + 1 < pageTotal) {
links.push(this.renderNextButton());
links.push(this.renderNextButton("pagination-link"));
}
if (page + 2 < pageTotal)
//if there exists pages between next and last
@@ -118,13 +118,13 @@ class LinkPaginator extends React.Component<Props> {
const { t } = this.props;
return (
<nav className="pagination is-centered" aria-label="pagination">
{this.renderPreviousButton(t("paginator.previous"))}
{this.renderPreviousButton("pagination-previous", t("paginator.previous"))}
<ul className="pagination-list">
{this.pageLinks().map((link, index) => {
return <li key={index}>{link}</li>;
})}
</ul>
{this.renderNextButton(t("paginator.next"))}
{this.renderNextButton("pagination-next", t("paginator.next"))}
</nav>
);
}

View File

@@ -1,7 +1,5 @@
//@flow
import React from "react";
import Button, { type ButtonProps } from "./Button";
import type {File} from "@scm-manager/ui-types";
type Props = {
displayName: string,
@@ -10,9 +8,9 @@ type Props = {
class DownloadButton extends React.Component<Props> {
render() {
const {displayName, url} = this.props;
const { displayName, url } = this.props;
return (
<a className="button is-large is-info" href={url}>
<a className="button is-large is-link" href={url}>
<span className="icon is-medium">
<i className="fas fa-arrow-circle-down" />
</span>

View File

@@ -0,0 +1,45 @@
//@flow
import React from "react";
import { Help } from "../index";
type Props = {
label?: string,
name?: string,
value?: string,
checked: boolean,
onChange?: (value: boolean, name?: string) => void,
disabled?: boolean,
helpText?: string
};
class Radio extends React.Component<Props> {
renderHelp = () => {
const helpText = this.props.helpText;
if (helpText) {
return <Help message={helpText} />;
}
};
render() {
return (
<div className="field is-grouped">
<div className="control">
<label className="radio" disabled={this.props.disabled}>
<input
type="radio"
name={this.props.name}
value={this.props.value}
checked={this.props.checked}
onChange={this.props.onChange}
disabled={this.props.disabled}
/>{" "}
{this.props.label}
{this.renderHelp()}
</label>
</div>
</div>
);
}
}
export default Radio;

View File

@@ -4,6 +4,7 @@ export { default as AddEntryToTableField } from "./AddEntryToTableField.js";
export { default as AutocompleteAddEntryToTableField } from "./AutocompleteAddEntryToTableField.js";
export { default as MemberNameTable } from "./MemberNameTable.js";
export { default as Checkbox } from "./Checkbox.js";
export { default as Radio } from "./Radio.js";
export { default as InputField } from "./InputField.js";
export { default as Select } from "./Select.js";
export { default as Textarea } from "./Textarea.js";

View File

@@ -43,6 +43,7 @@ class ConfirmAlert extends React.Component<Props> {
<button
key={i}
onClick={() => this.handleClickButton(button)}
href="javascript:void(0);"
>
{button.label}
</button>

View File

@@ -18,7 +18,7 @@ class NavAction extends React.Component<Props> {
return (
<li>
<a onClick={action}>{showIcon}{label}</a>
<a onClick={action} href="javascript:void(0);">{showIcon}{label}</a>
</li>
);
}

View File

@@ -3,15 +3,16 @@ 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 { parseDescription } from "./changesets";
import { AvatarWrapper, AvatarImage } from "../../avatar";
import { ExtensionPoint } from "@scm-manager/ui-extensions";
const styles = {
pointer: {
@@ -64,7 +65,15 @@ class ChangesetRow extends React.Component<Props> {
<div className={classNames("media-content", classes.withOverflow)}>
<div className="content">
<p className="is-ellipsis-overflow">
<strong>{description.title}</strong>
<strong>
<ExtensionPoint
name="changesets.changeset.description"
props={{ changeset, value: description.title }}
renderAll={false}
>
{description.title}
</ExtensionPoint>
</strong>
<br />
<Interpolate
i18nKey="changesets.changeset.summary"

View File

@@ -727,9 +727,9 @@
vinyl-source-stream "^2.0.0"
watchify "^3.11.0"
"@scm-manager/ui-extensions@^0.1.1":
version "0.1.1"
resolved "https://registry.yarnpkg.com/@scm-manager/ui-extensions/-/ui-extensions-0.1.1.tgz#966e62d89981e92a14adf7e674e646e76de96d45"
"@scm-manager/ui-extensions@^0.1.2":
version "0.1.2"
resolved "https://registry.yarnpkg.com/@scm-manager/ui-extensions/-/ui-extensions-0.1.2.tgz#0689427ca45c8e4e045b5b9dbc89036f1d2c45fc"
dependencies:
react "^16.4.2"
react-dom "^16.4.2"

View File

@@ -7,7 +7,7 @@
"dependencies": {
"@babel/polyfill": "^7.0.0",
"@fortawesome/fontawesome-free": "^5.3.1",
"@scm-manager/ui-extensions": "^0.1.1",
"@scm-manager/ui-extensions": "^0.1.2",
"bulma": "^0.7.1",
"bulma-tooltip": "^2.0.2",
"classnames": "^2.2.5",

View File

@@ -46,7 +46,15 @@ class ChangesetDetails extends React.Component<Props> {
return (
<div>
<div className="content">
<h4>{description.title}</h4>
<h4>
<ExtensionPoint
name="changesets.changeset.description"
props={{ changeset, value: description.title }}
renderAll={false}
>
{description.title}
</ExtensionPoint>
</h4>
<article className="media">
<AvatarWrapper>
<p className={classNames("image", "is-64x64", classes.spacing)}>
@@ -67,22 +75,23 @@ class ChangesetDetails extends React.Component<Props> {
</div>
<div className="media-right">{this.renderTags()}</div>
</article>
<ExtensionPoint
name="changesets.changeset.description"
props={{ changeset, description }}
renderAll={true}
>
<p>
{description.message.split("\n").map((item, key) => {
return (
<span key={key}>
<p>
{description.message.split("\n").map((item, key) => {
return (
<span key={key}>
<ExtensionPoint
name="changesets.changeset.description"
props={{ changeset, value: item }}
renderAll={false}
>
{item}
<br />
</span>
);
})}
</p>
</ExtensionPoint>
</ExtensionPoint>
<br />
</span>
);
})}
</p>
</div>
<div>
<ChangesetDiff changeset={changeset} />

View File

@@ -9,16 +9,6 @@ import classNames from "classnames";
import RepositoryAvatar from "./RepositoryAvatar";
const styles = {
overlayFullColumn: {
position: "absolute",
height: "calc(120px - 0.5rem)",
width: "calc(100% - 1.5rem)"
},
overlayHalfColumn: {
position: "absolute",
height: "calc(120px - 1.5rem)",
width: "calc(50% - 3rem)"
},
inner: {
position: "relative",
pointerEvents: "none",
@@ -86,8 +76,8 @@ class RepositoryEntry extends React.Component<Props> {
const repositoryLink = this.createLink(repository);
const halfColumn = fullColumnWidth ? "is-full" : "is-half";
const overlayLinkClass = fullColumnWidth
? classes.overlayFullColumn
: classes.overlayHalfColumn;
? "overlay-full-column"
: "overlay-half-column";
return (
<div
className={classNames(

View File

@@ -1,7 +1,7 @@
// @flow
import React from "react";
import { translate } from "react-i18next";
import { Autocomplete, SubmitButton } from "@scm-manager/ui-components";
import { Autocomplete, Radio, SubmitButton } from "@scm-manager/ui-components";
import TypeSelector from "./TypeSelector";
import type {
PermissionCollection,
@@ -132,43 +132,34 @@ class CreatePermissionForm extends React.Component<Props, State> {
{t("permission.add-permission.add-permission-heading")}
</h2>
<form onSubmit={this.submit}>
<div className="control">
<label className="radio">
<input
type="radio"
name="permission_scope"
checked={!this.state.groupPermission}
value="USER_PERMISSION"
onChange={this.permissionScopeChanged}
/>
{t("permission.user-permission")}
</label>
<label className="radio">
<input
type="radio"
name="permission_scope"
value="GROUP_PERMISSION"
checked={this.state.groupPermission}
onChange={this.permissionScopeChanged}
/>
{t("permission.group-permission")}
</label>
</div>
<div className="columns">
<Radio
name="permission_scope"
value="USER_PERMISSION"
checked={!this.state.groupPermission}
label={t("permission.user-permission")}
onChange={this.permissionScopeChanged}
/>
<Radio
name="permission_scope"
value="GROUP_PERMISSION"
checked={this.state.groupPermission}
label={t("permission.group-permission")}
onChange={this.permissionScopeChanged}
/>
<div className="columns">
<div className="column is-three-quarters">
{this.renderAutocompletionField()}
{this.renderAutocompletionField()}
</div>
<div className="column is-one-quarter">
<TypeSelector
label={t("permission.type")}
helpText={t("permission.help.typeHelpText")}
handleTypeChange={this.handleTypeChange}
type={type ? type : "READ"}
/>
<TypeSelector
label={t("permission.type")}
helpText={t("permission.help.typeHelpText")}
handleTypeChange={this.handleTypeChange}
type={type ? type : "READ"}
/>
</div>
</div>
<div className="columns">
</div>
<div className="columns">
<div className="column">
<SubmitButton
label={t("permission.add-permission.submit-button")}
@@ -176,7 +167,7 @@ class CreatePermissionForm extends React.Component<Props, State> {
disabled={!this.state.valid || this.state.name === ""}
/>
</div>
</div>
</div>
</form>
</div>
);

View File

@@ -25,9 +25,9 @@ class ButtonGroup extends React.Component<Props> {
let historyColor = "";
if (historyIsSelected) {
historyColor = "info is-selected";
historyColor = "link is-selected";
} else {
sourcesColor = "info is-selected";
sourcesColor = "link is-selected";
}
const sourcesLabel = (

View File

@@ -92,14 +92,16 @@ class UserForm extends React.Component<Props, State> {
if (!this.props.user) {
// create new user
nameField = (
<InputField
label={t("user.name")}
onChange={this.handleUsernameChange}
value={user ? user.name : ""}
validationError={this.state.nameValidationError}
errorMessage={t("validation.name-invalid")}
helpText={t("help.usernameHelpText")}
/>
<div className="column is-half">
<InputField
label={t("user.name")}
onChange={this.handleUsernameChange}
value={user ? user.name : ""}
validationError={this.state.nameValidationError}
errorMessage={t("validation.name-invalid")}
helpText={t("help.usernameHelpText")}
/>
</div>
);
passwordChangeField = (

View File

@@ -4,6 +4,8 @@
$blue: #33b2e8;
$mint: #11dfd0;
$info: $blue;
// $footer-background-color
.is-ellipsis-overflow {
@@ -100,6 +102,19 @@ $fa-font-path: "webfonts";
&:nth-child(odd) {
margin-right: 1.5rem;
}
.overlay-half-column {
position: absolute;
height: calc(120px - 1.5rem);
width: calc(50% - 3rem);
}
}
.column.is-full {
.overlay-full-column {
position: absolute;
height: calc(120px - 0.5rem);
width: calc(100% - 1.5rem);
}
}
@media screen and (max-width: 768px) {
.column.is-half {
@@ -108,6 +123,13 @@ $fa-font-path: "webfonts";
&:nth-child(odd) {
margin-right: 0;
}
.overlay-half-column{
position: absolute;
height: calc(120px - 0.5rem);
width: calc(100% - 1.5rem);
}
}
}
}

View File

@@ -738,9 +738,10 @@
vinyl-source-stream "^2.0.0"
watchify "^3.11.0"
"@scm-manager/ui-extensions@^0.1.1":
version "0.1.1"
resolved "https://registry.yarnpkg.com/@scm-manager/ui-extensions/-/ui-extensions-0.1.1.tgz#966e62d89981e92a14adf7e674e646e76de96d45"
"@scm-manager/ui-extensions@^0.1.2":
version "0.1.2"
resolved "https://registry.yarnpkg.com/@scm-manager/ui-extensions/-/ui-extensions-0.1.2.tgz#0689427ca45c8e4e045b5b9dbc89036f1d2c45fc"
integrity sha512-oIkXcc/VWssnK/yjWKC/Wnq5DZ01rArsz76n4X/0DT0hkGNIKmwk/Fdp7OoXiUEb7+aaPjUX1VvDqlTwCNKPmA==
dependencies:
react "^16.4.2"
react-dom "^16.4.2"

View File

@@ -896,6 +896,11 @@
<artifactId>enunciate-lombok</artifactId>
<version>${enunciate.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
</dependencies>
</plugin>

View File

@@ -41,6 +41,8 @@ public abstract class RepositoryToRepositoryDtoMapper extends BaseMapper<Reposit
}
if (RepositoryPermissions.modify(repository).isPermitted()) {
linksBuilder.single(link("update", resourceLinks.repository().update(target.getNamespace(), target.getName())));
}
if (RepositoryPermissions.permissionRead(repository).isPermitted()) {
linksBuilder.single(link("permissions", resourceLinks.repositoryPermission().all(target.getNamespace(), target.getName())));
}
try (RepositoryService repositoryService = serviceFactory.create(repository)) {