Merge with 2.0.0-m3

This commit is contained in:
René Pfeuffer
2019-05-28 07:57:55 +02:00
21 changed files with 276 additions and 176 deletions

View File

@@ -0,0 +1,47 @@
// @flow
import * as React from "react";
import { withRouter } from "react-router-dom";
import { withContextPath } from "./urls";
/**
* Adds anchor links to markdown headings.
*
* @see <a href="https://github.com/rexxars/react-markdown/issues/69">Headings are missing anchors / ids</a>
*/
type Props = {
children: React.Node,
level: number,
location: any
};
function flatten(text: string, child: any) {
return typeof child === "string"
? text + child
: React.Children.toArray(child.props.children).reduce(flatten, text);
}
/**
* Turns heading text into a anchor id
*
* @VisibleForTesting
*/
export function headingToAnchorId(heading: string) {
return heading.toLowerCase().replace(/\W/g, "-");
}
function MarkdownHeadingRenderer(props: Props) {
const children = React.Children.toArray(props.children);
const heading = children.reduce(flatten, "");
const anchorId = headingToAnchorId(heading);
const headingElement = React.createElement("h" + props.level, {}, props.children);
const href = withContextPath(props.location.pathname + "#" + anchorId);
return (
<a id={`${anchorId}`} className="anchor" href={href}>
{headingElement}
</a>
);
}
export default withRouter(MarkdownHeadingRenderer);

View File

@@ -0,0 +1,18 @@
// @flow
import React from "react";
import { headingToAnchorId } from "./MarkdownHeadingRenderer";
describe("headingToAnchorId tests", () => {
it("should lower case the text", () => {
expect(headingToAnchorId("Hello")).toBe("hello");
expect(headingToAnchorId("HeLlO")).toBe("hello");
expect(headingToAnchorId("HELLO")).toBe("hello");
});
it("should replace spaces with hyphen", () => {
expect(headingToAnchorId("awesome stuff")).toBe("awesome-stuff");
expect(headingToAnchorId("a b c d e f")).toBe("a-b-c-d-e-f");
});
});

View File

@@ -3,17 +3,49 @@ import React from "react";
import SyntaxHighlighter from "./SyntaxHighlighter"; import SyntaxHighlighter from "./SyntaxHighlighter";
import Markdown from "react-markdown/with-html"; import Markdown from "react-markdown/with-html";
import {binder} from "@scm-manager/ui-extensions"; import {binder} from "@scm-manager/ui-extensions";
import MarkdownHeadingRenderer from "./MarkdownHeadingRenderer";
import { withRouter } from "react-router-dom";
type Props = { type Props = {
content: string, content: string,
renderContext?: Object, renderContext?: Object,
renderers?: Object, renderers?: Object,
enableAnchorHeadings: boolean,
// context props
location: any
}; };
class MarkdownView extends React.Component<Props> { class MarkdownView extends React.Component<Props> {
static defaultProps = {
enableAnchorHeadings: false
};
contentRef: ?HTMLDivElement;
constructor(props: Props) {
super(props);
}
componentDidUpdate() {
// we have to use componentDidUpdate, because we have to wait until all
// children are rendered and componentDidMount is called before the
// markdown content was rendered.
const hash = this.props.location.hash;
if (this.contentRef && hash) {
// we query only child elements, to avoid strange scrolling with multiple
// markdown elements on one page.
const element = this.contentRef.querySelector(hash);
if (element && element.scrollIntoView) {
element.scrollIntoView();
}
}
}
render() { render() {
const {content, renderers, renderContext} = this.props; const {content, renderers, renderContext, enableAnchorHeadings} = this.props;
const rendererFactory = binder.getExtension("markdown-renderer-factory"); const rendererFactory = binder.getExtension("markdown-renderer-factory");
let rendererList = renderers; let rendererList = renderers;
@@ -26,11 +58,16 @@ class MarkdownView extends React.Component<Props> {
rendererList = {}; rendererList = {};
} }
if (enableAnchorHeadings) {
rendererList.heading = MarkdownHeadingRenderer;
}
if (!rendererList.code){ if (!rendererList.code){
rendererList.code = SyntaxHighlighter; rendererList.code = SyntaxHighlighter;
} }
return ( return (
<div ref={el => (this.contentRef = el)}>
<Markdown <Markdown
className="content" className="content"
skipHtml={true} skipHtml={true}
@@ -38,8 +75,9 @@ class MarkdownView extends React.Component<Props> {
source={content} source={content}
renderers={rendererList} renderers={rendererList}
/> />
</div>
); );
} }
} }
export default MarkdownView; export default withRouter(MarkdownView);

