Merge branch 'master' of github.com:ONLYOFFICE/CommunityServer-AspNetCore
This commit is contained in:
commit
99ba2483c8
@ -1,6 +1,6 @@
|
||||
import React, { useCallback } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { Text, IconButton, ContextMenuButton, toastr } from "asc-web-components";
|
||||
import { Text, IconButton, ContextMenuButton, toastr, utils } from "asc-web-components";
|
||||
import { withRouter } from "react-router";
|
||||
import { isAdmin, isMe } from "../../../../../store/auth/selectors";
|
||||
import { getUserStatus } from "../../../../../store/people/selectors";
|
||||
@ -8,16 +8,21 @@ import { useTranslation } from 'react-i18next';
|
||||
import { resendUserInvites } from "../../../../../store/services/api";
|
||||
import { EmployeeStatus } from "../../../../../helpers/constants";
|
||||
import { updateUserStatus } from "../../../../../store/people/actions";
|
||||
import { callbackify } from "util";
|
||||
import styled from 'styled-components';
|
||||
|
||||
const wrapperStyle = {
|
||||
display: "flex",
|
||||
alignItems: "center"
|
||||
};
|
||||
|
||||
const textStyle = {
|
||||
marginLeft: "16px",
|
||||
marginRight: "16px"
|
||||
};
|
||||
const Header = styled(Text.ContentHeader)`
|
||||
margin-left: 16px;
|
||||
margin-right: 16px;
|
||||
max-width: calc(100vw - 430px);
|
||||
@media ${utils.device.tablet} {
|
||||
max-width: calc(100vw - 96px);
|
||||
}
|
||||
`;
|
||||
|
||||
const SectionHeaderContent = props => {
|
||||
const { profile, history, settings, isAdmin, viewer, updateUserStatus } = props;
|
||||
@ -188,10 +193,10 @@ const SectionHeaderContent = props => {
|
||||
onClick={onClick}
|
||||
/>
|
||||
</div>
|
||||
<Text.ContentHeader truncate={true} style={textStyle}>
|
||||
<Header truncate={true}>
|
||||
{profile.displayName}
|
||||
{profile.isLDAP && ` (${t('LDAPLbl')})`}
|
||||
</Text.ContentHeader>
|
||||
</Header>
|
||||
{(isAdmin || isMe(viewer, profile.userName)) && (
|
||||
<ContextMenuButton
|
||||
directionX="right"
|
||||
|
@ -2,7 +2,7 @@ import React, { useCallback } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { connect } from 'react-redux';
|
||||
import { withRouter } from "react-router";
|
||||
import { IconButton, Text } from 'asc-web-components';
|
||||
import { IconButton, Text, utils } from 'asc-web-components';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {typeUser, typeGuest } from './../../../../../helpers/customNames';
|
||||
|
||||
@ -13,6 +13,10 @@ const Wrapper = styled.div`
|
||||
|
||||
const Header = styled(Text.ContentHeader)`
|
||||
margin-left: 16px;
|
||||
max-width: calc(100vw - 430px);
|
||||
@media ${utils.device.tablet} {
|
||||
max-width: calc(100vw - 64px);
|
||||
}
|
||||
`;
|
||||
|
||||
const SectionHeaderContent = (props) => {
|
||||
@ -35,7 +39,7 @@ const SectionHeaderContent = (props) => {
|
||||
return (
|
||||
<Wrapper>
|
||||
<IconButton iconName={'ArrowPathIcon'} size="16" onClick={onClick}/>
|
||||
<Header>{headerText}</Header>
|
||||
<Header truncate={true}>{headerText}</Header>
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
@ -5,11 +5,11 @@
|
||||
"AboutCompanyAddressTitle": "address",
|
||||
"AboutCompanyEmailTitle": "email",
|
||||
"AboutCompanyTelTitle": "tel.",
|
||||
"LicensedUnder": "This software is licensed under", "_comment": "{0}GNU GPL v.3{1}",
|
||||
"LicensedUnder": "This software is licensed under",
|
||||
|
||||
|
||||
|
||||
|
||||
"SourceCode": "Source code is available on", "_comment": "{0}GNU GPL v.3{1}","_comment":"SYNTAX ERROR"
|
||||
"SourceCode": "Source code is available on"
|
||||
|
||||
}
|
@ -5,6 +5,6 @@
|
||||
"AboutCompanyAddressTitle": "адрес",
|
||||
"AboutCompanyEmailTitle": "email",
|
||||
"AboutCompanyTelTitle": "тел.",
|
||||
"LicensedUnder": "Это программное обеспечение лицензируется под", "_comment": "{0}GNU GPL v.3{1}",
|
||||
"SourceCode": "Исходный код программы доступен по cсылке", "_comment": "{0}GNU GPL v.3{1}","_comment":"SYNTAX ERROR"
|
||||
"LicensedUnder": "Это программное обеспечение лицензируется под",
|
||||
"SourceCode": "Исходный код программы доступен по cсылке"
|
||||
}
|
@ -7,6 +7,7 @@ import i18n from "./i18n";
|
||||
import { I18nextProvider } from "react-i18next";
|
||||
import ChangeEmailForm from "./sub-components/changeEmail";
|
||||
import CreateUserForm from "./sub-components/createUser";
|
||||
import ActivateUserForm from "./sub-components/activateUser";
|
||||
|
||||
// const CreateUserForm = lazy(() => import("./sub-components/createUser"));
|
||||
const ChangePasswordForm = lazy(() => import("./sub-components/changePassword"));
|
||||
@ -26,9 +27,13 @@ const Confirm = ({ match, language }) => {
|
||||
>
|
||||
<Switch>
|
||||
<PublicRoute
|
||||
path={[`${match.path}/LinkInvite`, `${match.path}/Activation`]}
|
||||
path={`${match.path}/LinkInvite`}
|
||||
component={CreateUserForm}
|
||||
/>
|
||||
<PublicRoute
|
||||
path={`${match.path}/Activation`}
|
||||
component={ActivateUserForm}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/EmailActivation`}
|
||||
|
@ -13,7 +13,7 @@
|
||||
"ErrorPasswordNoUpperCase": "capital letters",
|
||||
"ErrorPasswordNoSpecialSymbols": "special characters",
|
||||
"EmailAndPasswordCopiedToClipboard": "Email and password copied to clipboard",
|
||||
"PassworResetTitle": "Now you can create a new password.", "_comment":"SYNTAX ERROR 'Passwor' Reset Title",
|
||||
"PassworResetTitle": "Now you can create a new password.",
|
||||
"PasswordCustomMode": "Password",
|
||||
"ImportContactsOkButton": "OK",
|
||||
"LoadingProcessing": "Loading...",
|
||||
|
@ -0,0 +1,321 @@
|
||||
import React from 'react';
|
||||
import { withRouter } from "react-router";
|
||||
import { withTranslation } from 'react-i18next';
|
||||
import { Button, TextInput, PageLayout, Text, PasswordInput, toastr, Loader } from 'asc-web-components';
|
||||
import styled from 'styled-components';
|
||||
import { Collapse } from 'reactstrap';
|
||||
import { connect } from 'react-redux';
|
||||
import { welcomePageTitle } from './../../../../helpers/customNames';
|
||||
import { EmployeeActivationStatus } from './../../../../helpers/constants';
|
||||
import { getPasswordSettings, activateConfirmUser } from '../../../../store/auth/actions';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const inputWidth = '400px';
|
||||
|
||||
const ConfirmContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-left: 200px;
|
||||
|
||||
@media (max-width: 830px) {
|
||||
margin-left: 40px;
|
||||
}
|
||||
|
||||
.start-basis {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.margin-left {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.full-width {
|
||||
width: ${inputWidth}
|
||||
}
|
||||
|
||||
.confirm-row {
|
||||
margin: 23px 0 0;
|
||||
}
|
||||
|
||||
.break-word {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
`;
|
||||
|
||||
const emailInputName = 'email';
|
||||
const passwordInputName = 'password';
|
||||
|
||||
class Confirm extends React.PureComponent {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const queryParams = props.location.search.slice(1).split('&');
|
||||
const arrayOfQueryParams = queryParams.map(queryParam => queryParam.split('='));
|
||||
const linkParams = Object.fromEntries(arrayOfQueryParams);
|
||||
const indexOfSlash = this.props.match.path.lastIndexOf('/');
|
||||
const typeLink = this.props.match.path.slice(indexOfSlash + 1);
|
||||
|
||||
this.state = {
|
||||
email: decodeURIComponent(linkParams.email),
|
||||
emailValid: true,
|
||||
firstName: decodeURIComponent(linkParams.firstname),
|
||||
firstNameValid: true,
|
||||
lastName: decodeURIComponent(linkParams.lastname),
|
||||
lastNameValid: true,
|
||||
password: '',
|
||||
passwordValid: true,
|
||||
errorText: '',
|
||||
isLoading: false,
|
||||
passwordEmpty: false,
|
||||
queryString: `type=${typeLink}&${props.location.search.slice(1)}`,
|
||||
type: typeLink,
|
||||
userId: linkParams.uid
|
||||
};
|
||||
}
|
||||
|
||||
onSubmit = (e) => {
|
||||
this.setState({ isLoading: true }, function () {
|
||||
const { history, activateConfirmUser } = this.props;
|
||||
|
||||
this.setState({ errorText: "" });
|
||||
|
||||
let hasError = false;
|
||||
|
||||
if (!this.state.firstName.trim()) {
|
||||
hasError = true;
|
||||
this.setState({ firstNameValid: !hasError });
|
||||
}
|
||||
|
||||
if (!this.state.lastName.trim()) {
|
||||
hasError = true;
|
||||
this.setState({ lastNameValid: !hasError });
|
||||
}
|
||||
|
||||
if (!this.state.passwordValid) {
|
||||
hasError = true;
|
||||
this.setState({ passwordValid: !hasError });
|
||||
}
|
||||
|
||||
!this.state.password.trim() && this.setState({ passwordEmpty: true });
|
||||
|
||||
if (hasError) {
|
||||
this.setState({ isLoading: false });
|
||||
return false;
|
||||
}
|
||||
|
||||
const loginData = {
|
||||
userName: this.state.email,
|
||||
password: this.state.password
|
||||
};
|
||||
const personalData = {
|
||||
firstname: this.state.firstName,
|
||||
lastname: this.state.lastName,
|
||||
email: this.state.email
|
||||
};
|
||||
|
||||
activateConfirmUser(personalData, loginData, this.state.queryString, this.state.userId, EmployeeActivationStatus.Activated)
|
||||
.then(() => history.push('/'))
|
||||
.catch(e => {
|
||||
console.error("activate error", e);
|
||||
this.setState({ errorText: e.message });
|
||||
this.setState({ isLoading: false });
|
||||
});
|
||||
|
||||
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
onKeyPress = (event) => {
|
||||
if (event.key === "Enter") {
|
||||
this.onSubmit();
|
||||
}
|
||||
};
|
||||
|
||||
onCopyToClipboard = () => toastr.success(this.props.t('EmailAndPasswordCopiedToClipboard'));
|
||||
validatePassword = (value) => this.setState({ passwordValid: value });
|
||||
|
||||
componentDidMount() {
|
||||
const { getPasswordSettings, history } = this.props;
|
||||
|
||||
getPasswordSettings(this.state.queryString)
|
||||
.then(
|
||||
function () {
|
||||
console.log("get settings success");
|
||||
}
|
||||
)
|
||||
.catch(e => {
|
||||
console.error("get settings error", e);
|
||||
history.push(`/login/error=${e}`);
|
||||
});
|
||||
|
||||
window.addEventListener('keydown', this.onKeyPress);
|
||||
window.addEventListener('keyup', this.onKeyPress);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('keydown', this.onKeyPress);
|
||||
window.removeEventListener('keyup', this.onKeyPress);
|
||||
}
|
||||
|
||||
onChangeName = event => {
|
||||
this.setState({ firstName: event.target.value });
|
||||
!this.state.firstNameValid && this.setState({ firstNameValid: event.target.value });
|
||||
this.state.errorText && this.setState({ errorText: "" });
|
||||
}
|
||||
|
||||
onChangeSurname = event => {
|
||||
this.setState({ lastName: event.target.value });
|
||||
!this.state.lastNameValid && this.setState({ lastNameValid: true });
|
||||
this.state.errorText && this.setState({ errorText: "" });;
|
||||
}
|
||||
|
||||
|
||||
onChangePassword = event => {
|
||||
this.setState({ password: event.target.value });
|
||||
!this.state.passwordValid && this.setState({ passwordValid: true });
|
||||
(event.target.value.trim()) && this.setState({ passwordEmpty: false });
|
||||
this.state.errorText && this.setState({ errorText: "" });
|
||||
this.onKeyPress(event);
|
||||
}
|
||||
|
||||
render() {
|
||||
console.log('ActivateUser render');
|
||||
const { settings, isConfirmLoaded, t } = this.props;
|
||||
return (
|
||||
!isConfirmLoaded
|
||||
? (
|
||||
<Loader className="pageLoader" type="rombs" size={40} />
|
||||
)
|
||||
: (
|
||||
<ConfirmContainer>
|
||||
<div className='start-basis'>
|
||||
<div className='margin-left'>
|
||||
<Text.Body className='confirm-row' as='p' fontSize={18}>{t('InviteTitle')}</Text.Body>
|
||||
|
||||
<div className='confirm-row full-width break-word'>
|
||||
<a href='/login'>
|
||||
<img src="images/dark_general.png" alt="Logo" />
|
||||
</a>
|
||||
<Text.Body as='p' fontSize={24} color='#116d9d'>{t('CustomWelcomePageTitle', { welcomePageTitle })}</Text.Body>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className='full-width'>
|
||||
|
||||
<TextInput
|
||||
className='confirm-row'
|
||||
id='name'
|
||||
name='name'
|
||||
value={this.state.firstName}
|
||||
placeholder={t('FirstName')}
|
||||
size='huge'
|
||||
scale={true}
|
||||
tabIndex={1}
|
||||
isAutoFocussed={true}
|
||||
autoComplete='given-name'
|
||||
isDisabled={this.state.isLoading}
|
||||
hasError={!this.state.firstNameValid}
|
||||
onChange={this.onChangeName}
|
||||
onKeyDown={this.onKeyPress}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
className='confirm-row'
|
||||
id='surname'
|
||||
name='surname'
|
||||
value={this.state.lastName}
|
||||
placeholder={t('LastName')}
|
||||
size='huge'
|
||||
scale={true}
|
||||
tabIndex={2}
|
||||
autoComplete='family-name'
|
||||
isDisabled={this.state.isLoading}
|
||||
hasError={!this.state.lastNameValid}
|
||||
onChange={this.onChangeSurname}
|
||||
onKeyDown={this.onKeyPress}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<PasswordInput
|
||||
className='confirm-row'
|
||||
id='password'
|
||||
inputName={passwordInputName}
|
||||
emailInputName={emailInputName}
|
||||
inputValue={this.state.password}
|
||||
placeholder={t('InvitePassword')}
|
||||
size='huge'
|
||||
scale={true}
|
||||
tabIndex={4}
|
||||
maxLength={30}
|
||||
inputWidth={inputWidth}
|
||||
hasError={this.state.passwordEmpty}
|
||||
onChange={this.onChangePassword}
|
||||
onCopyToClipboard={this.onCopyToClipboard}
|
||||
onValidateInput={this.validatePassword}
|
||||
clipActionResource={t('CopyEmailAndPassword')}
|
||||
clipEmailResource={`${t('Email')}: `}
|
||||
clipPasswordResource={`${t('InvitePassword')}: `}
|
||||
tooltipPasswordTitle={`${t('ErrorPasswordMessage')}:`}
|
||||
tooltipPasswordLength={`${t('ErrorPasswordLength', { fromNumber: 6, toNumber: 30 })}:`}
|
||||
tooltipPasswordDigits={t('ErrorPasswordNoDigits')}
|
||||
tooltipPasswordCapital={t('ErrorPasswordNoUpperCase')}
|
||||
tooltipPasswordSpecial={`${t('ErrorPasswordNoSpecialSymbols')} (!@#$%^&*)`}
|
||||
generatorSpecial="!@#$%^&*"
|
||||
passwordSettings={settings}
|
||||
isDisabled={this.state.isLoading}
|
||||
onKeyDown={this.onKeyPress}
|
||||
/>
|
||||
|
||||
|
||||
<Button
|
||||
className='confirm-row'
|
||||
primary
|
||||
size='big'
|
||||
label={t('LoginRegistryButton')}
|
||||
tabIndex={5}
|
||||
isLoading={this.state.isLoading}
|
||||
onClick={this.onSubmit}
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
{/* <Row className='confirm-row'>
|
||||
|
||||
<Text.Body as='p' fontSize={14}>{t('LoginWithAccount')}</Text.Body>
|
||||
|
||||
</Row>
|
||||
*/}
|
||||
<Collapse className='confirm-row'
|
||||
isOpen={!!this.state.errorText}>
|
||||
<div className="alert alert-danger">{this.state.errorText}</div>
|
||||
</Collapse>
|
||||
</div>
|
||||
</ConfirmContainer>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Confirm.propTypes = {
|
||||
getPasswordSettings: PropTypes.func.isRequired,
|
||||
activateConfirmUser: PropTypes.func.isRequired,
|
||||
location: PropTypes.object.isRequired,
|
||||
history: PropTypes.object.isRequired
|
||||
};
|
||||
const ActivateUserForm = (props) => (<PageLayout sectionBodyContent={<Confirm {...props} />} />);
|
||||
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
isConfirmLoaded: state.auth.isConfirmLoaded,
|
||||
settings: state.auth.password
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, { getPasswordSettings, activateConfirmUser })(withRouter(withTranslation()(ActivateUserForm)));
|
@ -1,32 +1,41 @@
|
||||
import React, { useState, useEffect, useCallback } from "react";
|
||||
import React from "react";
|
||||
import { withRouter } from "react-router";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import PropTypes from "prop-types";
|
||||
import styled from "styled-components";
|
||||
import { Row, Col, Card, CardImg, CardTitle } from "reactstrap";
|
||||
import { Button, PageLayout, Text, PasswordInput } from "asc-web-components";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import i18n from "../i18n";
|
||||
import {
|
||||
Container,
|
||||
Collapse,
|
||||
Row,
|
||||
Col,
|
||||
Card,
|
||||
CardTitle,
|
||||
CardImg
|
||||
} from "reactstrap";
|
||||
import {
|
||||
Button,
|
||||
PageLayout,
|
||||
Text,
|
||||
PasswordInput,
|
||||
Loader,
|
||||
toastr
|
||||
} from "asc-web-components";
|
||||
import { welcomePageTitle } from "../../../../helpers/customNames";
|
||||
import {
|
||||
changePassword,
|
||||
getPasswordSettings
|
||||
} from "../../../../../src/store/auth/actions";
|
||||
|
||||
const BodyStyle = styled.div`
|
||||
const BodyStyle = styled(Container)`
|
||||
margin-top: 70px;
|
||||
|
||||
p {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.password-row {
|
||||
margin: 23px 0 0;
|
||||
|
||||
.password-card {
|
||||
border: none;
|
||||
|
||||
.card-img {
|
||||
max-width: 216px;
|
||||
max-height: 35px;
|
||||
@ -42,179 +51,214 @@ const BodyStyle = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
const Form = props => {
|
||||
const [password, setPassword] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [errorText, setErrorText] = useState("");
|
||||
const [passwordValid, setPasswordValid] = useState(true);
|
||||
const {
|
||||
match,
|
||||
location,
|
||||
history,
|
||||
changePassword,
|
||||
getPasswordSettings
|
||||
} = props;
|
||||
const { params } = match;
|
||||
const { t } = useTranslation("translation", { i18n });
|
||||
class Form extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const indexOfSlash = match.path.lastIndexOf("/");
|
||||
const typeLink = match.path.slice(indexOfSlash + 1);
|
||||
const queryString = `type=${typeLink}&${location.search.slice(1)}`;
|
||||
const { match, location } = props;
|
||||
|
||||
getPasswordSettings(queryString)
|
||||
.then(function() {
|
||||
console.log("GET PASSWORD SETTINGS SUCCESS");
|
||||
})
|
||||
.catch(e => {
|
||||
console.log("ERROR GET PASSWORD SETTINGS", e);
|
||||
history.push(`/login/error=${e}`);
|
||||
});
|
||||
const str = location.search.split("&");
|
||||
const userId = str[2].slice(4);
|
||||
const indexOfSlash = match.path.lastIndexOf("/");
|
||||
const typeLink = match.path.slice(indexOfSlash + 1);
|
||||
const queryString = `type=${typeLink}&${location.search.slice(1)}`;
|
||||
|
||||
const onSubmit = useCallback(
|
||||
e => {
|
||||
errorText && setErrorText("");
|
||||
this.state = {
|
||||
password: "",
|
||||
passwordValid: true,
|
||||
isValidConfirmLink: false,
|
||||
errorText: "",
|
||||
isLoading: false,
|
||||
passwordEmpty: false,
|
||||
queryString: queryString,
|
||||
type: typeLink,
|
||||
userId: userId
|
||||
};
|
||||
}
|
||||
|
||||
onKeyPress = target => {
|
||||
if (target.key === "Enter") {
|
||||
this.onSubmit();
|
||||
}
|
||||
};
|
||||
|
||||
onChange = event => {
|
||||
this.setState({ password: event.target.value });
|
||||
!this.state.passwordValid && this.setState({ passwordValid: true });
|
||||
event.target.value.trim() && this.setState({ passwordEmpty: false });
|
||||
this.state.errorText && this.setState({ errorText: "" });
|
||||
this.onKeyPress(event);
|
||||
};
|
||||
|
||||
onSubmit = e => {
|
||||
this.setState({ isLoading: true }, function() {
|
||||
const { userId, password, queryString } = this.state;
|
||||
const { history, changePassword } = this.props;
|
||||
this.setState({ errorText: "" });
|
||||
let hasError = false;
|
||||
|
||||
if (!password.trim()) {
|
||||
if (!this.state.passwordValid) {
|
||||
hasError = true;
|
||||
setPasswordValid(!hasError);
|
||||
this.setState({ passwordValid: !hasError });
|
||||
}
|
||||
|
||||
if (hasError) return false;
|
||||
!this.state.password.trim() && this.setState({ passwordEmpty: true });
|
||||
|
||||
setIsLoading(true);
|
||||
console.log("changePassword onSubmit", match, location, history);
|
||||
if (hasError) {
|
||||
this.setState({ isLoading: false });
|
||||
return false;
|
||||
}
|
||||
|
||||
const str = location.search.split("&");
|
||||
const userId = str[2].slice(4);
|
||||
const key = `type=PasswordChange&${location.search.slice(1)}`;
|
||||
|
||||
changePassword(userId, { password }, key)
|
||||
changePassword(userId, { password }, queryString)
|
||||
.then(() => {
|
||||
console.log("UPDATE PASSWORD");
|
||||
history.push("/");
|
||||
})
|
||||
.catch(e => {
|
||||
history.push("/");
|
||||
console.log("ERROR UPDATE PASSWORD", e);
|
||||
console.log("ERROR UPDATE PASSWORD:", e.message);
|
||||
this.setState({ errorText: e.message });
|
||||
this.setState({ isLoading: false });
|
||||
});
|
||||
},
|
||||
[errorText, history, location, changePassword, match, password]
|
||||
);
|
||||
|
||||
const onKeyPress = useCallback(
|
||||
target => {
|
||||
if (target.key === "Enter") {
|
||||
onSubmit();
|
||||
}
|
||||
},
|
||||
[onSubmit]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
params.error && setErrorText(params.error);
|
||||
window.addEventListener("keydown", onKeyPress);
|
||||
window.addEventListener("keyup", onKeyPress);
|
||||
// Remove event listeners on cleanup
|
||||
return () => {
|
||||
window.removeEventListener("keydown", onKeyPress);
|
||||
window.removeEventListener("keyup", onKeyPress);
|
||||
};
|
||||
}, [onKeyPress, params.error]);
|
||||
|
||||
const settings = {
|
||||
minLength: 6,
|
||||
upperCase: false,
|
||||
digits: false,
|
||||
specSymbols: false
|
||||
});
|
||||
};
|
||||
|
||||
const tooltipPasswordLength =
|
||||
"from " + settings.minLength + " to 30 characters";
|
||||
componentDidMount() {
|
||||
const { getPasswordSettings, history } = this.props;
|
||||
getPasswordSettings(this.state.queryString)
|
||||
.then(() => {
|
||||
console.log("GET PASSWORD SETTINGS SUCCESS");
|
||||
})
|
||||
.catch(e => {
|
||||
console.log("ERROR GET PASSWORD SETTINGS", e);
|
||||
history.push(`/login/error=${e}`);
|
||||
});
|
||||
|
||||
const mdOptions = { size: 6, offset: 3 };
|
||||
return (
|
||||
<BodyStyle>
|
||||
<Row className="password-row">
|
||||
<Col sm="12" md={mdOptions}>
|
||||
<Card className="password-card">
|
||||
<CardImg
|
||||
className="card-img"
|
||||
src="images/dark_general.png"
|
||||
alt="Logo"
|
||||
top
|
||||
window.addEventListener("keydown", this.onKeyPress);
|
||||
window.addEventListener("keyup", this.onKeyPress);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener("keydown", this.onKeyPress);
|
||||
window.removeEventListener("keyup", this.onKeyPress);
|
||||
}
|
||||
|
||||
onCopyToClipboard = () =>
|
||||
toastr.success(this.props.t("EmailAndPasswordCopiedToClipboard"));
|
||||
|
||||
validatePassword = value => this.setState({ passwordValid: value });
|
||||
|
||||
render() {
|
||||
const { settings, isConfirmLoaded, t } = this.props;
|
||||
const { isLoading, password, passwordEmpty, errorText } = this.state;
|
||||
const mdOptions = { size: 6, offset: 3 };
|
||||
|
||||
return !isConfirmLoaded ? (
|
||||
<Loader className="pageLoader" type="rombs" size={40} />
|
||||
) : (
|
||||
<BodyStyle>
|
||||
<Row className="password-row">
|
||||
<Col sm="12" md={mdOptions}>
|
||||
<Card className="password-card">
|
||||
<CardImg
|
||||
className="card-img"
|
||||
src="images/dark_general.png"
|
||||
alt="Logo"
|
||||
top
|
||||
/>
|
||||
<CardTitle className="card-title">
|
||||
{t("CustomWelcomePageTitle", { welcomePageTitle })}
|
||||
</CardTitle>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row className="login-row">
|
||||
<Col sm="12" md={mdOptions}>
|
||||
<Text.Body fontSize={14}>{t("PassworResetTitle")}</Text.Body>
|
||||
|
||||
<PasswordInput
|
||||
id="password"
|
||||
name="password"
|
||||
inputName="password"
|
||||
inputValue={password}
|
||||
size="huge"
|
||||
scale={true}
|
||||
type="password"
|
||||
isDisabled={isLoading}
|
||||
hasError={passwordEmpty}
|
||||
onCopyToClipboard={this.onCopyToClipboard}
|
||||
onValidateInput={this.validatePassword}
|
||||
generatorSpecial="!@#$%^&*"
|
||||
tabIndex={1}
|
||||
value={password}
|
||||
onChange={this.onChange}
|
||||
emailInputName="E-mail"
|
||||
passwordSettings={settings}
|
||||
tooltipPasswordTitle="Password must contain:"
|
||||
tooltipPasswordLength={`${t("ErrorPasswordLength", {
|
||||
fromNumber: 6,
|
||||
toNumber: 30
|
||||
})}:`}
|
||||
placeholder={t("PasswordCustomMode")}
|
||||
maxLength={30}
|
||||
onKeyDown={this.onKeyPress}
|
||||
isAutoFocussed={true}
|
||||
inputWidth="490px"
|
||||
/>
|
||||
<CardTitle className="card-title">
|
||||
{t("CustomWelcomePageTitle", { welcomePageTitle })}
|
||||
</CardTitle>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="password-row">
|
||||
<Col sm="12" md={mdOptions}>
|
||||
<Button
|
||||
primary
|
||||
size="big"
|
||||
tabIndex={3}
|
||||
label={
|
||||
isLoading ? t("LoadingProcessing") : t("ImportContactsOkButton")
|
||||
}
|
||||
isDisabled={isLoading}
|
||||
isLoading={isLoading}
|
||||
onClick={this.onSubmit}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Collapse isOpen={!!errorText}>
|
||||
<Row className="login-row">
|
||||
<Col sm="12" md={mdOptions}>
|
||||
<div className="alert alert-danger">{errorText}</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</Collapse>
|
||||
</BodyStyle>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
<Text.Body fontSize={14}>{t("PassworResetTitle")}</Text.Body>
|
||||
<PasswordInput
|
||||
id="password"
|
||||
name="password"
|
||||
size="huge"
|
||||
scale={true}
|
||||
type="password"
|
||||
isDisabled={isLoading}
|
||||
hasError={!passwordValid}
|
||||
tabIndex={1}
|
||||
value={password}
|
||||
onChange={event => {
|
||||
setPassword(event.target.value);
|
||||
!passwordValid && setPasswordValid(true);
|
||||
errorText && setErrorText("");
|
||||
onKeyPress(event.target);
|
||||
}}
|
||||
emailInputName="E-mail"
|
||||
passwordSettings={settings}
|
||||
tooltipPasswordTitle="Password must contain:"
|
||||
tooltipPasswordLength={tooltipPasswordLength}
|
||||
placeholder={t("PasswordCustomMode")}
|
||||
maxLength={30}
|
||||
|
||||
//isAutoFocussed={true}
|
||||
//autocomple="current-password"
|
||||
//onKeyDown={event => onKeyPress(event.target)}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="password-row">
|
||||
<Col sm="12" md={mdOptions}>
|
||||
<Button
|
||||
primary
|
||||
size="big"
|
||||
tabIndex={3}
|
||||
label={
|
||||
isLoading ? t("LoadingProcessing") : t("ImportContactsOkButton")
|
||||
}
|
||||
isDisabled={isLoading}
|
||||
isLoading={isLoading}
|
||||
onClick={onSubmit}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</BodyStyle>
|
||||
);
|
||||
};
|
||||
|
||||
const ChangePasswordForm = props => {
|
||||
return <PageLayout sectionBodyContent={<Form {...props} />} />;
|
||||
};
|
||||
|
||||
ChangePasswordForm.propTypes = {
|
||||
Form.propTypes = {
|
||||
match: PropTypes.object.isRequired,
|
||||
location: PropTypes.object.isRequired,
|
||||
history: PropTypes.object.isRequired,
|
||||
changePassword: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
ChangePasswordForm.defaultProps = {
|
||||
Form.defaultProps = {
|
||||
password: ""
|
||||
};
|
||||
|
||||
const ChangePasswordForm = props => (
|
||||
<PageLayout sectionBodyContent={<Form {...props} />} />
|
||||
);
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
isValidConfirmLink: state.auth.isValidConfirmLink,
|
||||
isConfirmLoaded: state.auth.isConfirmLoaded,
|
||||
settings: state.auth.password
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(
|
||||
null,
|
||||
mapStateToProps,
|
||||
{ changePassword, getPasswordSettings }
|
||||
)(withRouter(withTranslation()(ChangePasswordForm)));
|
||||
|
@ -6,8 +6,7 @@ import styled from 'styled-components';
|
||||
import { Collapse } from 'reactstrap';
|
||||
import { connect } from 'react-redux';
|
||||
import { welcomePageTitle } from './../../../../helpers/customNames';
|
||||
import { EmployeeActivationStatus } from './../../../../helpers/constants';
|
||||
import { getPasswordSettings, createConfirmUser, activateConfirmUser } from '../../../../store/auth/actions';
|
||||
import { getPasswordSettings, createConfirmUser } from '../../../../store/auth/actions';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const inputWidth = '400px';
|
||||
@ -64,9 +63,9 @@ class Confirm extends React.PureComponent {
|
||||
this.state = {
|
||||
email: '',
|
||||
emailValid: true,
|
||||
firstName: linkParams.firstname === undefined ? '' : decodeURIComponent(linkParams.firstname),
|
||||
firstName: '',
|
||||
firstNameValid: true,
|
||||
lastName: linkParams.lastname === undefined ? '' : decodeURIComponent(linkParams.lastname),
|
||||
lastName: '',
|
||||
lastNameValid: true,
|
||||
password: '',
|
||||
passwordValid: true,
|
||||
@ -82,7 +81,7 @@ class Confirm extends React.PureComponent {
|
||||
|
||||
onSubmit = (e) => {
|
||||
this.setState({ isLoading: true }, function () {
|
||||
const { history, createConfirmUser, activateConfirmUser } = this.props;
|
||||
const { history, createConfirmUser } = this.props;
|
||||
const queryParams = this.state.queryString.split('&');
|
||||
const arrayOfQueryParams = queryParams.map(queryParam => queryParam.split('='));
|
||||
const linkParams = Object.fromEntries(arrayOfQueryParams);
|
||||
@ -102,7 +101,7 @@ class Confirm extends React.PureComponent {
|
||||
this.setState({ lastNameValid: !hasError });
|
||||
}
|
||||
|
||||
if (!this.state.queryEmail && !validationEmail.test(this.state.email.trim())) {
|
||||
if (!validationEmail.test(this.state.email.trim())) {
|
||||
hasError = true;
|
||||
this.setState({ emailValid: !hasError });
|
||||
}
|
||||
@ -118,40 +117,24 @@ class Confirm extends React.PureComponent {
|
||||
this.setState({ isLoading: false });
|
||||
return false;
|
||||
}
|
||||
const emailData = this.state.type === 'LinkInvite' ? this.state.email : this.state.queryEmail;
|
||||
|
||||
const loginData = {
|
||||
userName: emailData,
|
||||
userName: this.state.email,
|
||||
password: this.state.password
|
||||
};
|
||||
const personalData = {
|
||||
firstname: this.state.firstName,
|
||||
lastname: this.state.lastName,
|
||||
email: emailData
|
||||
email: this.state.email
|
||||
};
|
||||
const registerData = Object.assign(personalData, { isVisitor: isVisitor })
|
||||
switch (this.state.type) {
|
||||
case 'LinkInvite':
|
||||
createConfirmUser(registerData, loginData, this.state.queryString)
|
||||
.then(() => history.push('/'))
|
||||
.catch(e => {
|
||||
console.error("confirm error", e);
|
||||
this.setState({ errorText: e.message });
|
||||
this.setState({ isLoading: false });
|
||||
});
|
||||
break;
|
||||
case 'Activation':
|
||||
activateConfirmUser(personalData, loginData, this.state.queryString, this.state.userId, EmployeeActivationStatus.Activated)
|
||||
.then(() => history.push('/'))
|
||||
.catch(e => {
|
||||
console.error("activate error", e);
|
||||
this.setState({ errorText: e.message });
|
||||
this.setState({ isLoading: false });
|
||||
});
|
||||
break;
|
||||
default: return;
|
||||
|
||||
}
|
||||
|
||||
createConfirmUser(registerData, loginData, this.state.queryString)
|
||||
.then(() => history.push('/'))
|
||||
.catch(e => {
|
||||
console.error("confirm error", e);
|
||||
this.setState({ errorText: e.message });
|
||||
this.setState({ isLoading: false });
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
@ -271,7 +254,7 @@ class Confirm extends React.PureComponent {
|
||||
onKeyDown={this.onKeyPress}
|
||||
/>
|
||||
|
||||
{this.state.type === 'LinkInvite' && <TextInput
|
||||
<TextInput
|
||||
className='confirm-row'
|
||||
id='email'
|
||||
name={emailInputName}
|
||||
@ -286,7 +269,7 @@ class Confirm extends React.PureComponent {
|
||||
onChange={this.onChangeEmail}
|
||||
onKeyDown={this.onKeyPress}
|
||||
/>
|
||||
}
|
||||
|
||||
</div>
|
||||
|
||||
<PasswordInput
|
||||
@ -366,4 +349,4 @@ function mapStateToProps(state) {
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, { getPasswordSettings, createConfirmUser, activateConfirmUser })(withRouter(withTranslation()(CreateUserForm)));
|
||||
export default connect(mapStateToProps, { getPasswordSettings, createConfirmUser })(withRouter(withTranslation()(CreateUserForm)));
|
89
web/ASC.Web.Client/src/helpers/confirmRoute.js
Normal file
89
web/ASC.Web.Client/src/helpers/confirmRoute.js
Normal file
@ -0,0 +1,89 @@
|
||||
import React from 'react';
|
||||
import { Redirect, Route } from 'react-router-dom';
|
||||
import { AUTH_KEY } from './constants';
|
||||
import Cookies from 'universal-cookie';
|
||||
import { connect } from 'react-redux';
|
||||
import { checkConfirmLink } from './../store/auth/actions';
|
||||
import { ValidationResult } from './../helpers/constants';
|
||||
import decomposeConfirmLink from './../helpers/decomposeConfirmLink';
|
||||
import { PageLayout, Loader } from "asc-web-components";
|
||||
|
||||
class ConfirmRoute extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
type: '',
|
||||
key: '',
|
||||
p: '',
|
||||
emplType: '',
|
||||
email: '',
|
||||
uid: '',
|
||||
firstname: '',
|
||||
lastname: '',
|
||||
isReady: false,
|
||||
componentProps: {}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { pathname, search } = this.props.location;
|
||||
const { checkConfirmLink } = this.props;
|
||||
const decomposedLink = decomposeConfirmLink(pathname, search);
|
||||
let validationResult;
|
||||
checkConfirmLink(decomposedLink)
|
||||
.then((res) => {
|
||||
validationResult = res.data.response;
|
||||
switch (validationResult) {
|
||||
case ValidationResult.Ok:
|
||||
const confirmHeader = `type=${decomposedLink.type}&${search.slice(1)}`;
|
||||
const componentProps = Object.assign({}, decomposedLink, { confirmHeader });
|
||||
this.setState({
|
||||
isReady: true,
|
||||
componentProps
|
||||
});
|
||||
break;
|
||||
case ValidationResult.Invalid:
|
||||
window.location.href = '/login/error=Invalid link'
|
||||
break;
|
||||
case ValidationResult.Expired:
|
||||
window.location.href = '/login/error=Expired link'
|
||||
break;
|
||||
default:
|
||||
window.location.href = '/login/error=Unknown error'
|
||||
break;
|
||||
}
|
||||
})
|
||||
.catch((e) => window.location.href = '/');
|
||||
}
|
||||
|
||||
render() {
|
||||
const { component: Component, location, path, computedMatch, ...rest } = this.props;
|
||||
const newProps = Object.assign({}, { location, path, computedMatch }, { linkData: this.state.componentProps });
|
||||
return (
|
||||
<Route
|
||||
{...rest}
|
||||
render={props =>
|
||||
!this.state.isReady ? (
|
||||
<PageLayout
|
||||
sectionBodyContent={
|
||||
<Loader className="pageLoader" type="rombs" size={40} />
|
||||
}
|
||||
/>
|
||||
) :
|
||||
(new Cookies()).get(AUTH_KEY) ? (
|
||||
<Redirect
|
||||
to={{
|
||||
pathname: "/",
|
||||
state: { from: props.location }
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Component {...newProps} />
|
||||
)
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
export default connect(null, { checkConfirmLink })(ConfirmRoute);
|
@ -10,3 +10,37 @@ export const EmployeeActivationStatus = Object.freeze({
|
||||
Pending: 2,
|
||||
AutoGenerated: 4
|
||||
});
|
||||
|
||||
/**
|
||||
* Enum for type of confirm link.
|
||||
* @readonly
|
||||
*/
|
||||
export const ConfirmType = Object.freeze({
|
||||
EmpInvite: 0,
|
||||
LinkInvite: 1,
|
||||
PortalSuspend: 2,
|
||||
PortalContinue: 3,
|
||||
PortalRemove: 4,
|
||||
DnsChange: 5,
|
||||
PortalOwnerChange: 6,
|
||||
Activation: 7,
|
||||
EmailChange: 8,
|
||||
EmailActivation: 9,
|
||||
PasswordChange: 10,
|
||||
ProfileRemove: 11,
|
||||
PhoneActivation: 12,
|
||||
PhoneAuth: 13,
|
||||
Auth: 14,
|
||||
TfaActivation: 15,
|
||||
TfaAuth: 16
|
||||
});
|
||||
|
||||
/**
|
||||
* Enum for result of validation confirm link.
|
||||
* @readonly
|
||||
*/
|
||||
export const ValidationResult = Object.freeze({
|
||||
Ok: 0,
|
||||
Invalid: 1,
|
||||
Expired: 2
|
||||
});
|
||||
|
11
web/ASC.Web.Client/src/helpers/decomposeConfirmLink.js
Normal file
11
web/ASC.Web.Client/src/helpers/decomposeConfirmLink.js
Normal file
@ -0,0 +1,11 @@
|
||||
const decomposeLink = (url, querySearch) => {
|
||||
const queryString = querySearch.slice(1).split('&');
|
||||
const arrayOfQueryString = queryString.map(queryParam => queryParam.split('='));
|
||||
const queryParams = Object.fromEntries(arrayOfQueryString);
|
||||
const posSeparator = url.lastIndexOf('/');
|
||||
const type = url.slice(posSeparator + 1);
|
||||
const data = Object.assign({ type }, queryParams);
|
||||
return data;
|
||||
}
|
||||
|
||||
export default decomposeLink;
|
@ -202,4 +202,10 @@ export function activateConfirmUser(personalData, loginData, key, userId, activa
|
||||
return getUserInfo(dispatch);
|
||||
});
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
export function checkConfirmLink(data) {
|
||||
return dispatch => {
|
||||
return api.checkConfirmLink(data);
|
||||
}
|
||||
}
|
||||
|
@ -81,4 +81,10 @@ export function updateUser(data) {
|
||||
return IS_FAKE
|
||||
? fakeApi.updateUser()
|
||||
: axios.put(`${API_URL}/people/${data.id}`, data);
|
||||
}
|
||||
|
||||
export function checkConfirmLink(data) {
|
||||
return IS_FAKE
|
||||
? fakeApi.checkConfirmLink()
|
||||
: axios.post(`${API_URL}/authentication/confirm.json`, data);
|
||||
}
|
@ -163,3 +163,7 @@ export function updateActivationStatus() {
|
||||
export function updateUser(data) {
|
||||
return fakeResponse(data);
|
||||
}
|
||||
|
||||
export function checkConfirmLink(data) {
|
||||
return fakeResponse(data);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user