Merge pull request #376 from ONLYOFFICE/bugfix/profile-form-validation

Bugfix/profile form validation
This commit is contained in:
Ilya Oleshko 2021-10-06 13:58:03 +03:00 committed by GitHub
commit 596ff52ed4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 114 additions and 27 deletions

View File

@ -92,6 +92,9 @@ class SettingsStore {
};
debugInfo = false;
userFormValidation = /^[\p{L}\p{M}'\-]+$/gu;
folderFormValidation = new RegExp('[*+:"<>?|\\\\/]', "gim");
constructor() {
makeAutoObservable(this);
}

View File

@ -182,15 +182,15 @@ export default function withContent(WrappedContent) {
};
renameTitle = (e) => {
const { t } = this.props;
const { t, folderFormValidation } = this.props;
let title = e.target.value;
//const chars = '*+:"<>?|/'; TODO: think how to solve problem with interpolation escape values in i18n translate
const regexp = new RegExp('[*+:"<>?|\\\\/]', "gim");
if (title.match(regexp)) {
if (title.match(folderFormValidation)) {
toastr.warning(t("ContainsSpecCharacter"));
}
title = title.replace(regexp, "_");
title = title.replace(folderFormValidation, "_");
return this.setState({ itemTitle: title });
};
@ -324,7 +324,11 @@ export default function withContent(WrappedContent) {
id: fileActionId,
} = filesStore.fileActionStore;
const { replaceFileStream, setEncryptionAccess } = auth;
const { culture, isDesktopClient } = auth.settingsStore;
const {
culture,
isDesktopClient,
folderFormValidation,
} = auth.settingsStore;
return {
setIsLoading,
@ -346,6 +350,7 @@ export default function withContent(WrappedContent) {
homepage: config.homepage,
viewer: auth.userStore.user,
viewAs,
folderFormValidation,
};
}
)(observer(WithContent));

View File

@ -29,6 +29,7 @@ const PureConnectDialogContainer = (props) => {
setConnectDialogVisible,
personal,
getSubfolders,
folderFormValidation,
} = props;
const {
corporate,
@ -80,12 +81,11 @@ const PureConnectDialogContainer = (props) => {
setIsTitleValid(true);
let title = e.target.value;
//const chars = '*+:"<>?|/'; TODO: think how to solve problem with interpolation escape values in i18n translate
const regexp = new RegExp('[*+:"<>?|\\\\/]', "gim");
if (title.match(regexp)) {
if (title.match(folderFormValidation)) {
toastr.warning(t("Home:ContainsSpecCharacter"));
}
title = title.replace(regexp, "_");
title = title.replace(folderFormValidation, "_");
setCustomerTitleValue(title);
};
@ -340,7 +340,11 @@ export default inject(
openConnectWindow,
fetchThirdPartyProviders,
} = settingsStore.thirdPartyStore;
const { getOAuthToken, personal } = auth.settingsStore;
const {
getOAuthToken,
personal,
folderFormValidation,
} = auth.settingsStore;
const {
treeFolders,
@ -364,6 +368,7 @@ export default inject(
providers,
visible,
item,
folderFormValidation,
getOAuthToken,
getSubfolders,

View File

@ -19,5 +19,7 @@
"TemporaryPassword": "Vorübergehendes Kennwort",
"TermsOfUsePopupHelperLink": "Erfahren Sie mehr über die Nutzungsbedingungen",
"UpdatingProcess": "Wird aktualisiert...",
"WriteComment": "Kommentar hinzufügen"
"WriteComment": "Kommentar hinzufügen",
"ErrorInvalidUserLastName": "Ungültiger Nachname",
"ErrorInvalidUserFirstName": "Ungültiger Vorname"
}

View File

@ -20,5 +20,7 @@
"TemporaryPassword": "Temporary password",
"TermsOfUsePopupHelperLink": "Read more about terms of use",
"UpdatingProcess": "Updating...",
"WriteComment": "Add comment"
"WriteComment": "Add comment",
"ErrorInvalidUserLastName": "Invalid last name",
"ErrorInvalidUserFirstName": "Invalid first name"
}

View File

@ -19,5 +19,7 @@
"TemporaryPassword": "Mot de passe temporaire",
"TermsOfUsePopupHelperLink": "Savoir plus à propos de termes d'utilisation ",
"UpdatingProcess": "Mis à jour...",
"WriteComment": "Ajouter un commentaire"
"WriteComment": "Ajouter un commentaire",
"ErrorInvalidUserLastName": "Prénom invalide",
"ErrorInvalidUserFirstName": "Nom invalide"
}

View File

@ -19,5 +19,7 @@
"TemporaryPassword": "Password temporanea",
"TermsOfUsePopupHelperLink": "Maggiori dettagli sui termini di utilizzo",
"UpdatingProcess": "Caricamento in corso...",
"WriteComment": "Aggiungi commento"
"WriteComment": "Aggiungi commento",
"ErrorInvalidUserLastName": "Cognome non valido",
"ErrorInvalidUserFirstName": "Nome non valido"
}

View File

@ -19,5 +19,7 @@
"TemporaryPassword": "Senha temporária",
"TermsOfUsePopupHelperLink": "Leia mais sobre os termos de uso",
"UpdatingProcess": "Atualizando...",
"WriteComment": "Adicionar comentário"
"WriteComment": "Adicionar comentário",
"ErrorInvalidUserLastName": "Sobrenome inválido",
"ErrorInvalidUserFirstName": "Primeiro nome inválido"
}

