2020-07-21 10:22:45 +00:00
import React , { Fragment } from 'react' ;
2018-12-06 03:28:16 +00:00
import PropTypes from 'prop-types' ;
2019-01-04 10:11:13 +00:00
import copy from 'copy-to-clipboard' ;
2019-07-02 03:06:39 +00:00
import moment from 'moment' ;
2020-07-21 10:22:45 +00:00
import { Button , Form , FormGroup , Label , Input , InputGroup , InputGroupAddon , InputGroupText , Alert , FormText } from 'reactstrap' ;
2021-08-12 07:02:03 +00:00
import { gettext , shareLinkForceUsePassword , shareLinkPasswordMinLength , shareLinkPasswordStrengthLevel , canSendShareLinkEmail , uploadLinkExpireDaysMin , uploadLinkExpireDaysMax , uploadLinkExpireDaysDefault } from '../../utils/constants' ;
2019-01-04 10:11:13 +00:00
import { seafileAPI } from '../../utils/seafile-api' ;
2019-05-06 08:08:34 +00:00
import { Utils } from '../../utils/utils' ;
2019-08-21 08:36:55 +00:00
import UploadLink from '../../models/upload-link' ;
2019-01-04 10:11:13 +00:00
import toaster from '../toast' ;
2019-06-27 06:08:10 +00:00
import SendLink from '../send-link' ;
2020-05-21 03:32:02 +00:00
import DateTimePicker from '../date-and-time-picker' ;
2021-01-22 10:18:13 +00:00
import SharedLink from '../shared-link' ;
2018-12-06 03:28:16 +00:00
const propTypes = {
itemPath : PropTypes . string . isRequired ,
2019-01-05 12:40:41 +00:00
repoID : PropTypes . string . isRequired ,
closeShareDialog : PropTypes . func . isRequired ,
2018-12-06 03:28:16 +00:00
} ;
2020-05-21 03:32:02 +00:00
const inputWidth = Utils . isDesktop ( ) ? 250 : 210 ;
2018-12-06 03:28:16 +00:00
class GenerateUploadLink extends React . Component {
constructor ( props ) {
super ( props ) ;
2020-07-21 10:22:45 +00:00
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 ;
2018-12-06 03:28:16 +00:00
this . state = {
2021-08-12 07:02:03 +00:00
showPasswordInput : shareLinkForceUsePassword ? true : false ,
2018-12-06 03:28:16 +00:00
passwordVisible : false ,
password : '' ,
2021-08-12 07:02:03 +00:00
passwordnew : '' ,
2019-01-04 10:11:13 +00:00
sharedUploadInfo : null ,
2019-07-02 03:06:39 +00:00
isSendLinkShown : false ,
2020-07-21 10:22:45 +00:00
isExpireChecked : ! this . isExpireDaysNoLimit ,
2020-05-21 03:32:02 +00:00
setExp : 'by-days' ,
2020-07-21 10:22:45 +00:00
expireDays : this . defaultExpireDays ,
2020-05-21 03:32:02 +00:00
expDate : null
2018-12-06 03:28:16 +00:00
} ;
}
componentDidMount ( ) {
this . getUploadLink ( ) ;
}
getUploadLink = ( ) => {
let path = this . props . itemPath ;
2020-11-02 05:56:35 +00:00
let repoID = this . props . repoID ;
2020-01-11 05:29:04 +00:00
seafileAPI . getUploadLink ( repoID , path ) . then ( ( res ) => {
2018-12-06 03:28:16 +00:00
if ( res . data . length !== 0 ) {
2019-08-21 08:36:55 +00:00
let sharedUploadInfo = new UploadLink ( res . data [ 0 ] ) ;
2019-01-04 10:11:13 +00:00
this . setState ( { sharedUploadInfo : sharedUploadInfo } ) ;
2018-12-06 03:28:16 +00:00
}
2019-05-12 07:05:53 +00:00
} ) . catch ( ( err ) => {
2019-12-19 05:44:30 +00:00
let errMsg = Utils . getErrorMsg ( err , true ) ;
if ( ! err . response || err . response . status !== 403 ) {
toaster . danger ( errMsg ) ;
2019-05-12 07:05:53 +00:00
}
2019-12-19 05:44:30 +00:00
this . props . closeShareDialog ( ) ;
2018-12-06 03:28:16 +00:00
} ) ;
}
addPassword = ( ) => {
this . setState ( {
showPasswordInput : ! this . state . showPasswordInput ,
password : '' ,
2021-08-12 07:02:03 +00:00
passwordnew : '' ,
2018-12-06 03:28:16 +00:00
errorInfo : ''
} ) ;
}
togglePasswordVisible = ( ) => {
this . setState ( {
passwordVisible : ! this . state . passwordVisible
} ) ;
}
generatePassword = ( ) => {
2019-05-06 08:08:34 +00:00
let val = Utils . generatePassword ( shareLinkPasswordMinLength ) ;
2018-12-06 03:28:16 +00:00
this . setState ( {
password : val ,
passwordnew : val
} ) ;
}
inputPassword = ( e ) => {
this . setState ( {
password : e . target . value
} ) ;
}
inputPasswordNew = ( e ) => {
this . setState ( {
passwordnew : e . target . value
} ) ;
}
generateUploadLink = ( ) => {
2019-07-02 03:06:39 +00:00
let isValid = this . validateParamsInput ( ) ;
if ( isValid ) {
2020-05-21 03:32:02 +00:00
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 ) => {
2019-08-21 08:36:55 +00:00
let sharedUploadInfo = new UploadLink ( res . data ) ;
2020-11-02 05:56:35 +00:00
this . setState ( { sharedUploadInfo : sharedUploadInfo } ) ;
2019-07-16 02:01:09 +00:00
} ) . catch ( error => {
let errMessage = Utils . getErrorMsg ( error ) ;
toaster . danger ( errMessage ) ;
2018-12-06 03:28:16 +00:00
} ) ;
}
}
2019-07-02 03:06:39 +00:00
validateParamsInput = ( ) => {
2020-05-21 03:32:02 +00:00
let { showPasswordInput , password , passwordnew , isExpireChecked , setExp , expireDays , expDate } = this . state ;
2019-07-02 03:06:39 +00:00
// check password params
if ( showPasswordInput ) {
if ( password . length === 0 ) {
2019-07-04 10:14:13 +00:00
this . setState ( { errorInfo : gettext ( 'Please enter password' ) } ) ;
2019-07-02 03:06:39 +00:00
return false ;
}
if ( password . length < shareLinkPasswordMinLength ) {
2019-07-04 10:14:13 +00:00
this . setState ( { errorInfo : gettext ( 'Password is too short' ) } ) ;
2019-07-02 03:06:39 +00:00
return false ;
}
if ( password !== passwordnew ) {
2019-07-04 10:14:13 +00:00
this . setState ( { errorInfo : gettext ( 'Passwords don\'t match' ) } ) ;
2019-07-02 03:06:39 +00:00
return false ;
}
2021-08-12 07:02:03 +00:00
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 ;
}
2019-07-02 03:06:39 +00:00
}
if ( isExpireChecked ) {
2020-05-21 03:32:02 +00:00
if ( setExp == 'by-date' ) {
if ( ! expDate ) {
this . setState ( { errorInfo : 'Please select an expiration time' } ) ;
return false ;
}
return true ;
}
let reg = /^\d+$/ ;
2019-07-02 03:06:39 +00:00
if ( ! expireDays ) {
2019-07-04 10:14:13 +00:00
this . setState ( { errorInfo : gettext ( 'Please enter days' ) } ) ;
2019-07-02 03:06:39 +00:00
return false ;
}
if ( ! reg . test ( expireDays ) ) {
2019-07-04 10:14:13 +00:00
this . setState ( { errorInfo : gettext ( 'Please enter a non-negative integer' ) } ) ;
2019-07-02 03:06:39 +00:00
return false ;
}
this . setState ( { expireDays : parseInt ( expireDays ) } ) ;
}
return true ;
}
onExpireChecked = ( e ) => {
this . setState ( { isExpireChecked : e . target . checked } ) ;
}
2020-05-21 03:32:02 +00:00
setExp = ( e ) => {
this . setState ( {
setExp : e . target . value
} ) ;
}
disabledDate = ( current ) => {
if ( ! current ) {
// allow empty select
return false ;
}
2020-07-21 10:22:45 +00:00
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' ) ;
}
2020-05-21 03:32:02 +00:00
}
onExpDateChanged = ( value ) => {
this . setState ( {
expDate : value
} ) ;
}
2019-07-02 03:06:39 +00:00
onExpireDaysChanged = ( e ) => {
let day = e . target . value . trim ( ) ;
this . setState ( { expireDays : day } ) ;
}
2019-01-04 10:11:13 +00:00
onCopyUploadLink = ( ) => {
let uploadLink = this . state . sharedUploadInfo . link ;
copy ( uploadLink ) ;
toaster . success ( gettext ( 'Upload link is copied to the clipboard.' ) ) ;
2019-01-05 12:40:41 +00:00
this . props . closeShareDialog ( ) ;
2019-01-04 10:11:13 +00:00
}
2018-12-06 03:28:16 +00:00
deleteUploadLink = ( ) => {
2019-01-31 09:37:02 +00:00
let sharedUploadInfo = this . state . sharedUploadInfo ;
2019-01-04 10:11:13 +00:00
seafileAPI . deleteUploadLink ( sharedUploadInfo . token ) . then ( ( ) => {
2018-12-06 03:28:16 +00:00
this . setState ( {
2021-08-12 07:02:03 +00:00
showPasswordInput : shareLinkForceUsePassword ? true : false ,
2020-07-21 10:22:45 +00:00
expireDays : this . defaultExpireDays ,
isExpireChecked : ! this . isExpireDaysNoLimit ,
2018-12-06 03:28:16 +00:00
password : '' ,
passwordnew : '' ,
2019-01-04 10:11:13 +00:00
sharedUploadInfo : null ,
2018-12-06 03:28:16 +00:00
} ) ;
2019-07-16 02:01:09 +00:00
} ) . catch ( error => {
let errMessage = Utils . getErrorMsg ( error ) ;
toaster . danger ( errMessage ) ;
2018-12-06 03:28:16 +00:00
} ) ;
}
2019-06-27 06:08:10 +00:00
toggleSendLink = ( ) => {
this . setState ( {
isSendLinkShown : ! this . state . isSendLinkShown
} ) ;
}
2018-12-06 03:28:16 +00:00
render ( ) {
2019-05-06 08:08:34 +00:00
2019-06-27 06:08:10 +00:00
const { isSendLinkShown } = this . state ;
2021-08-12 07:02:03 +00:00
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 ) ;
2019-01-04 10:11:13 +00:00
if ( this . state . sharedUploadInfo ) {
let sharedUploadInfo = this . state . sharedUploadInfo ;
2018-12-06 03:28:16 +00:00
return (
2019-01-04 10:11:13 +00:00
< div >
< Form className = "mb-4" >
< FormGroup >
< dt className = "text-secondary font-weight-normal" > { gettext ( 'Upload Link:' ) } < / d t >
2021-01-22 10:18:13 +00:00
< dd >
< SharedLink
link = { sharedUploadInfo . link }
linkExpired = { sharedUploadInfo . is _expired }
copyLink = { this . onCopyUploadLink }
/ >
2019-01-04 10:11:13 +00:00
< / d d >
< / F o r m G r o u p >
2019-07-02 03:06:39 +00:00
{ sharedUploadInfo . expire _date && (
< FormGroup className = "mb-0" >
< dt className = "text-secondary font-weight-normal" > { gettext ( 'Expiration Date:' ) } < / d t >
2020-04-28 05:09:05 +00:00
< dd > { moment ( sharedUploadInfo . expire _date ) . format ( 'YYYY-MM-DD HH:mm:ss' ) } < / d d >
2019-07-02 03:06:39 +00:00
< / F o r m G r o u p >
) }
2019-01-04 10:11:13 +00:00
< / F o r m >
2019-06-27 06:08:10 +00:00
{ canSendShareLinkEmail && ! isSendLinkShown && < Button onClick = { this . toggleSendLink } className = "mr-2" > { gettext ( 'Send' ) } < / B u t t o n > }
{ ! isSendLinkShown && < Button onClick = { this . deleteUploadLink } > { gettext ( 'Delete' ) } < / B u t t o n > }
{ isSendLinkShown &&
< SendLink
linkType = 'uploadLink'
token = { sharedUploadInfo . token }
toggleSendLink = { this . toggleSendLink }
closeShareDialog = { this . props . closeShareDialog }
/ >
}
2019-01-04 10:11:13 +00:00
< / d i v >
2018-12-06 03:28:16 +00:00
) ;
}
return (
< Form className = "generate-upload-link" >
< FormGroup check >
< Label check >
2021-08-12 07:02:03 +00:00
{ shareLinkForceUsePassword ? (
< Label check >
< Input type = "checkbox" checked readOnly disabled / >
< span > { gettext ( 'Add password protection' ) } < / s p a n >
< / L a b e l >
) : (
< Label check >
< Input type = "checkbox" onChange = { this . addPassword } / >
< span > { gettext ( 'Add password protection' ) } < / s p a n >
< / L a b e l >
) }
2018-12-06 03:28:16 +00:00
< / L a b e l >
2020-05-21 03:32:02 +00:00
{ this . state . showPasswordInput &&
< div className = "ml-4" >
< FormGroup >
< Label for = "passwd" > { gettext ( 'Password' ) } < / L a b e l >
< span className = "tip" > { passwordLengthTip } < / s p a n >
< InputGroup style = { { width : inputWidth } } >
< Input id = "passwd" type = { this . state . passwordVisible ? 'text' : 'password' } value = { this . state . password || '' } onChange = { this . inputPassword } / >
< InputGroupAddon addonType = "append" >
< Button onClick = { this . togglePasswordVisible } > < i className = { ` link-operation-icon fas ${ this . state . passwordVisible ? 'fa-eye' : 'fa-eye-slash' } ` } > < / i > < / B u t t o n >
< Button onClick = { this . generatePassword } > < i className = "link-operation-icon fas fa-magic" > < / i > < / B u t t o n >
< / I n p u t G r o u p A d d o n >
< / I n p u t G r o u p >
< / F o r m G r o u p >
< FormGroup >
< Label for = "passwd-again" > { gettext ( 'Password again' ) } < / L a b e l >
< Input id = "passwd-again" style = { { width : inputWidth } } type = { this . state . passwordVisible ? 'text' : 'password' } value = { this . state . passwordnew || '' } onChange = { this . inputPasswordNew } / >
< / F o r m G r o u p >
< / d i v >
}
2018-12-06 03:28:16 +00:00
< / F o r m G r o u p >
2019-07-02 03:06:39 +00:00
< FormGroup check >
< Label check >
2020-11-02 05:56:35 +00:00
{ this . isExpireDaysNoLimit ? (
< Input type = "checkbox" onChange = { this . onExpireChecked } / >
) : (
< Input type = "checkbox" checked readOnly disabled / >
) }
2020-05-21 03:32:02 +00:00
< span > { gettext ( 'Add auto expiration' ) } < / s p a n >
2019-07-02 03:06:39 +00:00
< / L a b e l >
2020-05-21 03:32:02 +00:00
{ this . state . isExpireChecked &&
< div className = "ml-4" >
< FormGroup check >
< Label check >
< Input type = "radio" name = "set-exp" value = "by-days" checked = { this . state . setExp == 'by-days' } onChange = { this . setExp } className = "mr-1" / >
< span > { gettext ( 'Expiration days' ) } < / s p a n >
< / L a b e l >
{ this . state . setExp == 'by-days' && (
2020-07-21 10:22:45 +00:00
< Fragment >
< InputGroup style = { { width : inputWidth } } >
< Input type = "text" value = { this . state . expireDays } onChange = { this . onExpireDaysChanged } / >
< InputGroupAddon addonType = "append" >
< InputGroupText > { gettext ( 'days' ) } < / I n p u t G r o u p T e x t >
< / I n p u t G r o u p A d d o n >
< / I n p u t G r o u p >
{ ! this . state . isExpireDaysNoLimit && (
< FormText color = "muted" > { this . expirationLimitTip } < / F o r m T e x t >
) }
< / F r a g m e n t >
2020-05-21 03:32:02 +00:00
) }
< / F o r m G r o u p >
< FormGroup check >
< Label check >
< Input type = "radio" name = "set-exp" value = "by-date" checked = { this . state . setExp == 'by-date' } onChange = { this . setExp } className = "mr-1" / >
< span > { gettext ( 'Expiration time' ) } < / s p a n >
< / L a b e l >
{ this . state . setExp == 'by-date' && (
< DateTimePicker
inputWidth = { inputWidth }
disabledDate = { this . disabledDate }
value = { this . state . expDate }
onChange = { this . onExpDateChanged }
/ >
) }
< / F o r m G r o u p >
< / d i v >
}
2019-07-02 03:06:39 +00:00
< / F o r m G r o u p >
2019-02-12 03:16:49 +00:00
{ this . state . errorInfo && < Alert color = "danger" className = "mt-2" > { this . state . errorInfo } < / A l e r t > }
2018-12-06 03:28:16 +00:00
< Button className = "generate-link-btn" onClick = { this . generateUploadLink } > { gettext ( 'Generate' ) } < / B u t t o n >
< / F o r m >
) ;
}
}
GenerateUploadLink . propTypes = propTypes ;
export default GenerateUploadLink ;