View File

@@ -9,30 +9,26 @@
"repositoryRole": { "repositoryRole": {
"navLink": "Berechtigungsrollen", "navLink": "Berechtigungsrollen",
"title": "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.", "noPermissionRoles": "Keine Berechtigungsrollen gefunden.",
"system": "System", "createButton": "Berechtigungsrolle erstellen"
"createButton": "Berechtigungsrolle erstellen", },
"editButton": "Bearbeiten",
"name": "Name", "name": "Name",
"type": "Typ", "type": "Typ",
"verbs": "Berechtigungen", "verbs": "Berechtigungen",
"button": { "system": "System",
"edit": "Bearbeiten"
},
"create": {
"name": "Name"
},
"edit": "Berechtigungsrolle bearbeiten",
"form": { "form": {
"subtitle": "Berechtigungsrolle bearbeiten",
"name": "Name", "name": "Name",
"permissions": "Berechtigungen", "permissions": "Berechtigungen",
"submit": "Speichern" "submit": "Speichern"
} }
}, },
"role": {
"name": "Name",
"system": "System"
},
"deleteRole" : { "deleteRole" : {
"button": "Löschen", "button": "Löschen",
"subtitle": "Berechtigungsrolle löschen", "subtitle": "Berechtigungsrolle löschen",

View File

@@ -9,31 +9,27 @@
"repositoryRole": { "repositoryRole": {
"navLink": "Permission Roles", "navLink": "Permission Roles",
"title": "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.", "noPermissionRoles": "No permission roles found.",
"system": "System", "createButton": "Create Permission Role"
"createButton": "Create Permission Role", },
"editButton": "Edit",
"name": "Name", "name": "Name",
"type": "Type", "type": "Type",
"verbs": "Permissions", "verbs": "Permissions",
"edit": "Edit Permission Role", "system": "System",
"button": {
"edit": "Edit"
},
"create": {
"name": "Name"
},
"form": { "form": {
"subtitle": "Edit Permission Role",
"name": "Name", "name": "Name",
"permissions": "Permissions", "permissions": "Permissions",
"submit": "Save" "submit": "Save"
} }
}, },
"role": { "deleteRole": {
"name": "Name",
"system": "System"
},
"deleteRole" : {
"button": "Delete", "button": "Delete",
"subtitle": "Delete Permission Role", "subtitle": "Delete Permission Role",
"confirmAlert": { "confirmAlert": {

View File

@@ -74,7 +74,6 @@ class Config extends React.Component<Props> {
path={`${url}/roles/create`} path={`${url}/roles/create`}
render={() => ( render={() => (
<CreateRepositoryRole <CreateRepositoryRole
disabled={false}
history={this.props.history} history={this.props.history}
/> />
)} )}
@@ -104,6 +103,7 @@ class Config extends React.Component<Props> {
to={`${url}/roles/`} to={`${url}/roles/`}
label={t("repositoryRole.navLink")} label={t("repositoryRole.navLink")}
activeWhenMatch={this.matchesRoles} activeWhenMatch={this.matchesRoles}
activeOnlyWhenExact={false}
/> />
<ExtensionPoint <ExtensionPoint
name="config.navigation" name="config.navigation"

View File

@@ -1,13 +1,15 @@
//@flow //@flow
import React from "react"; import React from "react";
import type { RepositoryRole } from "@scm-manager/ui-types";
import { translate } from "react-i18next";
import { compose } from "redux"; import { compose } from "redux";
import injectSheet from "react-jss"; import injectSheet from "react-jss";
import { translate } from "react-i18next";
import type { RepositoryRole } from "@scm-manager/ui-types";
type Props = { type Props = {
role: RepositoryRole, role: RepositoryRole,
// context props // context props
classes: any,
t: string => string t: string => string
}; };

View File

@@ -2,9 +2,9 @@
import React from "react"; import React from "react";
import { translate } from "react-i18next"; import { translate } from "react-i18next";
import type { RepositoryRole } from "@scm-manager/ui-types"; import type { RepositoryRole } from "@scm-manager/ui-types";
import ExtensionPoint from "@scm-manager/ui-extensions/lib/ExtensionPoint"; import { ExtensionPoint } from "@scm-manager/ui-extensions";
import PermissionRoleDetailsTable from "./PermissionRoleDetailsTable"; import PermissionRoleDetailsTable from "./PermissionRoleDetailsTable";
import { Button, Subtitle } from "@scm-manager/ui-components"; import { Button } from "@scm-manager/ui-components";
type Props = { type Props = {
role: RepositoryRole, role: RepositoryRole,
@@ -20,7 +20,7 @@ class PermissionRoleDetails extends React.Component<Props> {
if (!!this.props.role._links.update) { if (!!this.props.role._links.update) {
return ( return (
<Button <Button
label={t("repositoryRole.button.edit")} label={t("repositoryRole.editButton")}
link={`${url}/edit`} link={`${url}/edit`}
color="primary" color="primary"
/> />
@@ -33,18 +33,16 @@ class PermissionRoleDetails extends React.Component<Props> {
const { role } = this.props; const { role } = this.props;
return ( return (
<div> <>
<PermissionRoleDetailsTable role={role} /> <PermissionRoleDetailsTable role={role} />
<hr /> <hr />
{this.renderEditButton()} {this.renderEditButton()}
<div className="content">
<ExtensionPoint <ExtensionPoint
name="repositoryRole.role-details.information" name="repositoryRole.role-details.information"
renderAll={true} renderAll={true}
props={{ role }} props={{ role }}
/> />
</div> </>
</div>
); );
} }
} }

View File

@@ -6,6 +6,7 @@ import AvailableVerbs from "./AvailableVerbs";
type Props = { type Props = {
role: RepositoryRole, role: RepositoryRole,
// context props // context props
t: string => string t: string => string
}; };

View File

@@ -8,6 +8,7 @@ type Props = {
baseUrl: string, baseUrl: string,
roles: RepositoryRole[], roles: RepositoryRole[],
// context props
t: string => string t: string => string
}; };
@@ -18,7 +19,7 @@ class PermissionRoleTable extends React.Component<Props> {
<table className="card-table table is-hoverable is-fullwidth"> <table className="card-table table is-hoverable is-fullwidth">
<thead> <thead>
<tr> <tr>
<th>{t("repositoryRole.form.name")}</th> <th>{t("repositoryRole.name")}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>

View File

@@ -6,6 +6,8 @@ import { translate } from "react-i18next";
type Props = { type Props = {
system?: boolean, system?: boolean,
// context props
classes: any, classes: any,
t: string => string t: string => string
}; };
@@ -24,7 +26,7 @@ class SystemRoleTag extends React.Component<Props> {
if (system) { if (system) {
return ( return (
<span className={classNames("tag is-dark", classes.tag)}> <span className={classNames("tag is-dark", classes.tag)}>
{t("role.system")} {t("repositoryRole.system")}
</span> </span>
); );
} }

View File

@@ -3,7 +3,7 @@ import React from "react";
import RepositoryRoleForm from "./RepositoryRoleForm"; import RepositoryRoleForm from "./RepositoryRoleForm";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { translate } from "react-i18next"; import { translate } from "react-i18next";
import { ErrorNotification, Title } from "@scm-manager/ui-components"; import {ErrorNotification, Subtitle, Title} from "@scm-manager/ui-components";
import { import {
createRole, createRole,
getCreateRoleFailure, getCreateRoleFailure,
@@ -15,11 +15,12 @@ import {
getRepositoryRolesLink, getRepositoryRolesLink,
getRepositoryVerbsLink getRepositoryVerbsLink
} from "../../../modules/indexResource"; } from "../../../modules/indexResource";
import type {History} from "history";
type Props = { type Props = {
disabled: boolean,
repositoryRolesLink: string, repositoryRolesLink: string,
error?: Error, error?: Error,
history: History,
//dispatch function //dispatch function
addRole: (link: string, role: RepositoryRole, callback?: () => void) => void, addRole: (link: string, role: RepositoryRole, callback?: () => void) => void,
@@ -50,8 +51,8 @@ class CreateRepositoryRole extends React.Component<Props> {
return ( return (
<> <>
<Title title={t("repositoryRole.title")} /> <Title title={t("repositoryRole.title")} />
<Subtitle subtitle={t("repositoryRole.createSubtitle")} />
<RepositoryRoleForm <RepositoryRoleForm
disabled={this.props.disabled}
submitForm={role => this.createRepositoryRole(role)} submitForm={role => this.createRepositoryRole(role)}
/> />
</> </>

View File

@@ -8,8 +8,9 @@ import {
isModifyRolePending, isModifyRolePending,
modifyRole modifyRole
} from "../modules/roles"; } from "../modules/roles";
import { ErrorNotification } from "@scm-manager/ui-components"; import { ErrorNotification, Subtitle } from "@scm-manager/ui-components";
import type { RepositoryRole } from "@scm-manager/ui-types"; import type { RepositoryRole } from "@scm-manager/ui-types";
import type { History } from "history";
import DeleteRepositoryRole from "./DeleteRepositoryRole"; import DeleteRepositoryRole from "./DeleteRepositoryRole";
type Props = { type Props = {
@@ -18,26 +19,25 @@ type Props = {
repositoryRolesLink: string, repositoryRolesLink: string,
error?: Error, error?: Error,
// context objects
t: string => string,
history: History,
//dispatch function //dispatch function
updateRole: ( updateRole: (role: RepositoryRole, callback?: () => void) => void
link: string,
role: RepositoryRole,
callback?: () => void
) => void
}; };
class EditRepositoryRole extends React.Component<Props> { class EditRepositoryRole extends React.Component<Props> {
repositoryRoleUpdated = (role: RepositoryRole) => { repositoryRoleUpdated = () => {
const { history } = this.props; this.props.history.push("/config/roles/");
history.push("/config/roles/");
}; };
updateRepositoryRole = (role: RepositoryRole) => { updateRepositoryRole = (role: RepositoryRole) => {
this.props.updateRole(role, () => this.repositoryRoleUpdated(role)); this.props.updateRole(role, this.repositoryRoleUpdated);
}; };
render() { render() {
const { error } = this.props; const { error, t } = this.props;
if (error) { if (error) {
return <ErrorNotification error={error} />; return <ErrorNotification error={error} />;
@@ -45,8 +45,8 @@ class EditRepositoryRole extends React.Component<Props> {
return ( return (
<> <>
<Subtitle subtitle={t("repositoryRole.editSubtitle")} />
<RepositoryRoleForm <RepositoryRoleForm
nameDisabled={true}
role={this.props.role} role={this.props.role}
submitForm={role => this.updateRepositoryRole(role)} submitForm={role => this.updateRepositoryRole(role)}
/> />
@@ -58,7 +58,7 @@ class EditRepositoryRole extends React.Component<Props> {
} }
const mapStateToProps = (state, ownProps) => { const mapStateToProps = (state, ownProps) => {
const loading = isModifyRolePending(state); const loading = isModifyRolePending(state, ownProps.role.name);
const error = getModifyRoleFailure(state, ownProps.role.name); const error = getModifyRoleFailure(state, ownProps.role.name);
return { return {

View File

@@ -19,7 +19,6 @@ import {
type Props = { type Props = {
role?: RepositoryRole, role?: RepositoryRole,
loading?: boolean, loading?: boolean,
nameDisabled: boolean,
availableVerbs: string[], availableVerbs: string[],
verbsLink: string, verbsLink: string,
submitForm: RepositoryRole => void, submitForm: RepositoryRole => void,
@@ -103,7 +102,7 @@ class RepositoryRoleForm extends React.Component<Props, State> {
}; };
render() { render() {
const { loading, availableVerbs, nameDisabled, t } = this.props; const { loading, availableVerbs, t } = this.props;
const { role } = this.state; const { role } = this.state;
const verbSelectBoxes = !availableVerbs const verbSelectBoxes = !availableVerbs
@@ -119,28 +118,25 @@ class RepositoryRoleForm extends React.Component<Props, State> {
return ( return (
<form onSubmit={this.submit}> <form onSubmit={this.submit}>
<div className="columns">
<div className="column">
<InputField <InputField
name="name" name="name"
label={t("repositoryRole.create.name")} label={t("repositoryRole.form.name")}
onChange={this.handleNameChange} onChange={this.handleNameChange}
value={role.name ? role.name : ""} value={role.name ? role.name : ""}
disabled={nameDisabled} disabled={!!this.props.role}
/> />
<div className="field">
<label className="label">
{t("repositoryRole.form.permissions")}
</label>
{verbSelectBoxes}
</div> </div>
</div>
<>{verbSelectBoxes}</>
<hr /> <hr />
<div className="columns">
<div className="column">
<SubmitButton <SubmitButton
loading={loading} loading={loading}
label={t("repositoryRole.form.submit")} label={t("repositoryRole.form.submit")}
disabled={!this.isValid()} disabled={!this.isValid()}
/> />
</div>
</div>
</form> </form>
); );
} }

View File

@@ -7,6 +7,7 @@ import type { History } from "history";
import type { RepositoryRole, PagedCollection } from "@scm-manager/ui-types"; import type { RepositoryRole, PagedCollection } from "@scm-manager/ui-types";
import { import {
Title, Title,
Subtitle,
Loading, Loading,
Notification, Notification,
LinkPaginator, LinkPaginator,
@@ -22,7 +23,8 @@ import {
getFetchRolesFailure getFetchRolesFailure
} from "../modules/roles"; } from "../modules/roles";
import PermissionRoleTable from "../components/PermissionRoleTable"; import PermissionRoleTable from "../components/PermissionRoleTable";
import { getRolesLink } from "../../../modules/indexResource"; import { getRepositoryRolesLink } from "../../../modules/indexResource";
type Props = { type Props = {
baseUrl: string, baseUrl: string,
roles: RepositoryRole[], roles: RepositoryRole[],
@@ -36,6 +38,7 @@ type Props = {
// context objects // context objects
t: string => string, t: string => string,
history: History, history: History,
location: any,
// dispatch functions // dispatch functions
fetchRolesByPage: (link: string, page: number) => void fetchRolesByPage: (link: string, page: number) => void
@@ -61,8 +64,7 @@ class RepositoryRoles extends React.Component<Props> {
if (page !== statePage || prevProps.location.search !== location.search) { if (page !== statePage || prevProps.location.search !== location.search) {
fetchRolesByPage( fetchRolesByPage(
rolesLink, rolesLink,
page, page
urls.getQueryStringFromLocation(location)
); );
} }
} }
@@ -76,11 +78,12 @@ class RepositoryRoles extends React.Component<Props> {
} }
return ( return (
<div> <>
<Title title={t("repositoryRole.title")} /> <Title title={t("repositoryRole.title")} />
<Subtitle subtitle={t("repositoryRole.overview.title")} />
{this.renderPermissionsTable()} {this.renderPermissionsTable()}
{this.renderCreateButton()} {this.renderCreateButton()}
</div> </>
); );
} }
@@ -96,7 +99,7 @@ class RepositoryRoles extends React.Component<Props> {
} }
return ( return (
<Notification type="info"> <Notification type="info">
{t("repositoryRole.noPermissionRoles")} {t("repositoryRole.overview.noPermissionRoles")}
</Notification> </Notification>
); );
} }
@@ -106,7 +109,7 @@ class RepositoryRoles extends React.Component<Props> {
if (canAddRoles) { if (canAddRoles) {
return ( return (
<CreateButton <CreateButton
label={t("repositoryRole.createButton")} label={t("repositoryRole.overview.createButton")}
link={`${baseUrl}/create`} link={`${baseUrl}/create`}
/> />
); );
@@ -123,7 +126,7 @@ const mapStateToProps = (state, ownProps) => {
const page = urls.getPageFromMatch(match); const page = urls.getPageFromMatch(match);
const canAddRoles = isPermittedToCreateRoles(state); const canAddRoles = isPermittedToCreateRoles(state);
const list = selectListAsCollection(state); const list = selectListAsCollection(state);
const rolesLink = getRolesLink(state); const rolesLink = getRepositoryRolesLink(state);
return { return {
roles, roles,

View File

@@ -81,8 +81,6 @@ class SingleRepositoryRole extends React.Component<Props> {
return ( return (
<> <>
<Title title={t("repositoryRole.title")} /> <Title title={t("repositoryRole.title")} />
<div className="columns">
<div className="column is-three-quarters">
<Route <Route
path={`${url}/info`} path={`${url}/info`}
component={() => <PermissionRoleDetail role={role} url={url} />} component={() => <PermissionRoleDetail role={role} url={url} />}
@@ -99,8 +97,6 @@ class SingleRepositoryRole extends React.Component<Props> {
props={extensionProps} props={extensionProps}
renderAll={true} renderAll={true}
/> />
</div>
</div>
</> </>
); );
} }

View File

@@ -159,10 +159,6 @@ export function getSvnConfigLink(state: Object) {
return getLink(state, "svnConfig"); return getLink(state, "svnConfig");
} }
export function getRolesLink(state: Object) {
return getLink(state, "repositoryRoles");
}
export function getUserAutoCompleteLink(state: Object): string { export function getUserAutoCompleteLink(state: Object): string {
const link = getLinkCollection(state, "autocomplete").find( const link = getLinkCollection(state, "autocomplete").find(
i => i.name === "users" i => i.name === "users"

View File

@@ -7,12 +7,15 @@ import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import javax.validation.constraints.NotNull;
@Getter @Getter
@Setter @Setter
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
public class PermissionListDto extends HalRepresentation { public class PermissionListDto extends HalRepresentation {
@NotNull
private String[] permissions; private String[] permissions;
@Override @Override

View File

@@ -8,6 +8,7 @@ import sonia.scm.security.PermissionDescriptor;
import sonia.scm.web.VndMediaType; import sonia.scm.web.VndMediaType;
import javax.inject.Inject; import javax.inject.Inject;
import javax.validation.Valid;
import javax.ws.rs.Consumes; import javax.ws.rs.Consumes;
import javax.ws.rs.GET; import javax.ws.rs.GET;
import javax.ws.rs.PUT; import javax.ws.rs.PUT;
@@ -69,7 +70,7 @@ public class UserPermissionResource {
@ResponseCode(code = 500, condition = "internal server error") @ResponseCode(code = 500, condition = "internal server error")
}) })
@TypeHint(TypeHint.NO_CONTENT.class) @TypeHint(TypeHint.NO_CONTENT.class)
public Response overwritePermissions(@PathParam("id") String id, PermissionListDto newPermissions) { public Response overwritePermissions(@PathParam("id") String id, @Valid PermissionListDto newPermissions) {
Collection<PermissionDescriptor> permissionDescriptors = Arrays.stream(newPermissions.getPermissions()) Collection<PermissionDescriptor> permissionDescriptors = Arrays.stream(newPermissions.getPermissions())
.map(PermissionDescriptor::new) .map(PermissionDescriptor::new)
.collect(Collectors.toList()); .collect(Collectors.toList());

View File

@@ -50,6 +50,7 @@ import sonia.scm.plugin.DefaultPluginLoader;
import sonia.scm.plugin.Plugin; import sonia.scm.plugin.Plugin;
import sonia.scm.plugin.PluginException; import sonia.scm.plugin.PluginException;
import sonia.scm.plugin.PluginLoadException; import sonia.scm.plugin.PluginLoadException;
import sonia.scm.plugin.PluginLoader;
import sonia.scm.plugin.PluginWrapper; import sonia.scm.plugin.PluginWrapper;
import sonia.scm.plugin.PluginsInternal; import sonia.scm.plugin.PluginsInternal;
import sonia.scm.plugin.SmpArchive; import sonia.scm.plugin.SmpArchive;
@@ -137,6 +138,19 @@ public class BootstrapContextListener implements ServletContextListener {
File pluginDirectory = getPluginDirectory(); File pluginDirectory = getPluginDirectory();
createContextListener(pluginDirectory);
contextListener.contextInitialized(sce);
// register for restart events
if (!registered && (SCMContext.getContext().getStage() == Stage.DEVELOPMENT)) {
logger.info("register for restart events");
ScmEventBus.getInstance().register(this);
registered = true;
}
}
private void createContextListener(File pluginDirectory) {
try { try {
if (!isCorePluginExtractionDisabled()) { if (!isCorePluginExtractionDisabled()) {
extractCorePlugins(context, pluginDirectory); extractCorePlugins(context, pluginDirectory);
@@ -148,22 +162,9 @@ public class BootstrapContextListener implements ServletContextListener {
Set<PluginWrapper> plugins = PluginsInternal.collectPlugins(cl, pluginDirectory.toPath()); Set<PluginWrapper> plugins = PluginsInternal.collectPlugins(cl, pluginDirectory.toPath());
DefaultPluginLoader pluginLoader = new DefaultPluginLoader(context, cl, plugins); PluginLoader pluginLoader = new DefaultPluginLoader(context, cl, plugins);
Module scmContextListenerModule = new ScmContextListenerModule(); Injector bootstrapInjector = createBootstrapInjector(pluginLoader);
BootstrapModule bootstrapModule = new BootstrapModule(pluginLoader);
ScmInitializerModule scmInitializerModule = new ScmInitializerModule();
EagerSingletonModule eagerSingletonModule = new EagerSingletonModule();
ScmEventBusModule scmEventBusModule = new ScmEventBusModule();
Injector bootstrapInjector =
Guice.createInjector(
bootstrapModule,
scmContextListenerModule,
scmEventBusModule,
scmInitializerModule,
eagerSingletonModule
);
processUpdates(pluginLoader, bootstrapInjector); processUpdates(pluginLoader, bootstrapInjector);
@@ -171,19 +172,25 @@ public class BootstrapContextListener implements ServletContextListener {
} catch (IOException ex) { } catch (IOException ex) {
throw new PluginLoadException("could not load plugins", ex); throw new PluginLoadException("could not load plugins", ex);
} }
contextListener.contextInitialized(sce);
// register for restart events
if (!registered
&& (SCMContext.getContext().getStage() == Stage.DEVELOPMENT)) {
logger.info("register for restart events");
ScmEventBus.getInstance().register(this);
registered = true;
}
} }
private void processUpdates(DefaultPluginLoader pluginLoader, Injector bootstrapInjector) { private Injector createBootstrapInjector(PluginLoader pluginLoader) {
Module scmContextListenerModule = new ScmContextListenerModule();
BootstrapModule bootstrapModule = new BootstrapModule(pluginLoader);
ScmInitializerModule scmInitializerModule = new ScmInitializerModule();
EagerSingletonModule eagerSingletonModule = new EagerSingletonModule();
ScmEventBusModule scmEventBusModule = new ScmEventBusModule();
return Guice.createInjector(
bootstrapModule,
scmContextListenerModule,
scmEventBusModule,
scmInitializerModule,
eagerSingletonModule
);
}
private void processUpdates(PluginLoader pluginLoader, Injector bootstrapInjector) {
Injector updateInjector = bootstrapInjector.createChildInjector(new UpdateStepModule(pluginLoader)); Injector updateInjector = bootstrapInjector.createChildInjector(new UpdateStepModule(pluginLoader));
UpdateEngine updateEngine = updateInjector.getInstance(UpdateEngine.class); UpdateEngine updateEngine = updateInjector.getInstance(UpdateEngine.class);
@@ -403,7 +410,6 @@ public class BootstrapContextListener implements ServletContextListener {
private static class ScmContextListenerModule extends AbstractModule { private static class ScmContextListenerModule extends AbstractModule {
@Override @Override
protected void configure() { protected void configure() {
install(new FactoryModuleBuilder().build(ScmContextListener.Factory.class)); install(new FactoryModuleBuilder().build(ScmContextListener.Factory.class));
} }
} }

View File

@@ -9,7 +9,6 @@ import sonia.scm.SCMContext;
import sonia.scm.SCMContextProvider; import sonia.scm.SCMContextProvider;
import sonia.scm.io.DefaultFileSystem; import sonia.scm.io.DefaultFileSystem;
import sonia.scm.io.FileSystem; import sonia.scm.io.FileSystem;
import sonia.scm.plugin.DefaultPluginLoader;
import sonia.scm.plugin.PluginLoader; import sonia.scm.plugin.PluginLoader;
import sonia.scm.repository.RepositoryLocationResolver; import sonia.scm.repository.RepositoryLocationResolver;
import sonia.scm.repository.xml.PathBasedRepositoryLocationResolver; import sonia.scm.repository.xml.PathBasedRepositoryLocationResolver;
@@ -33,7 +32,7 @@ public class BootstrapModule extends AbstractModule {
private final ClassOverrides overrides; private final ClassOverrides overrides;
private final PluginLoader pluginLoader; private final PluginLoader pluginLoader;
BootstrapModule(DefaultPluginLoader pluginLoader) { BootstrapModule(PluginLoader pluginLoader) {
this.overrides = ClassOverrides.findOverrides(pluginLoader.getUberClassLoader()); this.overrides = ClassOverrides.findOverrides(pluginLoader.getUberClassLoader());
this.pluginLoader = pluginLoader; this.pluginLoader = pluginLoader;
} }