import React, { Fragment } from 'react'; import PropTypes from 'prop-types'; import copy from 'copy-to-clipboard'; import moment from 'moment'; import { Button, Form, FormGroup, Label, Input, InputGroup, InputGroupAddon, InputGroupText, Alert, FormText } from 'reactstrap'; import { gettext, shareLinkForceUsePassword, shareLinkPasswordMinLength, shareLinkPasswordStrengthLevel, canSendShareLinkEmail, uploadLinkExpireDaysMin, uploadLinkExpireDaysMax, uploadLinkExpireDaysDefault } from '../../utils/constants'; import { seafileAPI } from '../../utils/seafile-api'; import { Utils } from '../../utils/utils'; import UploadLink from '../../models/upload-link'; import toaster from '../toast'; import SendLink from '../send-link'; import DateTimePicker from '../date-and-time-picker'; import SharedLink from '../shared-link'; const propTypes = { itemPath: PropTypes.string.isRequired, repoID: PropTypes.string.isRequired, closeShareDialog: PropTypes.func.isRequired, }; const inputWidth = Utils.isDesktop() ? 250 : 210; class GenerateUploadLink extends React.Component { constructor(props) { super(props); this.isExpireDaysNoLimit = (uploadLinkExpireDaysMin === 0 && uploadLinkExpireDaysMax === 0 && uploadLinkExpireDaysDefault == 0); this.defaultExpireDays = this.isExpireDaysNoLimit ? '' : uploadLinkExpireDaysDefault; let expirationLimitTip = ''; if (uploadLinkExpireDaysMin !== 0 && uploadLinkExpireDaysMax !== 0) { expirationLimitTip = gettext('{minDays_placeholder} - {maxDays_placeholder} days') .replace('{minDays_placeholder}', uploadLinkExpireDaysMin) .replace('{maxDays_placeholder}', uploadLinkExpireDaysMax); } else if (uploadLinkExpireDaysMin !== 0 && uploadLinkExpireDaysMax === 0) { expirationLimitTip = gettext('Greater than or equal to {minDays_placeholder} days') .replace('{minDays_placeholder}', uploadLinkExpireDaysMin); } else if (uploadLinkExpireDaysMin === 0 && uploadLinkExpireDaysMax !== 0) { expirationLimitTip = gettext('Less than or equal to {maxDays_placeholder} days') .replace('{maxDays_placeholder}', uploadLinkExpireDaysMax); } this.expirationLimitTip = expirationLimitTip; this.state = { showPasswordInput: shareLinkForceUsePassword ? true : false, passwordVisible: false, password: '', passwordnew: '', sharedUploadInfo: null, isSendLinkShown: false, isExpireChecked: !this.isExpireDaysNoLimit, setExp: 'by-days', expireDays: this.defaultExpireDays, expDate: null }; } componentDidMount() { this.getUploadLink(); } getUploadLink = () => { let path = this.props.itemPath; let repoID = this.props.repoID; seafileAPI.getUploadLink(repoID, path).then((res) => { if (res.data.length !== 0) { let sharedUploadInfo = new UploadLink(res.data[0]); this.setState({sharedUploadInfo: sharedUploadInfo}); } }).catch((err) => { let errMsg = Utils.getErrorMsg(err, true); if (!err.response || err.response.status !== 403) { toaster.danger(errMsg); } this.props.closeShareDialog(); }); } addPassword = () => { this.setState({ showPasswordInput: !this.state.showPasswordInput, password: '', passwordnew: '', errorInfo: '' }); } togglePasswordVisible = () => { this.setState({ passwordVisible: !this.state.passwordVisible }); } generatePassword = () => { let val = Utils.generatePassword(shareLinkPasswordMinLength); this.setState({ password: val, passwordnew: val }); } inputPassword = (e) => { this.setState({ password: e.target.value }); } inputPasswordNew = (e) => { this.setState({ passwordnew: e.target.value }); } generateUploadLink = () => { let isValid = this.validateParamsInput(); if (isValid) { this.setState({errorInfo: ''}); let { itemPath, repoID } = this.props; let { password, isExpireChecked, setExp, expireDays, expDate } = this.state; let expirationTime = ''; if (isExpireChecked) { if (setExp == 'by-days') { expirationTime = moment().add(parseInt(expireDays), 'days').format(); } else { expirationTime = expDate.format(); } } seafileAPI.createUploadLink(repoID, itemPath, password, expirationTime).then((res) => { let sharedUploadInfo = new UploadLink(res.data); this.setState({sharedUploadInfo: sharedUploadInfo}); }).catch(error => { let errMessage = Utils.getErrorMsg(error); toaster.danger(errMessage); }); } } validateParamsInput = () => { let { showPasswordInput, password, passwordnew, isExpireChecked, setExp, expireDays, expDate } = this.state; // check password params if (showPasswordInput) { if (password.length === 0) { this.setState({errorInfo: gettext('Please enter password')}); return false; } if (password.length < shareLinkPasswordMinLength) { this.setState({errorInfo: gettext('Password is too short')}); return false; } if (password !== passwordnew) { this.setState({errorInfo: gettext('Passwords don\'t match')}); return false; } if (Utils.getStrengthLevel(password) < shareLinkPasswordStrengthLevel) { this.setState({errorInfo: gettext('Password is too weak, should have at least {shareLinkPasswordStrengthLevel} of the following: num, upper letter, lower letter and other symbols'.replace('{shareLinkPasswordStrengthLevel}', shareLinkPasswordStrengthLevel))}); return false; } } if (isExpireChecked) { if (setExp == 'by-date') { if (!expDate) { this.setState({errorInfo: 'Please select an expiration time'}); return false; } return true; } let reg = /^\d+$/; if (!expireDays) { this.setState({errorInfo: gettext('Please enter days')}); return false; } if (!reg.test(expireDays)) { this.setState({errorInfo: gettext('Please enter a non-negative integer')}); return false; } this.setState({expireDays: parseInt(expireDays)}); } return true; } onExpireChecked = (e) => { this.setState({isExpireChecked: e.target.checked}); } setExp = (e) => { this.setState({ setExp: e.target.value }); } disabledDate = (current) => { if (!current) { // allow empty select return false; } if (this.isExpireDaysNoLimit) { return current.isBefore(moment(), 'day'); } const startDay = moment().add(uploadLinkExpireDaysMin, 'days'); const endDay = moment().add(uploadLinkExpireDaysMax, 'days'); if (uploadLinkExpireDaysMin !== 0 && uploadLinkExpireDaysMax !== 0) { return current.isBefore(startDay, 'day') || current.isAfter(endDay, 'day'); } else if (uploadLinkExpireDaysMin !== 0 && uploadLinkExpireDaysMax === 0) { return current.isBefore(startDay, 'day'); } else if (uploadLinkExpireDaysMin === 0 && uploadLinkExpireDaysMax !== 0) { return current.isBefore(moment(), 'day') || current.isAfter(endDay, 'day'); } } onExpDateChanged = (value) => { this.setState({ expDate: value }); } onExpireDaysChanged = (e) => { let day = e.target.value.trim(); this.setState({expireDays: day}); } onCopyUploadLink = () => { let uploadLink = this.state.sharedUploadInfo.link; copy(uploadLink); toaster.success(gettext('Upload link is copied to the clipboard.')); this.props.closeShareDialog(); } deleteUploadLink = () => { let sharedUploadInfo = this.state.sharedUploadInfo; seafileAPI.deleteUploadLink(sharedUploadInfo.token).then(() => { this.setState({ showPasswordInput: shareLinkForceUsePassword ? true : false, expireDays: this.defaultExpireDays, isExpireChecked: !this.isExpireDaysNoLimit, password: '', passwordnew: '', sharedUploadInfo: null, }); }).catch(error => { let errMessage = Utils.getErrorMsg(error); toaster.danger(errMessage); }); } toggleSendLink = () => { this.setState({ isSendLinkShown: !this.state.isSendLinkShown }); } render() { const { isSendLinkShown } = this.state; let passwordLengthTip = gettext('(at least {passwordLength} characters and has {shareLinkPasswordStrengthLevel} of the following: num, upper letter, lower letter and other symbols)'); passwordLengthTip = passwordLengthTip.replace('{passwordLength}', shareLinkPasswordMinLength) .replace('{shareLinkPasswordStrengthLevel}', shareLinkPasswordStrengthLevel); if (this.state.sharedUploadInfo) { let sharedUploadInfo = this.state.sharedUploadInfo; return (