mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-07 22:15:45 +01:00
Merged in feature/git-config (pull request #103)
Repository type configuration + abstraction for global configuration
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@scm-manager/scm-git-plugin",
|
"name": "@scm-manager/scm-git-plugin",
|
||||||
"license" : "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"main": "src/main/js/index.js",
|
"main": "src/main/js/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "ui-bundler plugin",
|
"build": "ui-bundler plugin",
|
||||||
@@ -9,9 +9,9 @@
|
|||||||
"flow": "flow check"
|
"flow": "flow check"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@scm-manager/ui-extensions": "^0.0.7"
|
"@scm-manager/ui-extensions": "^0.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@scm-manager/ui-bundler": "^0.0.19"
|
"@scm-manager/ui-bundler": "^0.0.21"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,17 +116,17 @@ public class GitRepositoryHandler
|
|||||||
public void init(SCMContextProvider context)
|
public void init(SCMContextProvider context)
|
||||||
{
|
{
|
||||||
super.init(context);
|
super.init(context);
|
||||||
scheduleGc();
|
scheduleGc(getConfig().getGcExpression());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setConfig(GitConfig config)
|
public void setConfig(GitConfig config)
|
||||||
{
|
{
|
||||||
|
scheduleGc(config.getGcExpression());
|
||||||
super.setConfig(config);
|
super.setConfig(config);
|
||||||
scheduleGc();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scheduleGc()
|
private void scheduleGc(String expression)
|
||||||
{
|
{
|
||||||
synchronized (LOCK){
|
synchronized (LOCK){
|
||||||
if ( task != null ){
|
if ( task != null ){
|
||||||
@@ -134,11 +134,10 @@ public class GitRepositoryHandler
|
|||||||
task.cancel();
|
task.cancel();
|
||||||
task = null;
|
task = null;
|
||||||
}
|
}
|
||||||
String exp = getConfig().getGcExpression();
|
if (!Strings.isNullOrEmpty(expression))
|
||||||
if (!Strings.isNullOrEmpty(exp))
|
|
||||||
{
|
{
|
||||||
logger.info("schedule git gc task with expression {}", exp);
|
logger.info("schedule git gc task with expression {}", expression);
|
||||||
task = scheduler.schedule(exp, GitGcTask.class);
|
task = scheduler.schedule(expression, GitGcTask.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
//@flow
|
||||||
|
import React from "react";
|
||||||
|
import { translate } from "react-i18next";
|
||||||
|
import type { Links } from "@scm-manager/ui-types";
|
||||||
|
|
||||||
|
import { InputField, Checkbox } from "@scm-manager/ui-components";
|
||||||
|
|
||||||
|
type Configuration = {
|
||||||
|
repositoryDirectory?: string,
|
||||||
|
gcExpression?: string,
|
||||||
|
disabled: boolean,
|
||||||
|
_links: Links
|
||||||
|
}
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
initialConfiguration: Configuration,
|
||||||
|
readOnly: boolean,
|
||||||
|
|
||||||
|
onConfigurationChange: (Configuration, boolean) => void,
|
||||||
|
|
||||||
|
// context props
|
||||||
|
t: (string) => string
|
||||||
|
}
|
||||||
|
|
||||||
|
type State = Configuration & {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class GitConfigurationForm extends React.Component<Props, State> {
|
||||||
|
|
||||||
|
constructor(props: Props) {
|
||||||
|
super(props);
|
||||||
|
this.state = { ...props.initialConfiguration };
|
||||||
|
}
|
||||||
|
|
||||||
|
isValid = () => {
|
||||||
|
return !!this.state.repositoryDirectory;
|
||||||
|
};
|
||||||
|
|
||||||
|
handleChange = (value: any, name: string) => {
|
||||||
|
this.setState({
|
||||||
|
[name]: value
|
||||||
|
}, () => this.props.onConfigurationChange(this.state, this.isValid()));
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { repositoryDirectory, gcExpression, disabled } = this.state;
|
||||||
|
const { readOnly, t } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<InputField name="repositoryDirectory"
|
||||||
|
label={t("scm-git-plugin.config.directory")}
|
||||||
|
helpText={t("scm-git-plugin.config.directoryHelpText")}
|
||||||
|
value={repositoryDirectory}
|
||||||
|
onChange={this.handleChange}
|
||||||
|
disabled={readOnly}
|
||||||
|
/>
|
||||||
|
<InputField name="gcExpression"
|
||||||
|
label={t("scm-git-plugin.config.gcExpression")}
|
||||||
|
helpText={t("scm-git-plugin.config.gcExpressionHelpText")}
|
||||||
|
value={gcExpression}
|
||||||
|
onChange={this.handleChange}
|
||||||
|
disabled={readOnly}
|
||||||
|
/>
|
||||||
|
<Checkbox name="disabled"
|
||||||
|
label={t("scm-git-plugin.config.disabled")}
|
||||||
|
helpText={t("scm-git-plugin.config.disabledHelpText")}
|
||||||
|
checked={disabled}
|
||||||
|
onChange={this.handleChange}
|
||||||
|
disabled={readOnly}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default translate("plugins")(GitConfigurationForm);
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
//@flow
|
||||||
|
import React from "react";
|
||||||
|
import { translate } from "react-i18next";
|
||||||
|
import { Title, GlobalConfiguration } from "@scm-manager/ui-components";
|
||||||
|
import GitConfigurationForm from "./GitConfigurationForm";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
link: string,
|
||||||
|
|
||||||
|
t: (string) => string
|
||||||
|
};
|
||||||
|
|
||||||
|
class GitGlobalConfiguration extends React.Component<Props> {
|
||||||
|
|
||||||
|
constructor(props: Props) {
|
||||||
|
super(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { link, t } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Title title={t("scm-git-plugin.config.title")}/>
|
||||||
|
<GlobalConfiguration link={link} render={props => <GitConfigurationForm {...props} />}/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default translate("plugins")(GitGlobalConfiguration);
|
||||||
@@ -3,9 +3,18 @@ import { binder } from "@scm-manager/ui-extensions";
|
|||||||
import ProtocolInformation from "./ProtocolInformation";
|
import ProtocolInformation from "./ProtocolInformation";
|
||||||
import GitAvatar from "./GitAvatar";
|
import GitAvatar from "./GitAvatar";
|
||||||
|
|
||||||
|
import { ConfigurationBinder as cfgBinder } from "@scm-manager/ui-components";
|
||||||
|
import GitGlobalConfiguration from "./GitGlobalConfiguration";
|
||||||
|
|
||||||
|
// repository
|
||||||
|
|
||||||
const gitPredicate = (props: Object) => {
|
const gitPredicate = (props: Object) => {
|
||||||
return props.repository && props.repository.type === "git";
|
return props.repository && props.repository.type === "git";
|
||||||
};
|
};
|
||||||
|
|
||||||
binder.bind("repos.repository-details.information", ProtocolInformation, gitPredicate);
|
binder.bind("repos.repository-details.information", ProtocolInformation, gitPredicate);
|
||||||
binder.bind("repos.repository-avatar", GitAvatar, gitPredicate);
|
binder.bind("repos.repository-avatar", GitAvatar, gitPredicate);
|
||||||
|
|
||||||
|
// global config
|
||||||
|
|
||||||
|
cfgBinder.bindGlobal("/git", "scm-git-plugin.config.link", "gitConfig", GitGlobalConfiguration);
|
||||||
|
|||||||
@@ -4,6 +4,17 @@
|
|||||||
"clone" : "Clone the repository",
|
"clone" : "Clone the repository",
|
||||||
"create" : "Create a new repository",
|
"create" : "Create a new repository",
|
||||||
"replace" : "Push an existing repository"
|
"replace" : "Push an existing repository"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"link": "Git",
|
||||||
|
"title": "Git Configuration",
|
||||||
|
"directory": "Repository Directory",
|
||||||
|
"directoryHelpText": "Location of the Git repositories.",
|
||||||
|
"gcExpression": "GC Cron Expression",
|
||||||
|
"gcExpressionHelpText": "Use Quartz Cron Expressions (SECOND MINUTE HOUR DAYOFMONTH MONTH DAYOFWEEK) to run git gc in intervals.",
|
||||||
|
"disabled": "Disabled",
|
||||||
|
"disabledHelpText": "Enable or disable the Git plugin",
|
||||||
|
"submit": "Submit"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -707,9 +707,9 @@
|
|||||||
version "0.0.2"
|
version "0.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85"
|
resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85"
|
||||||
|
|
||||||
"@scm-manager/ui-bundler@^0.0.19":
|
"@scm-manager/ui-bundler@^0.0.21":
|
||||||
version "0.0.19"
|
version "0.0.21"
|
||||||
resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.19.tgz#646ab1fa1e5389fad614542215c60678fb9816ae"
|
resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.21.tgz#f8b5fa355415cc67b8aaf8744e1701a299dff647"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/core" "^7.0.0"
|
"@babel/core" "^7.0.0"
|
||||||
"@babel/plugin-proposal-class-properties" "^7.0.0"
|
"@babel/plugin-proposal-class-properties" "^7.0.0"
|
||||||
@@ -747,9 +747,9 @@
|
|||||||
vinyl-source-stream "^2.0.0"
|
vinyl-source-stream "^2.0.0"
|
||||||
watchify "^3.11.0"
|
watchify "^3.11.0"
|
||||||
|
|
||||||
"@scm-manager/ui-extensions@^0.0.7":
|
"@scm-manager/ui-extensions@^0.1.1":
|
||||||
version "0.0.7"
|
version "0.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/@scm-manager/ui-extensions/-/ui-extensions-0.0.7.tgz#a0a657a1410b78838ba0b36096ef631dca7fe27e"
|
resolved "https://registry.yarnpkg.com/@scm-manager/ui-extensions/-/ui-extensions-0.1.1.tgz#966e62d89981e92a14adf7e674e646e76de96d45"
|
||||||
dependencies:
|
dependencies:
|
||||||
react "^16.4.2"
|
react "^16.4.2"
|
||||||
react-dom "^16.4.2"
|
react-dom "^16.4.2"
|
||||||
|
|||||||
@@ -6,9 +6,9 @@
|
|||||||
"build": "ui-bundler plugin"
|
"build": "ui-bundler plugin"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@scm-manager/ui-extensions": "^0.0.7"
|
"@scm-manager/ui-extensions": "^0.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@scm-manager/ui-bundler": "^0.0.19"
|
"@scm-manager/ui-bundler": "^0.0.21"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
113
scm-plugins/scm-hg-plugin/src/main/js/HgConfigurationForm.js
Normal file
113
scm-plugins/scm-hg-plugin/src/main/js/HgConfigurationForm.js
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
//@flow
|
||||||
|
import React from "react";
|
||||||
|
import type { Links } from "@scm-manager/ui-types";
|
||||||
|
import { translate } from "react-i18next";
|
||||||
|
import { InputField, Checkbox } from "@scm-manager/ui-components";
|
||||||
|
|
||||||
|
type Configuration = {
|
||||||
|
"hgBinary": string,
|
||||||
|
"pythonBinary": string,
|
||||||
|
"pythonPath"?: string,
|
||||||
|
"repositoryDirectory": string,
|
||||||
|
"encoding": string,
|
||||||
|
"useOptimizedBytecode": boolean,
|
||||||
|
"showRevisionInId": boolean,
|
||||||
|
"disabled": boolean,
|
||||||
|
"_links": Links
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
initialConfiguration: Configuration,
|
||||||
|
readOnly: boolean,
|
||||||
|
|
||||||
|
onConfigurationChange: (Configuration, boolean) => void,
|
||||||
|
|
||||||
|
// context props
|
||||||
|
t: (string) => string
|
||||||
|
}
|
||||||
|
|
||||||
|
type State = Configuration & {
|
||||||
|
validationErrors: string[]
|
||||||
|
};
|
||||||
|
|
||||||
|
class HgConfigurationForm extends React.Component<Props, State> {
|
||||||
|
|
||||||
|
constructor(props: Props) {
|
||||||
|
super(props);
|
||||||
|
this.state = { ...props.initialConfiguration, validationErrors: [] };
|
||||||
|
}
|
||||||
|
|
||||||
|
updateValidationStatus = () => {
|
||||||
|
const requiredFields = [
|
||||||
|
"hgBinary", "pythonBinary", "repositoryDirectory", "encoding"
|
||||||
|
];
|
||||||
|
|
||||||
|
const validationErrors = [];
|
||||||
|
for (let field of requiredFields) {
|
||||||
|
if (!this.state[field]) {
|
||||||
|
validationErrors.push( field );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
validationErrors
|
||||||
|
});
|
||||||
|
|
||||||
|
return validationErrors.length === 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
hasValidationError = (name: string) => {
|
||||||
|
return this.state.validationErrors.indexOf(name) >= 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
handleChange = (value: any, name: string) => {
|
||||||
|
this.setState({
|
||||||
|
[name]: value
|
||||||
|
}, () => this.props.onConfigurationChange(this.state, this.updateValidationStatus()));
|
||||||
|
};
|
||||||
|
|
||||||
|
inputField = (name: string) => {
|
||||||
|
const { readOnly, t } = this.props;
|
||||||
|
return <InputField
|
||||||
|
name={ name }
|
||||||
|
label={t("scm-hg-plugin.config." + name)}
|
||||||
|
helpText={t("scm-hg-plugin.config." + name + "HelpText")}
|
||||||
|
value={this.state[name]}
|
||||||
|
onChange={this.handleChange}
|
||||||
|
validationError={this.hasValidationError(name)}
|
||||||
|
errorMessage={t("scm-hg-plugin.config.required")}
|
||||||
|
disabled={readOnly}
|
||||||
|
/>;
|
||||||
|
};
|
||||||
|
|
||||||
|
checkbox = (name: string) => {
|
||||||
|
const { readOnly, t } = this.props;
|
||||||
|
return <Checkbox
|
||||||
|
name={ name }
|
||||||
|
label={t("scm-hg-plugin.config." + name)}
|
||||||
|
helpText={t("scm-hg-plugin.config." + name + "HelpText")}
|
||||||
|
checked={this.state[name]}
|
||||||
|
onChange={this.handleChange}
|
||||||
|
disabled={readOnly}
|
||||||
|
/>;
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{this.inputField("hgBinary")}
|
||||||
|
{this.inputField("pythonBinary")}
|
||||||
|
{this.inputField("pythonPath")}
|
||||||
|
{this.inputField("repositoryDirectory")}
|
||||||
|
{this.inputField("encoding")}
|
||||||
|
{this.checkbox("useOptimizedBytecode")}
|
||||||
|
{this.checkbox("showRevisionInId")}
|
||||||
|
{this.checkbox("disabled")}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default translate("plugins")(HgConfigurationForm);
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
//@flow
|
||||||
|
import React from "react";
|
||||||
|
import { Title, GlobalConfiguration } from "@scm-manager/ui-components";
|
||||||
|
import { translate } from "react-i18next";
|
||||||
|
import HgConfigurationForm from "./HgConfigurationForm";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
link: string,
|
||||||
|
|
||||||
|
// context props
|
||||||
|
t: (string) => string
|
||||||
|
}
|
||||||
|
|
||||||
|
class HgGlobalConfiguration extends React.Component<Props> {
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { link, t } = this.props;
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Title title={t("scm-hg-plugin.config.title")}/>
|
||||||
|
<GlobalConfiguration link={link} render={props => <HgConfigurationForm {...props} />}/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default translate("plugins")(HgGlobalConfiguration);
|
||||||
@@ -2,6 +2,8 @@
|
|||||||
import { binder } from "@scm-manager/ui-extensions";
|
import { binder } from "@scm-manager/ui-extensions";
|
||||||
import ProtocolInformation from "./ProtocolInformation";
|
import ProtocolInformation from "./ProtocolInformation";
|
||||||
import HgAvatar from "./HgAvatar";
|
import HgAvatar from "./HgAvatar";
|
||||||
|
import { ConfigurationBinder as cfgBinder } from "@scm-manager/ui-components";
|
||||||
|
import HgGlobalConfiguration from "./HgGlobalConfiguration";
|
||||||
|
|
||||||
const hgPredicate = (props: Object) => {
|
const hgPredicate = (props: Object) => {
|
||||||
return props.repository && props.repository.type === "hg";
|
return props.repository && props.repository.type === "hg";
|
||||||
@@ -9,3 +11,7 @@ const hgPredicate = (props: Object) => {
|
|||||||
|
|
||||||
binder.bind("repos.repository-details.information", ProtocolInformation, hgPredicate);
|
binder.bind("repos.repository-details.information", ProtocolInformation, hgPredicate);
|
||||||
binder.bind("repos.repository-avatar", HgAvatar, hgPredicate);
|
binder.bind("repos.repository-avatar", HgAvatar, hgPredicate);
|
||||||
|
|
||||||
|
// bind global configuration
|
||||||
|
|
||||||
|
cfgBinder.bindGlobal("/hg", "scm-hg-plugin.config.link", "hgConfig", HgGlobalConfiguration);
|
||||||
|
|||||||
@@ -4,6 +4,27 @@
|
|||||||
"clone" : "Clone the repository",
|
"clone" : "Clone the repository",
|
||||||
"create" : "Create a new repository",
|
"create" : "Create a new repository",
|
||||||
"replace" : "Push an existing repository"
|
"replace" : "Push an existing repository"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"link": "Mercurial",
|
||||||
|
"title": "Mercurial Configuration",
|
||||||
|
"hgBinary": "HG Binary",
|
||||||
|
"hgBinaryHelpText": "Location of Mercurial binary.",
|
||||||
|
"pythonBinary": "Python Binary",
|
||||||
|
"pythonBinaryHelpText": "Location of Python binary.",
|
||||||
|
"pythonPath": "Python Module Search Path",
|
||||||
|
"pythonPathHelpText": "Python Module Search Path (PYTHONPATH).",
|
||||||
|
"repositoryDirectory": "Repository directory",
|
||||||
|
"repositoryDirectoryHelpText": "Location of Mercurial repositories.",
|
||||||
|
"encoding": "Encoding",
|
||||||
|
"encodingHelpText": "Repository Encoding.",
|
||||||
|
"useOptimizedBytecode": "Optimized Bytecode (.pyo)",
|
||||||
|
"useOptimizedBytecodeHelpText": "Use the Python '-O' switch.",
|
||||||
|
"showRevisionInId": "Show Revision",
|
||||||
|
"showRevisionInIdHelpText": "Show revision as part of the node id.",
|
||||||
|
"disabled": "Disabled",
|
||||||
|
"disabledHelpText": "Enable or disable the Mercurial plugin.",
|
||||||
|
"required": "This configuration value is required"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -641,9 +641,9 @@
|
|||||||
version "0.0.2"
|
version "0.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85"
|
resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85"
|
||||||
|
|
||||||
"@scm-manager/ui-bundler@^0.0.19":
|
"@scm-manager/ui-bundler@^0.0.21":
|
||||||
version "0.0.19"
|
version "0.0.21"
|
||||||
resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.19.tgz#646ab1fa1e5389fad614542215c60678fb9816ae"
|
resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.21.tgz#f8b5fa355415cc67b8aaf8744e1701a299dff647"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/core" "^7.0.0"
|
"@babel/core" "^7.0.0"
|
||||||
"@babel/plugin-proposal-class-properties" "^7.0.0"
|
"@babel/plugin-proposal-class-properties" "^7.0.0"
|
||||||
@@ -660,7 +660,6 @@
|
|||||||
browserify-css "^0.14.0"
|
browserify-css "^0.14.0"
|
||||||
colors "^1.3.1"
|
colors "^1.3.1"
|
||||||
commander "^2.17.1"
|
commander "^2.17.1"
|
||||||
connect-history-api-fallback "^1.5.0"
|
|
||||||
eslint "^5.4.0"
|
eslint "^5.4.0"
|
||||||
eslint-config-react-app "^2.1.0"
|
eslint-config-react-app "^2.1.0"
|
||||||
eslint-plugin-flowtype "^2.50.0"
|
eslint-plugin-flowtype "^2.50.0"
|
||||||
@@ -682,9 +681,9 @@
|
|||||||
vinyl-source-stream "^2.0.0"
|
vinyl-source-stream "^2.0.0"
|
||||||
watchify "^3.11.0"
|
watchify "^3.11.0"
|
||||||
|
|
||||||
"@scm-manager/ui-extensions@^0.0.7":
|
"@scm-manager/ui-extensions@^0.1.1":
|
||||||
version "0.0.7"
|
version "0.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/@scm-manager/ui-extensions/-/ui-extensions-0.0.7.tgz#a0a657a1410b78838ba0b36096ef631dca7fe27e"
|
resolved "https://registry.yarnpkg.com/@scm-manager/ui-extensions/-/ui-extensions-0.1.1.tgz#966e62d89981e92a14adf7e674e646e76de96d45"
|
||||||
dependencies:
|
dependencies:
|
||||||
react "^16.4.2"
|
react "^16.4.2"
|
||||||
react-dom "^16.4.2"
|
react-dom "^16.4.2"
|
||||||
|
|||||||
@@ -6,9 +6,9 @@
|
|||||||
"build": "ui-bundler plugin"
|
"build": "ui-bundler plugin"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@scm-manager/ui-extensions": "^0.0.7"
|
"@scm-manager/ui-extensions": "^0.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@scm-manager/ui-bundler": "^0.0.19"
|
"@scm-manager/ui-bundler": "^0.0.21"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
107
scm-plugins/scm-svn-plugin/src/main/js/SvnConfigurationForm.js
Normal file
107
scm-plugins/scm-svn-plugin/src/main/js/SvnConfigurationForm.js
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
//@flow
|
||||||
|
import React from "react";
|
||||||
|
import type { Links } from "@scm-manager/ui-types";
|
||||||
|
import { translate } from "react-i18next";
|
||||||
|
import { InputField, Checkbox, Select } from "@scm-manager/ui-components";
|
||||||
|
|
||||||
|
type Configuration = {
|
||||||
|
repositoryDirectory: string,
|
||||||
|
compatibility: string,
|
||||||
|
enabledGZip: boolean,
|
||||||
|
disabled: boolean,
|
||||||
|
_links: Links
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
initialConfiguration: Configuration,
|
||||||
|
readOnly: boolean,
|
||||||
|
|
||||||
|
onConfigurationChange: (Configuration, boolean) => void,
|
||||||
|
|
||||||
|
// context props
|
||||||
|
t: (string) => string
|
||||||
|
}
|
||||||
|
|
||||||
|
type State = Configuration;
|
||||||
|
|
||||||
|
class HgConfigurationForm extends React.Component<Props, State> {
|
||||||
|
|
||||||
|
constructor(props: Props) {
|
||||||
|
super(props);
|
||||||
|
this.state = { ...props.initialConfiguration, validationErrors: [] };
|
||||||
|
}
|
||||||
|
|
||||||
|
isValid = () => {
|
||||||
|
return !!this.state.repositoryDirectory;
|
||||||
|
};
|
||||||
|
|
||||||
|
handleChange = (value: any, name: string) => {
|
||||||
|
this.setState({
|
||||||
|
[name]: value
|
||||||
|
}, () => this.props.onConfigurationChange(this.state, this.isValid()));
|
||||||
|
};
|
||||||
|
|
||||||
|
compatibilityOptions = (values: string[]) => {
|
||||||
|
const options = [];
|
||||||
|
for (let value of values) {
|
||||||
|
options.push( this.compatibilityOption(value) );
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
};
|
||||||
|
|
||||||
|
compatibilityOption = (value: string) => {
|
||||||
|
return {
|
||||||
|
value,
|
||||||
|
label: this.props.t("scm-svn-plugin.config.compatibility-values." + value.toLowerCase())
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { readOnly, t } = this.props;
|
||||||
|
const compatibilityOptions = this.compatibilityOptions([
|
||||||
|
"NONE", "PRE14", "PRE15", "PRE16", "PRE17", "WITH17"
|
||||||
|
]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<InputField
|
||||||
|
name="repositoryDirectory"
|
||||||
|
label={t("scm-svn-plugin.config.directory")}
|
||||||
|
helpText={t("scm-svn-plugin.config.directoryHelpText")}
|
||||||
|
value={this.state.repositoryDirectory}
|
||||||
|
errorMessage={t("scm-svn-plugin.config.required")}
|
||||||
|
validationError={!this.state.repositoryDirectory}
|
||||||
|
onChange={this.handleChange}
|
||||||
|
disabled={readOnly}
|
||||||
|
/>
|
||||||
|
<Select
|
||||||
|
name="compatibility"
|
||||||
|
label={t("scm-svn-plugin.config.compatibility")}
|
||||||
|
helpText={t("scm-svn-plugin.config.compatibilityHelpText")}
|
||||||
|
value={this.state.compatibility}
|
||||||
|
options={compatibilityOptions}
|
||||||
|
onChange={this.handleChange}
|
||||||
|
/>
|
||||||
|
<Checkbox
|
||||||
|
name="enabledGZip"
|
||||||
|
label={t("scm-svn-plugin.config.enabledGZip")}
|
||||||
|
helpText={t("scm-svn-plugin.config.enabledGZipHelpText")}
|
||||||
|
checked={this.state.enabledGZip}
|
||||||
|
onChange={this.handleChange}
|
||||||
|
disabled={readOnly}
|
||||||
|
/>
|
||||||
|
<Checkbox
|
||||||
|
name="disabled"
|
||||||
|
label={t("scm-svn-plugin.config.disabled")}
|
||||||
|
helpText={t("scm-svn-plugin.config.disabledHelpText")}
|
||||||
|
checked={this.state.disabled}
|
||||||
|
onChange={this.handleChange}
|
||||||
|
disabled={readOnly}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default translate("plugins")(HgConfigurationForm);
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
//@flow
|
||||||
|
import React from "react";
|
||||||
|
import { translate } from "react-i18next";
|
||||||
|
import { Title, GlobalConfiguration } from "@scm-manager/ui-components";
|
||||||
|
import SvnConfigurationForm from "./SvnConfigurationForm";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
link: string,
|
||||||
|
|
||||||
|
// context props
|
||||||
|
t: (string) => string
|
||||||
|
}
|
||||||
|
|
||||||
|
class SvnGlobalConfiguration extends React.Component<Props> {
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { link, t } = this.props;
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Title title={t("scm-svn-plugin.config.title")}/>
|
||||||
|
<GlobalConfiguration link={link} render={props => <SvnConfigurationForm {...props} />}/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default translate("plugins")(SvnGlobalConfiguration);
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import { binder } from "@scm-manager/ui-extensions";
|
import { binder } from "@scm-manager/ui-extensions";
|
||||||
|
import { ConfigurationBinder as cfgBinder } from "@scm-manager/ui-components";
|
||||||
import ProtocolInformation from "./ProtocolInformation";
|
import ProtocolInformation from "./ProtocolInformation";
|
||||||
import SvnAvatar from "./SvnAvatar";
|
import SvnAvatar from "./SvnAvatar";
|
||||||
|
import SvnGlobalConfiguration from "./SvnGlobalConfiguration";
|
||||||
|
|
||||||
const svnPredicate = (props: Object) => {
|
const svnPredicate = (props: Object) => {
|
||||||
return props.repository && props.repository.type === "svn";
|
return props.repository && props.repository.type === "svn";
|
||||||
@@ -9,3 +11,7 @@ const svnPredicate = (props: Object) => {
|
|||||||
|
|
||||||
binder.bind("repos.repository-details.information", ProtocolInformation, svnPredicate);
|
binder.bind("repos.repository-details.information", ProtocolInformation, svnPredicate);
|
||||||
binder.bind("repos.repository-avatar", SvnAvatar, svnPredicate);
|
binder.bind("repos.repository-avatar", SvnAvatar, svnPredicate);
|
||||||
|
|
||||||
|
// bind global configuration
|
||||||
|
|
||||||
|
cfgBinder.bindGlobal("/svn", "scm-svn-plugin.config.link", "svnConfig", SvnGlobalConfiguration);
|
||||||
|
|||||||
@@ -2,6 +2,27 @@
|
|||||||
"scm-svn-plugin": {
|
"scm-svn-plugin": {
|
||||||
"information": {
|
"information": {
|
||||||
"checkout" : "Checkout repository"
|
"checkout" : "Checkout repository"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"link": "Subversion",
|
||||||
|
"title": "Subversion Configuration",
|
||||||
|
"directory": "Repository Directory",
|
||||||
|
"directoryHelpText": "Location of Subversion repositories.",
|
||||||
|
"compatibility": "Version Compatibility",
|
||||||
|
"compatibilityHelpText": "Specifies with which subversion version repositories are compatible.",
|
||||||
|
"compatibility-values": {
|
||||||
|
"none": "No compatibility",
|
||||||
|
"pre14": "Pre 1.4 Compatible",
|
||||||
|
"pre15": "Pre 1.5 Compatible",
|
||||||
|
"pre16": "Pre 1.6 Compatible",
|
||||||
|
"pre17": "Pre 1.7 Compatible",
|
||||||
|
"with17": "With 1.7 Compatible"
|
||||||
|
},
|
||||||
|
"enabledGZip": "Enable GZip Compression",
|
||||||
|
"enabledGZipHelpText": "Enable GZip compression for svn responses.",
|
||||||
|
"disabled": "Disabled",
|
||||||
|
"disabledHelpText": "Enable or disable the Git plugin",
|
||||||
|
"required": "This configuration value is required"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -641,9 +641,9 @@
|
|||||||
version "0.0.2"
|
version "0.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85"
|
resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85"
|
||||||
|
|
||||||
"@scm-manager/ui-bundler@^0.0.19":
|
"@scm-manager/ui-bundler@^0.0.21":
|
||||||
version "0.0.19"
|
version "0.0.21"
|
||||||
resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.19.tgz#646ab1fa1e5389fad614542215c60678fb9816ae"
|
resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.21.tgz#f8b5fa355415cc67b8aaf8744e1701a299dff647"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/core" "^7.0.0"
|
"@babel/core" "^7.0.0"
|
||||||
"@babel/plugin-proposal-class-properties" "^7.0.0"
|
"@babel/plugin-proposal-class-properties" "^7.0.0"
|
||||||
@@ -681,9 +681,9 @@
|
|||||||
vinyl-source-stream "^2.0.0"
|
vinyl-source-stream "^2.0.0"
|
||||||
watchify "^3.11.0"
|
watchify "^3.11.0"
|
||||||
|
|
||||||
"@scm-manager/ui-extensions@^0.0.7":
|
"@scm-manager/ui-extensions@^0.1.1":
|
||||||
version "0.0.7"
|
version "0.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/@scm-manager/ui-extensions/-/ui-extensions-0.0.7.tgz#a0a657a1410b78838ba0b36096ef631dca7fe27e"
|
resolved "https://registry.yarnpkg.com/@scm-manager/ui-extensions/-/ui-extensions-0.1.1.tgz#966e62d89981e92a14adf7e674e646e76de96d45"
|
||||||
dependencies:
|
dependencies:
|
||||||
react "^16.4.2"
|
react "^16.4.2"
|
||||||
react-dom "^16.4.2"
|
react-dom "^16.4.2"
|
||||||
|
|||||||
@@ -3,16 +3,18 @@
|
|||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"description": "UI Components for SCM-Manager and its plugins",
|
"description": "UI Components for SCM-Manager and its plugins",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"files": ["src"],
|
"files": [
|
||||||
|
"src"
|
||||||
|
],
|
||||||
"repository": "https://bitbucket.org/sdorra/scm-manager",
|
"repository": "https://bitbucket.org/sdorra/scm-manager",
|
||||||
"author": "Sebastian Sdorra <sebastian.sdorra@cloudogu.com>",
|
"author": "Sebastian Sdorra <sebastian.sdorra@cloudogu.com>",
|
||||||
"license" : "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"update-index": "create-index -r src",
|
"update-index": "create-index -r src",
|
||||||
"eslint-fix": "eslint src --fix"
|
"eslint-fix": "eslint src --fix"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@scm-manager/ui-bundler": "^0.0.19",
|
"@scm-manager/ui-bundler": "^0.0.21",
|
||||||
"create-index": "^2.3.0",
|
"create-index": "^2.3.0",
|
||||||
"enzyme": "^3.5.0",
|
"enzyme": "^3.5.0",
|
||||||
"enzyme-adapter-react-16": "^1.3.1",
|
"enzyme-adapter-react-16": "^1.3.1",
|
||||||
@@ -23,23 +25,32 @@
|
|||||||
"react-router-enzyme-context": "^1.2.0"
|
"react-router-enzyme-context": "^1.2.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@scm-manager/ui-extensions": "^0.1.1",
|
||||||
|
"@scm-manager/ui-types": "0.0.1",
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
"moment": "^2.22.2",
|
"moment": "^2.22.2",
|
||||||
"react": "^16.5.2",
|
"react": "^16.5.2",
|
||||||
"react-dom": "^16.5.2",
|
"react-dom": "^16.5.2",
|
||||||
"react-i18next": "^7.11.0",
|
"react-i18next": "^7.11.0",
|
||||||
"react-jss": "^8.6.1",
|
"react-jss": "^8.6.1",
|
||||||
"react-router-dom": "^4.3.1",
|
"react-router-dom": "^4.3.1"
|
||||||
"@scm-manager/ui-types": "0.0.1"
|
|
||||||
},
|
},
|
||||||
"browserify": {
|
"browserify": {
|
||||||
"transform": [
|
"transform": [
|
||||||
["browserify-css"],
|
[
|
||||||
|
"browserify-css"
|
||||||
|
],
|
||||||
[
|
[
|
||||||
"babelify",
|
"babelify",
|
||||||
{
|
{
|
||||||
"plugins": ["@babel/plugin-proposal-class-properties"],
|
"plugins": [
|
||||||
"presets": ["@babel/preset-env", "@babel/preset-flow", "@babel/preset-react"]
|
"@babel/plugin-proposal-class-properties"
|
||||||
|
],
|
||||||
|
"presets": [
|
||||||
|
"@babel/preset-env",
|
||||||
|
"@babel/preset-flow",
|
||||||
|
"@babel/preset-react"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
// @flow
|
||||||
|
import React from "react";
|
||||||
|
import { binder } from "@scm-manager/ui-extensions";
|
||||||
|
import { NavLink } from "../navigation";
|
||||||
|
import { Route } from "react-router-dom";
|
||||||
|
import { translate } from "react-i18next";
|
||||||
|
|
||||||
|
class ConfigurationBinder {
|
||||||
|
|
||||||
|
i18nNamespace: string = "plugins";
|
||||||
|
|
||||||
|
bindGlobal(to: string, labelI18nKey: string, linkName: string, ConfigurationComponent: any) {
|
||||||
|
|
||||||
|
// create predicate based on the link name of the index resource
|
||||||
|
// if the linkname is not available, the navigation link and the route are not bound to the extension points
|
||||||
|
const configPredicate = (props: Object) => {
|
||||||
|
return props.links && props.links[linkName];
|
||||||
|
};
|
||||||
|
|
||||||
|
// create NavigationLink with translated label
|
||||||
|
const ConfigNavLink = translate(this.i18nNamespace)(({t}) => {
|
||||||
|
return <NavLink to={"/config" + to} label={t(labelI18nKey)} />;
|
||||||
|
});
|
||||||
|
|
||||||
|
// bind navigation link to extension point
|
||||||
|
binder.bind("config.navigation", ConfigNavLink, configPredicate);
|
||||||
|
|
||||||
|
|
||||||
|
// route for global configuration, passes the link from the index resource to component
|
||||||
|
const ConfigRoute = ({ url, links }) => {
|
||||||
|
const link = links[linkName].href;
|
||||||
|
return <Route path={url + to}
|
||||||
|
render={() => <ConfigurationComponent link={link}/>}
|
||||||
|
exact/>;
|
||||||
|
};
|
||||||
|
|
||||||
|
// bind config route to extension point
|
||||||
|
binder.bind("config.route", ConfigRoute, configPredicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new ConfigurationBinder();
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
//@flow
|
||||||
|
import React from "react";
|
||||||
|
import { translate } from "react-i18next";
|
||||||
|
import type { Links } from "@scm-manager/ui-types";
|
||||||
|
import {
|
||||||
|
apiClient,
|
||||||
|
SubmitButton,
|
||||||
|
Loading,
|
||||||
|
ErrorNotification
|
||||||
|
} from "../";
|
||||||
|
|
||||||
|
type RenderProps = {
|
||||||
|
readOnly: boolean,
|
||||||
|
initialConfiguration: Configuration,
|
||||||
|
onConfigurationChange: (Configuration, boolean) => void
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
link: string,
|
||||||
|
render: (props: RenderProps) => any, // ???
|
||||||
|
|
||||||
|
// context props
|
||||||
|
t: (string) => string
|
||||||
|
};
|
||||||
|
|
||||||
|
type Configuration = {
|
||||||
|
_links: Links
|
||||||
|
} & Object;
|
||||||
|
|
||||||
|
type State = {
|
||||||
|
error?: Error,
|
||||||
|
fetching: boolean,
|
||||||
|
modifying: boolean,
|
||||||
|
contentType?: string,
|
||||||
|
|
||||||
|
configuration?: Configuration,
|
||||||
|
modifiedConfiguration?: Configuration,
|
||||||
|
valid: boolean
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GlobalConfiguration uses the render prop pattern to encapsulate the logic for
|
||||||
|
* synchronizing the configuration with the backend.
|
||||||
|
*/
|
||||||
|
class GlobalConfiguration extends React.Component<Props, State> {
|
||||||
|
|
||||||
|
constructor(props: Props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
fetching: true,
|
||||||
|
modifying: false,
|
||||||
|
valid: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const { link } = this.props;
|
||||||
|
|
||||||
|
apiClient.get(link)
|
||||||
|
.then(this.captureContentType)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(this.loadConfig)
|
||||||
|
.catch(this.handleError);
|
||||||
|
}
|
||||||
|
|
||||||
|
captureContentType = (response: Response) => {
|
||||||
|
const contentType = response.headers.get("Content-Type");
|
||||||
|
this.setState({
|
||||||
|
contentType
|
||||||
|
});
|
||||||
|
return response;
|
||||||
|
};
|
||||||
|
|
||||||
|
getContentType = (): string => {
|
||||||
|
const { contentType } = this.state;
|
||||||
|
return contentType ? contentType : "application/json";
|
||||||
|
};
|
||||||
|
|
||||||
|
handleError = (error: Error) => {
|
||||||
|
this.setState({
|
||||||
|
error,
|
||||||
|
fetching: false,
|
||||||
|
modifying: false
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
loadConfig = (configuration: Configuration) => {
|
||||||
|
this.setState({
|
||||||
|
configuration,
|
||||||
|
fetching: false,
|
||||||
|
error: undefined
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
getModificationUrl = (): ?string => {
|
||||||
|
const { configuration } = this.state;
|
||||||
|
if (configuration) {
|
||||||
|
const links = configuration._links;
|
||||||
|
if (links && links.update) {
|
||||||
|
return links.update.href;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
isReadOnly = (): boolean => {
|
||||||
|
const modificationUrl = this.getModificationUrl();
|
||||||
|
return !modificationUrl;
|
||||||
|
};
|
||||||
|
|
||||||
|
configurationChanged = (configuration: Configuration, valid: boolean) => {
|
||||||
|
this.setState({
|
||||||
|
modifiedConfiguration: configuration,
|
||||||
|
valid
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
modifyConfiguration = (event: Event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
this.setState({ modifying: true });
|
||||||
|
|
||||||
|
const {modifiedConfiguration} = this.state;
|
||||||
|
|
||||||
|
apiClient.put(this.getModificationUrl(), modifiedConfiguration, this.getContentType())
|
||||||
|
.then(() => this.setState({ modifying: false }))
|
||||||
|
.catch(this.handleError);
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { t } = this.props;
|
||||||
|
const { fetching, error, configuration, modifying, valid } = this.state;
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return <ErrorNotification error={error}/>;
|
||||||
|
} else if (fetching || !configuration) {
|
||||||
|
return <Loading />;
|
||||||
|
} else {
|
||||||
|
const readOnly = this.isReadOnly();
|
||||||
|
|
||||||
|
const renderProps: RenderProps = {
|
||||||
|
readOnly,
|
||||||
|
initialConfiguration: configuration,
|
||||||
|
onConfigurationChange: this.configurationChanged
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={this.modifyConfiguration}>
|
||||||
|
{ this.props.render(renderProps) }
|
||||||
|
<hr/>
|
||||||
|
<SubmitButton
|
||||||
|
label={t("config-form.submit")}
|
||||||
|
disabled={!valid || readOnly}
|
||||||
|
loading={modifying}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default translate("config")(GlobalConfiguration);
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
// @flow
|
||||||
|
export { default as ConfigurationBinder } from "./ConfigurationBinder";
|
||||||
|
export { default as GlobalConfiguration } from "./GlobalConfiguration";
|
||||||
@@ -4,15 +4,16 @@ import { Help } from "../index";
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
label?: string,
|
label?: string,
|
||||||
|
name?: string,
|
||||||
checked: boolean,
|
checked: boolean,
|
||||||
onChange?: boolean => void,
|
onChange?: (value: boolean, name?: string) => void,
|
||||||
disabled?: boolean,
|
disabled?: boolean,
|
||||||
helpText?: string
|
helpText?: string
|
||||||
};
|
};
|
||||||
class Checkbox extends React.Component<Props> {
|
class Checkbox extends React.Component<Props> {
|
||||||
onCheckboxChange = (event: SyntheticInputEvent<HTMLInputElement>) => {
|
onCheckboxChange = (event: SyntheticInputEvent<HTMLInputElement>) => {
|
||||||
if (this.props.onChange) {
|
if (this.props.onChange) {
|
||||||
this.props.onChange(event.target.checked);
|
this.props.onChange(event.target.checked, this.props.name);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -5,11 +5,12 @@ import { LabelWithHelpIcon } from "../index";
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
label?: string,
|
label?: string,
|
||||||
|
name?: string,
|
||||||
placeholder?: string,
|
placeholder?: string,
|
||||||
value?: string,
|
value?: string,
|
||||||
type?: string,
|
type?: string,
|
||||||
autofocus?: boolean,
|
autofocus?: boolean,
|
||||||
onChange: string => void,
|
onChange: (value: string, name?: string) => void,
|
||||||
onReturnPressed?: () => void,
|
onReturnPressed?: () => void,
|
||||||
validationError: boolean,
|
validationError: boolean,
|
||||||
errorMessage: string,
|
errorMessage: string,
|
||||||
@@ -32,7 +33,7 @@ class InputField extends React.Component<Props> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleInput = (event: SyntheticInputEvent<HTMLInputElement>) => {
|
handleInput = (event: SyntheticInputEvent<HTMLInputElement>) => {
|
||||||
this.props.onChange(event.target.value);
|
this.props.onChange(event.target.value, this.props.name);
|
||||||
};
|
};
|
||||||
|
|
||||||
handleKeyPress = (event: SyntheticKeyboardEvent<HTMLInputElement>) => {
|
handleKeyPress = (event: SyntheticKeyboardEvent<HTMLInputElement>) => {
|
||||||
|
|||||||
@@ -9,10 +9,11 @@ export type SelectItem = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
name?: string,
|
||||||
label?: string,
|
label?: string,
|
||||||
options: SelectItem[],
|
options: SelectItem[],
|
||||||
value?: SelectItem,
|
value?: string,
|
||||||
onChange: string => void,
|
onChange: (value: string, name?: string) => void,
|
||||||
loading?: boolean,
|
loading?: boolean,
|
||||||
helpText?: string
|
helpText?: string
|
||||||
};
|
};
|
||||||
@@ -29,7 +30,7 @@ class Select extends React.Component<Props> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleInput = (event: SyntheticInputEvent<HTMLSelectElement>) => {
|
handleInput = (event: SyntheticInputEvent<HTMLSelectElement>) => {
|
||||||
this.props.onChange(event.target.value);
|
this.props.onChange(event.target.value, this.props.name);
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -40,10 +41,10 @@ class Select extends React.Component<Props> {
|
|||||||
return (
|
return (
|
||||||
<div className="field">
|
<div className="field">
|
||||||
<LabelWithHelpIcon label={label} helpText={helpText} />
|
<LabelWithHelpIcon label={label} helpText={helpText} />
|
||||||
<div className={classNames(
|
<div className={classNames(
|
||||||
"control select",
|
"control select",
|
||||||
loadingClass
|
loadingClass
|
||||||
)}>
|
)}>
|
||||||
<select
|
<select
|
||||||
ref={input => {
|
ref={input => {
|
||||||
this.field = input;
|
this.field = input;
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ export { getPageFromMatch } from "./urls";
|
|||||||
export { apiClient, NOT_FOUND_ERROR, UNAUTHORIZED_ERROR } from "./apiclient.js";
|
export { apiClient, NOT_FOUND_ERROR, UNAUTHORIZED_ERROR } from "./apiclient.js";
|
||||||
|
|
||||||
export * from "./buttons";
|
export * from "./buttons";
|
||||||
|
export * from "./config";
|
||||||
export * from "./forms";
|
export * from "./forms";
|
||||||
export * from "./layout";
|
export * from "./layout";
|
||||||
export * from "./modals";
|
export * from "./modals";
|
||||||
|
|||||||
@@ -641,9 +641,9 @@
|
|||||||
version "0.0.2"
|
version "0.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85"
|
resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85"
|
||||||
|
|
||||||
"@scm-manager/ui-bundler@^0.0.19":
|
"@scm-manager/ui-bundler@^0.0.21":
|
||||||
version "0.0.19"
|
version "0.0.21"
|
||||||
resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.19.tgz#646ab1fa1e5389fad614542215c60678fb9816ae"
|
resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.21.tgz#f8b5fa355415cc67b8aaf8744e1701a299dff647"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/core" "^7.0.0"
|
"@babel/core" "^7.0.0"
|
||||||
"@babel/plugin-proposal-class-properties" "^7.0.0"
|
"@babel/plugin-proposal-class-properties" "^7.0.0"
|
||||||
@@ -681,6 +681,13 @@
|
|||||||
vinyl-source-stream "^2.0.0"
|
vinyl-source-stream "^2.0.0"
|
||||||
watchify "^3.11.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"
|
||||||
|
dependencies:
|
||||||
|
react "^16.4.2"
|
||||||
|
react-dom "^16.4.2"
|
||||||
|
|
||||||
"@types/node@*":
|
"@types/node@*":
|
||||||
version "10.12.0"
|
version "10.12.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.0.tgz#ea6dcbddbc5b584c83f06c60e82736d8fbb0c235"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.0.tgz#ea6dcbddbc5b584c83f06c60e82736d8fbb0c235"
|
||||||
@@ -6229,6 +6236,15 @@ rc@^1.2.7:
|
|||||||
minimist "^1.2.0"
|
minimist "^1.2.0"
|
||||||
strip-json-comments "~2.0.1"
|
strip-json-comments "~2.0.1"
|
||||||
|
|
||||||
|
react-dom@^16.4.2:
|
||||||
|
version "16.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.6.0.tgz#6375b8391e019a632a89a0988bce85f0cc87a92f"
|
||||||
|
dependencies:
|
||||||
|
loose-envify "^1.1.0"
|
||||||
|
object-assign "^4.1.1"
|
||||||
|
prop-types "^15.6.2"
|
||||||
|
scheduler "^0.10.0"
|
||||||
|
|
||||||
react-dom@^16.5.2:
|
react-dom@^16.5.2:
|
||||||
version "16.5.2"
|
version "16.5.2"
|
||||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.5.2.tgz#b69ee47aa20bab5327b2b9d7c1fe2a30f2cfa9d7"
|
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.5.2.tgz#b69ee47aa20bab5327b2b9d7c1fe2a30f2cfa9d7"
|
||||||
@@ -6299,6 +6315,15 @@ react-test-renderer@^16.0.0-0:
|
|||||||
react-is "^16.5.2"
|
react-is "^16.5.2"
|
||||||
schedule "^0.5.0"
|
schedule "^0.5.0"
|
||||||
|
|
||||||
|
react@^16.4.2:
|
||||||
|
version "16.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react/-/react-16.6.0.tgz#b34761cfaf3e30f5508bc732fb4736730b7da246"
|
||||||
|
dependencies:
|
||||||
|
loose-envify "^1.1.0"
|
||||||
|
object-assign "^4.1.1"
|
||||||
|
prop-types "^15.6.2"
|
||||||
|
scheduler "^0.10.0"
|
||||||
|
|
||||||
react@^16.5.2:
|
react@^16.5.2:
|
||||||
version "16.5.2"
|
version "16.5.2"
|
||||||
resolved "https://registry.yarnpkg.com/react/-/react-16.5.2.tgz#19f6b444ed139baa45609eee6dc3d318b3895d42"
|
resolved "https://registry.yarnpkg.com/react/-/react-16.5.2.tgz#19f6b444ed139baa45609eee6dc3d318b3895d42"
|
||||||
@@ -6726,6 +6751,13 @@ schedule@^0.5.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
object-assign "^4.1.1"
|
object-assign "^4.1.1"
|
||||||
|
|
||||||
|
scheduler@^0.10.0:
|
||||||
|
version "0.10.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.10.0.tgz#7988de90fe7edccc774ea175a783e69c40c521e1"
|
||||||
|
dependencies:
|
||||||
|
loose-envify "^1.1.0"
|
||||||
|
object-assign "^4.1.1"
|
||||||
|
|
||||||
"semver@2 || 3 || 4 || 5", semver@^5.0.1, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1:
|
"semver@2 || 3 || 4 || 5", semver@^5.0.1, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1:
|
||||||
version "5.6.0"
|
version "5.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
|
resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
"check": "flow check"
|
"check": "flow check"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@scm-manager/ui-bundler": "^0.0.19"
|
"@scm-manager/ui-bundler": "^0.0.21"
|
||||||
},
|
},
|
||||||
"browserify": {
|
"browserify": {
|
||||||
"transform": [
|
"transform": [
|
||||||
|
|||||||
@@ -707,9 +707,9 @@
|
|||||||
version "0.0.2"
|
version "0.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85"
|
resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85"
|
||||||
|
|
||||||
"@scm-manager/ui-bundler@^0.0.19":
|
"@scm-manager/ui-bundler@^0.0.21":
|
||||||
version "0.0.19"
|
version "0.0.21"
|
||||||
resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.19.tgz#646ab1fa1e5389fad614542215c60678fb9816ae"
|
resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.21.tgz#f8b5fa355415cc67b8aaf8744e1701a299dff647"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/core" "^7.0.0"
|
"@babel/core" "^7.0.0"
|
||||||
"@babel/plugin-proposal-class-properties" "^7.0.0"
|
"@babel/plugin-proposal-class-properties" "^7.0.0"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-free": "^5.3.1",
|
"@fortawesome/fontawesome-free": "^5.3.1",
|
||||||
"@scm-manager/ui-extensions": "^0.0.7",
|
"@scm-manager/ui-extensions": "^0.1.1",
|
||||||
"bulma": "^0.7.1",
|
"bulma": "^0.7.1",
|
||||||
"bulma-tooltip": "^2.0.2",
|
"bulma-tooltip": "^2.0.2",
|
||||||
"classnames": "^2.2.5",
|
"classnames": "^2.2.5",
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
"pre-commit": "jest && flow && eslint src"
|
"pre-commit": "jest && flow && eslint src"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@scm-manager/ui-bundler": "^0.0.19",
|
"@scm-manager/ui-bundler": "^0.0.21",
|
||||||
"copyfiles": "^2.0.0",
|
"copyfiles": "^2.0.0",
|
||||||
"enzyme": "^3.3.0",
|
"enzyme": "^3.3.0",
|
||||||
"enzyme-adapter-react-16": "^1.1.1",
|
"enzyme-adapter-react-16": "^1.1.1",
|
||||||
|
|||||||
@@ -2,12 +2,19 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { translate } from "react-i18next";
|
import { translate } from "react-i18next";
|
||||||
import { Route } from "react-router";
|
import { Route } from "react-router";
|
||||||
|
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||||
|
|
||||||
|
import type { Links } from "@scm-manager/ui-types";
|
||||||
import { Page, Navigation, NavLink, Section } from "@scm-manager/ui-components";
|
import { Page, Navigation, NavLink, Section } from "@scm-manager/ui-components";
|
||||||
import GlobalConfig from "./GlobalConfig";
|
import GlobalConfig from "./GlobalConfig";
|
||||||
import type { History } from "history";
|
import type { History } from "history";
|
||||||
|
import {connect} from "react-redux";
|
||||||
|
import {compose} from "redux";
|
||||||
|
import { getLinks } from "../../modules/indexResource";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
links: Links,
|
||||||
|
|
||||||
// context objects
|
// context objects
|
||||||
t: string => string,
|
t: string => string,
|
||||||
match: any,
|
match: any,
|
||||||
@@ -27,15 +34,23 @@ class Config extends React.Component<Props> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { t } = this.props;
|
const { links, t } = this.props;
|
||||||
|
|
||||||
const url = this.matchedUrl();
|
const url = this.matchedUrl();
|
||||||
|
const extensionProps = {
|
||||||
|
links,
|
||||||
|
url
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page>
|
<Page>
|
||||||
<div className="columns">
|
<div className="columns">
|
||||||
<div className="column is-three-quarters">
|
<div className="column is-three-quarters">
|
||||||
<Route path={url} exact component={GlobalConfig} />
|
<Route path={url} exact component={GlobalConfig} />
|
||||||
|
<ExtensionPoint name="config.route"
|
||||||
|
props={extensionProps}
|
||||||
|
renderAll={true}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="column">
|
<div className="column">
|
||||||
<Navigation>
|
<Navigation>
|
||||||
@@ -44,6 +59,10 @@ class Config extends React.Component<Props> {
|
|||||||
to={`${url}`}
|
to={`${url}`}
|
||||||
label={t("global-config.navigation-label")}
|
label={t("global-config.navigation-label")}
|
||||||
/>
|
/>
|
||||||
|
<ExtensionPoint name="config.navigation"
|
||||||
|
props={extensionProps}
|
||||||
|
renderAll={true}
|
||||||
|
/>
|
||||||
</Section>
|
</Section>
|
||||||
</Navigation>
|
</Navigation>
|
||||||
</div>
|
</div>
|
||||||
@@ -53,4 +72,15 @@ class Config extends React.Component<Props> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default translate("config")(Config);
|
const mapStateToProps = (state: any) => {
|
||||||
|
const links = getLinks(state);
|
||||||
|
return {
|
||||||
|
links
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default compose(
|
||||||
|
connect(mapStateToProps),
|
||||||
|
translate("config")
|
||||||
|
)(Config);
|
||||||
|
|
||||||
|
|||||||
@@ -102,7 +102,6 @@ class Main extends React.Component<Props> {
|
|||||||
authenticated={authenticated}
|
authenticated={authenticated}
|
||||||
/>
|
/>
|
||||||
<ProtectedRoute
|
<ProtectedRoute
|
||||||
exact
|
|
||||||
path="/config"
|
path="/config"
|
||||||
component={Config}
|
component={Config}
|
||||||
authenticated={authenticated}
|
authenticated={authenticated}
|
||||||
|
|||||||
1341
scm-ui/yarn.lock
1341
scm-ui/yarn.lock
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user