Login: {
LoginCluster: string;
};
+ Collections: {
+ ForwardSlashNameSubstitution: string;
+ };
}
export class Config {
Login: {
LoginCluster: "",
},
+ Collections: {
+ ForwardSlashNameSubstitution: "",
+ },
...config
});
import * as React from "react";
import { ErrorIcon } from "~/components/icon/icon";
-import { invalidNamingRules } from "~/validators/valid-name";
+import { disallowSlash } from "~/validators/valid-name";
import { Tooltip } from "@material-ui/core";
interface WarningComponentProps {
export const WarningComponent = ({ text, rules, message }: WarningComponentProps) =>
rules.find(aRule => text.match(aRule) !== null)
- ? message
- ? <Tooltip title={message}><ErrorIcon /></Tooltip>
- : <ErrorIcon />
- : null;
+ ? message
+ ? <Tooltip title={message}><ErrorIcon /></Tooltip>
+ : <ErrorIcon />
+ : null;
interface IllegalNamingWarningProps {
name: string;
export const IllegalNamingWarning = ({ name }: IllegalNamingWarningProps) =>
<WarningComponent
- text={name} rules={invalidNamingRules}
- message="Names being '.', '..' or including '/' cause issues with WebDAV, please edit it to something different." />;
\ No newline at end of file
+ text={name} rules={[disallowSlash]}
+ message="Names embedding '/' will be renamed or invisible to file system access (arv-mount or WebDAV)" />;
//
// SPDX-License-Identifier: AGPL-3.0
+export const disallowDotName = /^\.{1,2}$/;
+export const disallowSlash = /\//;
const ERROR_MESSAGE = "Name cannot be '.' or '..' or contain '/' characters";
-export const invalidNamingRules = [/\//, /^\.{1,2}$/];
-
export const validName = (value: string) => {
- return invalidNamingRules.find(aRule => value.match(aRule) !== null)
+ return [disallowDotName, disallowSlash].find(aRule => value.match(aRule) !== null)
? ERROR_MESSAGE
: undefined;
};
+
+export const validNameAllowSlash = (value: string) => {
+ return [disallowDotName].find(aRule => value.match(aRule) !== null)
+ ? "Name cannot be '.' or '..'"
+ : undefined;
+};
import { maxLength } from './max-length';
import { isRsaKey } from './is-rsa-key';
import { isRemoteHost } from "./is-remote-host";
-import { validName } from "./valid-name";
+import { validName, validNameAllowSlash } from "./valid-name";
export const TAG_KEY_VALIDATION = [require, maxLength(255)];
export const TAG_VALUE_VALIDATION = [require, maxLength(255)];
export const PROJECT_NAME_VALIDATION = [require, validName, maxLength(255)];
+export const PROJECT_NAME_VALIDATION_ALLOW_SLASH = [require, validNameAllowSlash, maxLength(255)];
export const COLLECTION_NAME_VALIDATION = [require, validName, maxLength(255)];
+export const COLLECTION_NAME_VALIDATION_ALLOW_SLASH = [require, validNameAllowSlash, maxLength(255)];
export const COLLECTION_DESCRIPTION_VALIDATION = [maxLength(255)];
export const COLLECTION_PROJECT_VALIDATION = [require];
// SPDX-License-Identifier: AGPL-3.0
import * as React from "react";
-import { Field } from "redux-form";
+import { Field, Validator } from "redux-form";
import { TextField } from "~/components/text-field/text-field";
-import { COLLECTION_NAME_VALIDATION, COLLECTION_DESCRIPTION_VALIDATION, COLLECTION_PROJECT_VALIDATION } from "~/validators/validators";
+import {
+ COLLECTION_NAME_VALIDATION, COLLECTION_NAME_VALIDATION_ALLOW_SLASH,
+ COLLECTION_DESCRIPTION_VALIDATION, COLLECTION_PROJECT_VALIDATION
+} from "~/validators/validators";
import { ProjectTreePickerField, CollectionTreePickerField } from "~/views-components/projects-tree-picker/tree-picker-field";
import { PickerIdProp } from '~/store/tree-picker/picker-id';
+import { connect } from "react-redux";
+import { RootState } from "~/store/store";
-export const CollectionNameField = () =>
- <Field
- name='name'
- component={TextField}
- validate={COLLECTION_NAME_VALIDATION}
- label="Collection Name"
- autoFocus={true} />;
+interface CollectionNameFieldProps {
+ validate: Validator[];
+}
+
+// See implementation note on declaration of ProjectNameField
+
+export const CollectionNameField = connect(
+ (state: RootState) => {
+ return {
+ validate: (state.auth.config.clusterConfig.Collections.ForwardSlashNameSubstitution === "" ?
+ COLLECTION_NAME_VALIDATION : COLLECTION_NAME_VALIDATION_ALLOW_SLASH)
+ };
+ })((props: CollectionNameFieldProps) =>
+ <Field
+ name='name'
+ component={TextField}
+ validate={props.validate}
+ label="Collection Name"
+ autoFocus={true} />
+ );
export const CollectionDescriptionField = () =>
<Field
// SPDX-License-Identifier: AGPL-3.0
import * as React from "react";
-import { Field } from "redux-form";
+import { Field, Validator } from "redux-form";
import { TextField, RichEditorTextField } from "~/components/text-field/text-field";
-import { PROJECT_NAME_VALIDATION } from "~/validators/validators";
+import { PROJECT_NAME_VALIDATION, PROJECT_NAME_VALIDATION_ALLOW_SLASH } from "~/validators/validators";
+import { connect } from "react-redux";
+import { RootState } from "~/store/store";
-export const ProjectNameField = () =>
- <Field
- name='name'
- component={TextField}
- validate={PROJECT_NAME_VALIDATION}
- label="Project Name"
- autoFocus={true} />;
+interface ProjectNameFieldProps {
+ validate: Validator[];
+}
+
+// Validation behavior depends on the value of ForwardSlashNameSubstitution.
+//
+// Redux form doesn't let you pass anonymous functions to 'validate'
+// -- it fails with a very confusing recursive-update-exceeded error.
+// So we can't construct the validation function on the fly.
+//
+// As a workaround, use ForwardSlashNameSubstitution to choose between one of two const-defined validators.
+
+export const ProjectNameField = connect(
+ (state: RootState) => {
+ return {
+ validate: (state.auth.config.clusterConfig.Collections.ForwardSlashNameSubstitution === "" ?
+ PROJECT_NAME_VALIDATION : PROJECT_NAME_VALIDATION_ALLOW_SLASH)
+ };
+ })((props: ProjectNameFieldProps) =>
+ <Field
+ name='name'
+ component={TextField}
+ validate={props.validate}
+ label="Project Name"
+ autoFocus={true} />
+ );
export const ProjectDescriptionField = () =>
<Field