View File

@ -20,5 +20,7 @@
"TemporaryPassword": "Временный пароль",
"TermsOfUsePopupHelperLink": "Подробнее об условиях использования",
"UpdatingProcess": "Обновление...",
"WriteComment": "Добавить комментарий"
"WriteComment": "Добавить комментарий",
"ErrorInvalidUserLastName": "Недопустимая фамилия",
"ErrorInvalidUserFirstName": "Недопустимое имя"
}

View File

@ -15,6 +15,7 @@ class TextField extends React.Component {
isRequired,
hasError,
labelText,
errorMessage,
inputName,
inputValue,
@ -32,6 +33,7 @@ class TextField extends React.Component {
<FieldContainer
isRequired={isRequired}
hasError={hasError}
errorMessage={errorMessage}
labelText={labelText}
tooltipContent={tooltipContent}
helpButtonHeaderContent={helpButtonHeaderContent}

View File

@ -242,8 +242,19 @@ class CreateUserForm extends React.Component {
}
onInputChange = (event) => {
const { userFormValidation } = this.props;
var stateCopy = Object.assign({}, this.state);
stateCopy.profile[event.target.name] = event.target.value;
const value = event.target.value;
const title = event.target.name;
if (!value.match(userFormValidation)) {
stateCopy.errors[title] = true;
} else {
if (this.state.errors[title]) stateCopy.errors[title] = false;
}
stateCopy.profile[title] = value;
this.setState(stateCopy);
this.setIsEdit();
};
@ -262,8 +273,20 @@ class CreateUserForm extends React.Component {
this.setIsEdit();
};
scrollToErrorForm = () => {
const element = this.mainFieldsContainerRef.current;
const parent = element.closest(".scroll-body");
(parent || window).scrollTo(0, element.offsetTop);
};
validate = () => {
const { profile, errors: stateErrors } = this.state;
if (stateErrors.firstName || stateErrors.lastName) {
this.scrollToErrorForm();
return;
}
const errors = {
firstName: !profile.firstName.trim(),
lastName: !profile.lastName.trim(),
@ -274,9 +297,7 @@ class CreateUserForm extends React.Component {
errors.firstName || errors.lastName || errors.email || errors.password;
if (hasError) {
const element = this.mainFieldsContainerRef.current;
const parent = element.closest(".scroll-body");
(parent || window).scrollTo(0, element.offsetTop);
this.scrollToErrorForm();
}
this.setState({ errors: errors });
@ -436,6 +457,9 @@ class CreateUserForm extends React.Component {
const pattern = getUserContactsPattern();
const contacts = getUserContacts(profile.contacts);
const notEmptyFirstName = Boolean(profile.firstName.trim());
const notEmptyLastName = Boolean(profile.lastName.trim());
return (
<>
<MainContainer>
@ -470,6 +494,9 @@ class CreateUserForm extends React.Component {
<TextField
isRequired={true}
hasError={errors.firstName}
{...(notEmptyFirstName && {
errorMessage: t("ErrorInvalidUserFirstName"),
})}
labelText={`${t("FirstName")}:`}
inputName="firstName"
inputValue={profile.firstName}
@ -481,6 +508,9 @@ class CreateUserForm extends React.Component {
<TextField
isRequired={true}
hasError={errors.lastName}
{...(notEmptyLastName && {
errorMessage: t("ErrorInvalidUserLastName"),
})}
labelText={`${t("Common:LastName")}:`}
inputName="lastName"
inputValue={profile.lastName}
@ -687,6 +717,7 @@ export default withRouter(
setCroppedAvatar: peopleStore.avatarEditorStore.setCroppedAvatar,
updateProfileInUsers: peopleStore.usersStore.updateProfileInUsers,
updateCreatedAvatar: peopleStore.targetUserStore.updateCreatedAvatar,
userFormValidation: auth.settingsStore.userFormValidation,
}))(
observer(
withTranslation(["ProfileAction", "Common", "Translations"])(

View File

@ -238,8 +238,19 @@ class UpdateUserForm extends React.Component {
}
onInputChange(event) {
const { userFormValidation } = this.props;
var stateCopy = Object.assign({}, this.state);
stateCopy.profile[event.target.name] = event.target.value;
const value = event.target.value;
const title = event.target.name;
if (!value.match(userFormValidation)) {
stateCopy.errors[title] = true;
} else {
if (this.state.errors[title]) stateCopy.errors[title] = false;
}
stateCopy.profile[title] = value;
this.setState(stateCopy);
this.setIsEdit();
}
@ -278,21 +289,30 @@ class UpdateUserForm extends React.Component {
this.setIsEdit();
}
scrollToErrorForm = () => {
const element = this.mainFieldsContainerRef.current;
const parent = element.closest(".scroll-body");
(parent || window).scrollTo(0, element.offsetTop);
};
validate() {
const { profile } = this.state;
const errors = {
const { profile, errors } = this.state;
if (errors.firstName || errors.lastName) {
this.scrollToErrorForm();
return;
}
const errorsObj = {
firstName: !profile.firstName.trim(),
lastName: !profile.lastName.trim(),
};
const hasError = errors.firstName || errors.lastName;
const hasError = errorsObj.firstName || errorsObj.lastName;
if (hasError) {
const element = this.mainFieldsContainerRef.current;
const parent = element.closest(".scroll-body");
(parent || window).scrollTo(0, element.offsetTop);
this.scrollToErrorForm();
}
this.setState({ errors: errors });
this.setState({ errors: errorsObj });
return !hasError;
}
@ -615,6 +635,8 @@ class UpdateUserForm extends React.Component {
const pattern = getUserContactsPattern();
const contacts = getUserContacts(profile.contacts);
const notEmptyFirstName = Boolean(profile.firstName.trim());
const notEmptyLastName = Boolean(profile.lastName.trim());
//TODO: inject guestsCaption in 'ProfileTypePopupHelper' key instead of hardcoded 'Guests'
const tooltipTypeContent = (
<>
@ -790,6 +812,9 @@ class UpdateUserForm extends React.Component {
isRequired={true}
hasError={errors.firstName}
labelText={`${t("FirstName")}:`}
{...(notEmptyFirstName && {
errorMessage: t("ErrorInvalidUserFirstName"),
})}
inputName="firstName"
inputValue={profile.firstName}
inputIsDisabled={isLoading}
@ -802,6 +827,9 @@ class UpdateUserForm extends React.Component {
<TextField
isRequired={true}
hasError={errors.lastName}
{...(notEmptyLastName && {
errorMessage: t("ErrorInvalidUserLastName"),
})}
labelText={`${t("Common:LastName")}:`}
inputName="lastName"
inputValue={profile.lastName}
@ -1020,6 +1048,7 @@ export default withRouter(
isEditTargetUser: peopleStore.targetUserStore.isEditTargetUser,
personal: auth.settingsStore.personal,
setUserIsUpdate: auth.userStore.setUserIsUpdate,
userFormValidation: auth.settingsStore.userFormValidation,
}))(
observer(
withTranslation(["ProfileAction", "Common", "Translations"])(