2023-03-28 16:29:34 +08:00
import React , { Fragment } from 'react' ;
2018-12-06 11:28:16 +08:00
import PropTypes from 'prop-types' ;
2019-01-04 18:11:13 +08:00
import moment from 'moment' ;
import copy from 'copy-to-clipboard' ;
2022-03-10 14:14:15 +08:00
import { Button , Form , FormGroup , Label , Input , InputGroup , InputGroupAddon , Alert } from 'reactstrap' ;
2021-08-12 15:02:03 +08:00
import { isPro , gettext , shareLinkExpireDaysMin , shareLinkExpireDaysMax , shareLinkExpireDaysDefault , shareLinkForceUsePassword , shareLinkPasswordMinLength , shareLinkPasswordStrengthLevel , canSendShareLinkEmail } from '../../utils/constants' ;
2020-01-02 12:05:50 +08:00
import ShareLinkPermissionEditor from '../../components/select-editor/share-link-permission-editor' ;
2018-12-06 11:28:16 +08:00
import { seafileAPI } from '../../utils/seafile-api' ;
2019-05-06 16:08:34 +08:00
import { Utils } from '../../utils/utils' ;
2019-08-20 12:00:58 +08:00
import ShareLink from '../../models/share-link' ;
2019-01-04 18:11:13 +08:00
import toaster from '../toast' ;
2019-05-13 20:01:53 +08:00
import Loading from '../loading' ;
2019-06-27 14:08:10 +08:00
import SendLink from '../send-link' ;
2021-01-22 18:18:13 +08:00
import SharedLink from '../shared-link' ;
2022-03-10 14:14:15 +08:00
import SetLinkExpiration from '../set-link-expiration' ;
2018-12-06 11:28:16 +08:00
const propTypes = {
itemPath : PropTypes . string . isRequired ,
2019-01-05 20:40:41 +08:00
repoID : PropTypes . string . isRequired ,
closeShareDialog : PropTypes . func . isRequired ,
2021-09-13 10:37:07 +08:00
userPerm : PropTypes . string ,
2023-03-28 16:29:34 +08:00
itemType : PropTypes . string
2018-12-06 11:28:16 +08:00
} ;
2020-05-21 11:32:02 +08:00
const inputWidth = Utils . isDesktop ( ) ? 250 : 210 ;
2023-03-30 17:31:31 +08:00
class ShareLinkPanel extends React . Component {
2018-12-06 11:28:16 +08:00
constructor ( props ) {
super ( props ) ;
2019-07-09 11:06:49 +08:00
2020-05-21 11:32:02 +08:00
this . isExpireDaysNoLimit = ( shareLinkExpireDaysMin === 0 && shareLinkExpireDaysMax === 0 && shareLinkExpireDaysDefault == 0 ) ;
2019-07-09 11:06:49 +08:00
this . defaultExpireDays = this . isExpireDaysNoLimit ? '' : shareLinkExpireDaysDefault ;
2019-08-27 12:04:03 +08:00
2018-12-06 11:28:16 +08:00
this . state = {
2021-08-12 15:02:03 +08:00
isShowPasswordInput : shareLinkForceUsePassword ? true : false ,
2018-12-14 15:09:07 +08:00
isPasswordVisible : false ,
2019-07-09 11:06:49 +08:00
isExpireChecked : ! this . isExpireDaysNoLimit ,
2022-03-10 14:14:15 +08:00
expType : 'by-days' ,
2020-05-21 11:32:02 +08:00
expireDays : this . defaultExpireDays ,
expDate : null ,
2018-12-06 11:28:16 +08:00
password : '' ,
passwdnew : '' ,
2019-01-04 18:11:13 +08:00
errorInfo : '' ,
sharedLinkInfo : null ,
2023-03-28 16:29:34 +08:00
shareLinks : [ ] ,
2019-05-13 20:01:53 +08:00
isLoading : true ,
2019-10-12 14:54:25 +08:00
permissionOptions : [ ] ,
currentPermission : '' ,
2018-12-06 11:28:16 +08:00
} ;
}
componentDidMount ( ) {
2020-01-02 12:05:50 +08:00
let path = this . props . itemPath ;
2018-12-06 11:28:16 +08:00
let repoID = this . props . repoID ;
seafileAPI . getShareLink ( repoID , path ) . then ( ( res ) => {
2023-03-28 16:29:34 +08:00
this . setState ( {
isLoading : false ,
shareLinks : res . data . map ( item => new ShareLink ( item ) )
} ) ;
2019-07-16 10:01:09 +08:00
} ) . catch ( error => {
let errMessage = Utils . getErrorMsg ( error ) ;
toaster . danger ( errMessage ) ;
2018-12-06 11:28:16 +08:00
} ) ;
2019-08-27 12:04:03 +08:00
2019-10-12 14:54:25 +08:00
if ( isPro ) {
2020-10-20 16:24:58 +08:00
const { itemType , userPerm } = this . props ;
if ( itemType == 'library' ) {
let permissionOptions = Utils . getShareLinkPermissionList ( itemType , userPerm , path ) ;
2019-10-12 14:54:25 +08:00
this . setState ( {
permissionOptions : permissionOptions ,
currentPermission : permissionOptions [ 0 ] ,
} ) ;
} else {
let getDirentInfoAPI ;
if ( this . props . itemType === 'file' ) {
getDirentInfoAPI = seafileAPI . getFileInfo ( repoID , path ) ;
} else if ( this . props . itemType === 'dir' ) {
getDirentInfoAPI = seafileAPI . getDirInfo ( repoID , path ) ;
2019-07-05 15:28:13 +08:00
}
2019-10-12 14:54:25 +08:00
getDirentInfoAPI . then ( ( res ) => {
2019-12-03 13:52:52 +08:00
let canEdit = res . data . can _edit ;
2019-10-12 14:54:25 +08:00
let permission = res . data . permission ;
2019-12-03 13:52:52 +08:00
let permissionOptions = Utils . getShareLinkPermissionList ( this . props . itemType , permission , path , canEdit ) ;
2019-10-12 14:54:25 +08:00
this . setState ( {
permissionOptions : permissionOptions ,
currentPermission : permissionOptions [ 0 ] ,
} ) ;
} ) . catch ( error => {
let errMessage = Utils . getErrorMsg ( error ) ;
toaster . danger ( errMessage ) ;
} ) ;
}
2019-07-05 15:28:13 +08:00
}
2019-05-13 20:01:53 +08:00
}
2018-12-06 11:28:16 +08:00
2022-03-10 14:14:15 +08:00
setExpType = ( e ) => {
2020-05-21 11:32:02 +08:00
this . setState ( {
2022-03-10 14:14:15 +08:00
expType : e . target . value
2020-05-21 11:32:02 +08:00
} ) ;
}
onExpDateChanged = ( value ) => {
this . setState ( {
expDate : value
} ) ;
}
2018-12-14 15:09:07 +08:00
onPasswordInputChecked = ( ) => {
2018-12-06 11:28:16 +08:00
this . setState ( {
2018-12-14 15:09:07 +08:00
isShowPasswordInput : ! this . state . isShowPasswordInput ,
2018-12-06 11:28:16 +08:00
password : '' ,
passwdnew : '' ,
errorInfo : ''
} ) ;
}
togglePasswordVisible = ( ) => {
this . setState ( {
2018-12-14 15:09:07 +08:00
isPasswordVisible : ! this . state . isPasswordVisible
2018-12-06 11:28:16 +08:00
} ) ;
}
generatePassword = ( ) => {
2019-05-06 16:08:34 +08:00
let val = Utils . generatePassword ( shareLinkPasswordMinLength ) ;
2018-12-06 11:28:16 +08:00
this . setState ( {
password : val ,
2018-12-14 15:09:07 +08:00
passwdnew : val
2018-12-06 11:28:16 +08:00
} ) ;
}
inputPassword = ( e ) => {
2018-12-14 15:21:48 +08:00
let passwd = e . target . value . trim ( ) ;
this . setState ( { password : passwd } ) ;
2018-12-06 11:28:16 +08:00
}
inputPasswordNew = ( e ) => {
2018-12-14 15:21:48 +08:00
let passwd = e . target . value . trim ( ) ;
this . setState ( { passwdnew : passwd } ) ;
2018-12-06 11:28:16 +08:00
}
2019-08-27 12:04:03 +08:00
setPermission = ( e ) => {
this . setState ( { currentPermission : e . target . value } ) ;
2018-12-06 11:28:16 +08:00
}
generateShareLink = ( ) => {
2018-12-14 15:09:07 +08:00
let isValid = this . validateParamsInput ( ) ;
if ( isValid ) {
2018-12-14 15:49:24 +08:00
this . setState ( { errorInfo : '' } ) ;
2018-12-14 15:09:07 +08:00
let { itemPath , repoID } = this . props ;
2022-03-10 14:14:15 +08:00
let { password , isExpireChecked , expType , expireDays , expDate } = this . state ;
2019-08-27 12:04:03 +08:00
let permissions ;
if ( isPro ) {
const permissionDetails = Utils . getShareLinkPermissionObject ( this . state . currentPermission ) . permissionDetails ;
permissions = JSON . stringify ( permissionDetails ) ;
}
2020-11-02 13:56:35 +08:00
let expirationTime = '' ;
2020-05-21 11:32:02 +08:00
if ( isExpireChecked ) {
2022-03-10 14:14:15 +08:00
if ( expType == 'by-days' ) {
2020-05-21 11:32:02 +08:00
expirationTime = moment ( ) . add ( parseInt ( expireDays ) , 'days' ) . format ( ) ;
} else {
expirationTime = expDate . format ( ) ;
}
}
2023-03-28 16:29:34 +08:00
seafileAPI . createMultiShareLink ( repoID , itemPath , password , expirationTime , permissions ) . then ( ( res ) => {
const links = this . state . shareLinks ;
2023-03-30 17:31:31 +08:00
const newLink = new ShareLink ( res . data ) ;
links . unshift ( newLink ) ;
2023-03-28 16:29:34 +08:00
this . setState ( {
password : '' ,
passwdnew : '' ,
isShowPasswordInput : shareLinkForceUsePassword ? true : false ,
expireDays : this . defaultExpireDays ,
expDate : null ,
isExpireChecked : ! this . isExpireDaysNoLimit ,
errorInfo : '' ,
2023-03-30 17:31:31 +08:00
sharedLinkInfo : newLink ,
2023-03-28 16:29:34 +08:00
shareLinks : links
} ) ;
2019-07-04 16:22:50 +08:00
} ) . catch ( ( error ) => {
2019-07-16 10:01:09 +08:00
let errMessage = Utils . getErrorMsg ( error ) ;
toaster . danger ( errMessage ) ;
2018-12-06 11:28:16 +08:00
} ) ;
}
}
2018-12-14 15:09:07 +08:00
onExpireChecked = ( e ) => {
this . setState ( { isExpireChecked : e . target . checked } ) ;
}
onExpireDaysChanged = ( e ) => {
2018-12-14 15:21:48 +08:00
let day = e . target . value . trim ( ) ;
this . setState ( { expireDays : day } ) ;
2018-12-14 15:09:07 +08:00
}
validateParamsInput = ( ) => {
2022-03-10 14:14:15 +08:00
let { isShowPasswordInput , password , passwdnew , isExpireChecked , expType , expireDays , expDate } = this . state ;
2020-05-21 11:32:02 +08:00
2018-12-14 15:09:07 +08:00
// validate password
if ( isShowPasswordInput ) {
if ( password . length === 0 ) {
2022-02-17 15:30:06 +08:00
this . setState ( { errorInfo : gettext ( 'Please enter a password.' ) } ) ;
2018-12-14 15:09:07 +08:00
return false ;
}
2019-05-06 16:08:34 +08:00
if ( password . length < shareLinkPasswordMinLength ) {
2022-02-17 15:30:06 +08:00
this . setState ( { errorInfo : gettext ( 'The password is too short.' ) } ) ;
2018-12-14 15:09:07 +08:00
return false ;
}
if ( password !== passwdnew ) {
2022-02-17 15:30:06 +08:00
this . setState ( { errorInfo : gettext ( 'Passwords don\'t match' ) } ) ;
2018-12-14 15:09:07 +08:00
return false ;
}
2021-08-12 15:02:03 +08:00
if ( Utils . getStrengthLevel ( password ) < shareLinkPasswordStrengthLevel ) {
2022-02-17 15:30:06 +08:00
this . setState ( { errorInfo : gettext ( 'The password is too weak. It should include at least {passwordStrengthLevel} of the following: number, upper letter, lower letter and other symbols.' ) . replace ( '{passwordStrengthLevel}' , shareLinkPasswordStrengthLevel ) } ) ;
2021-08-12 15:02:03 +08:00
return false ;
}
2018-12-06 11:28:16 +08:00
}
2020-05-21 11:32:02 +08:00
if ( isExpireChecked ) {
2022-03-10 14:14:15 +08:00
if ( expType == 'by-date' ) {
2020-05-21 11:32:02 +08:00
if ( ! expDate ) {
2022-02-17 15:46:03 +08:00
this . setState ( { errorInfo : gettext ( 'Please select an expiration time' ) } ) ;
2018-12-14 15:09:07 +08:00
return false ;
}
2020-05-21 11:32:02 +08:00
return true ;
2018-12-14 15:09:07 +08:00
}
2020-05-21 11:32:02 +08:00
// by days
let reg = /^\d+$/ ;
2018-12-14 15:09:07 +08:00
if ( ! expireDays ) {
2022-02-17 15:46:03 +08:00
this . setState ( { errorInfo : gettext ( 'Please enter days' ) } ) ;
2018-12-14 15:09:07 +08:00
return false ;
}
2019-05-29 10:43:30 +08:00
if ( ! reg . test ( expireDays ) ) {
2022-02-17 15:46:03 +08:00
this . setState ( { errorInfo : gettext ( 'Please enter a non-negative integer' ) } ) ;
2018-12-14 15:09:07 +08:00
return false ;
}
expireDays = parseInt ( expireDays ) ;
2020-05-21 11:32:02 +08:00
let minDays = shareLinkExpireDaysMin ;
let maxDays = shareLinkExpireDaysMax ;
2018-12-14 15:09:07 +08:00
2020-05-21 11:32:02 +08:00
if ( minDays !== 0 && maxDays == 0 ) {
2018-12-14 15:09:07 +08:00
if ( expireDays < minDays ) {
this . setState ( { errorInfo : 'Please enter valid days' } ) ;
return false ;
}
}
2020-01-02 12:05:50 +08:00
2018-12-14 15:09:07 +08:00
if ( minDays === 0 && maxDays !== 0 ) {
if ( expireDays > maxDays ) {
this . setState ( { errorInfo : 'Please enter valid days' } ) ;
return false ;
}
}
2020-01-02 12:05:50 +08:00
2018-12-14 15:09:07 +08:00
if ( minDays !== 0 && maxDays !== 0 ) {
2019-05-29 10:43:30 +08:00
if ( expireDays < minDays || expireDays > maxDays ) {
2018-12-14 15:09:07 +08:00
this . setState ( { errorInfo : 'Please enter valid days' } ) ;
return false ;
}
}
2020-05-21 11:32:02 +08:00
2018-12-14 15:09:07 +08:00
this . setState ( { expireDays : expireDays } ) ;
2018-12-06 11:28:16 +08:00
}
2018-12-14 15:09:07 +08:00
return true ;
2018-12-06 11:28:16 +08:00
}
2023-03-30 17:31:31 +08:00
showLinkDetails = ( link ) => {
this . setState ( {
sharedLinkInfo : link
2022-02-24 10:47:17 +08:00
} ) ;
}
2023-03-30 17:31:31 +08:00
updateLink = ( link ) => {
const { shareLinks } = this . state ;
this . setState ( {
sharedLinkInfo : link ,
shareLinks : shareLinks . map ( item => item . token == link . token ? link : item )
} ) ;
2020-01-02 12:05:50 +08:00
}
2023-03-30 17:31:31 +08:00
deleteLink = ( ) => {
2023-03-28 16:29:34 +08:00
const { sharedLinkInfo , shareLinks } = this . state ;
2023-03-30 17:31:31 +08:00
seafileAPI . deleteShareLink ( sharedLinkInfo . token ) . then ( ( ) => {
2023-03-28 16:29:34 +08:00
this . setState ( {
2023-03-30 17:31:31 +08:00
sharedLinkInfo : null ,
shareLinks : shareLinks . filter ( item => item . token !== sharedLinkInfo . token )
2023-03-28 16:29:34 +08:00
} ) ;
2023-03-30 17:31:31 +08:00
toaster . success ( gettext ( 'The link is deleted.' ) ) ;
2020-01-02 12:05:50 +08:00
} ) . catch ( ( error ) => {
let errMessage = Utils . getErrorMsg ( error ) ;
toaster . danger ( errMessage ) ;
} ) ;
}
2018-12-06 11:28:16 +08:00
render ( ) {
2019-05-13 20:01:53 +08:00
if ( this . state . isLoading ) {
return < Loading / > ;
}
2021-09-13 10:37:07 +08:00
const { userPerm } = this . props ;
const { isCustomPermission } = Utils . getUserPermission ( userPerm ) ;
2023-03-30 17:31:31 +08:00
const { shareLinks , permissionOptions , sharedLinkInfo } = this . state ;
2021-09-13 10:37:07 +08:00
2023-03-28 16:29:34 +08:00
if ( sharedLinkInfo ) {
2018-12-06 11:28:16 +08:00
return (
2023-03-30 17:31:31 +08:00
< LinkDetails
sharedLinkInfo = { sharedLinkInfo }
permissionOptions = { permissionOptions }
defaultExpireDays = { this . defaultExpireDays }
showLinkDetails = { this . showLinkDetails }
updateLink = { this . updateLink }
deleteLink = { this . deleteLink }
closeShareDialog = { this . props . closeShareDialog }
/ >
2018-12-06 11:28:16 +08:00
) ;
} else {
return (
2023-03-28 16:29:34 +08:00
< Fragment >
< Form className = "generate-share-link" >
< FormGroup check >
{ shareLinkForceUsePassword ? (
< Label check >
< Input type = "checkbox" checked readOnly disabled / >
< span > { gettext ( 'Add password protection' ) } < / s p a n >
< / L a b e l >
2020-11-02 13:56:35 +08:00
) : (
2023-03-28 16:29:34 +08:00
< Label check >
< Input type = "checkbox" checked = { this . state . isShowPasswordInput } onChange = { this . onPasswordInputChecked } / >
< span > { gettext ( 'Add password protection' ) } < / s p a n >
< / L a b e l >
2020-05-21 11:32:02 +08:00
) }
2023-03-28 16:29:34 +08:00
{ this . state . isShowPasswordInput &&
< div className = "ml-4" >
< FormGroup >
< Label for = "passwd" > { gettext ( 'Password' ) } < / L a b e l >
< span className = "tip" > { gettext ( '(at least {passwordMinLength} characters and includes {passwordStrengthLevel} of the following: number, upper letter, lower letter and other symbols)' ) . replace ( '{passwordMinLength}' , shareLinkPasswordMinLength ) . replace ( '{passwordStrengthLevel}' , shareLinkPasswordStrengthLevel ) } < / s p a n >
< InputGroup style = { { width : inputWidth } } >
< Input id = "passwd" type = { this . state . isPasswordVisible ? 'text' : 'password' } value = { this . state . password || '' } onChange = { this . inputPassword } / >
< InputGroupAddon addonType = "append" >
< Button onClick = { this . togglePasswordVisible } > < i className = { ` link-operation-icon fas ${ this . state . isPasswordVisible ? '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 . isPasswordVisible ? 'text' : 'password' } value = { this . state . passwdnew || '' } onChange = { this . inputPasswordNew } / >
< / F o r m G r o u p >
< / d i v >
}
< / F o r m G r o u p >
< FormGroup check >
< Label check >
{ this . isExpireDaysNoLimit ? (
< Input type = "checkbox" onChange = { this . onExpireChecked } / >
) : (
< Input type = "checkbox" checked readOnly disabled / >
) }
< span > { gettext ( 'Add auto expiration' ) } < / s p a n >
< / L a b e l >
{ this . state . isExpireChecked &&
2020-05-21 11:32:02 +08:00
< div className = "ml-4" >
2022-03-10 14:14:15 +08:00
< SetLinkExpiration
minDays = { shareLinkExpireDaysMin }
maxDays = { shareLinkExpireDaysMax }
defaultDays = { shareLinkExpireDaysDefault }
expType = { this . state . expType }
setExpType = { this . setExpType }
expireDays = { this . state . expireDays }
onExpireDaysChanged = { this . onExpireDaysChanged }
expDate = { this . state . expDate }
onExpDateChanged = { this . onExpDateChanged }
/ >
2020-05-21 11:32:02 +08:00
< / d i v >
2023-03-28 16:29:34 +08:00
}
2020-05-21 11:32:02 +08:00
< / F o r m G r o u p >
2023-03-28 16:29:34 +08:00
{ ( isPro && ! isCustomPermission ) && (
< FormGroup check >
< Label check >
< span > { gettext ( 'Set permission' ) } < / s p a n >
< / L a b e l >
{ this . state . permissionOptions . map ( ( item , index ) => {
return (
< FormGroup check className = "ml-4" key = { index } >
< Label check >
< Input type = "radio" name = "permission" value = { item } checked = { this . state . currentPermission == item } onChange = { this . setPermission } className = "mr-1" / >
{ Utils . getShareLinkPermissionObject ( item ) . text }
< / L a b e l >
< / F o r m G r o u p >
) ;
} ) }
< / F o r m G r o u p >
) }
{ this . state . errorInfo && < Alert color = "danger" className = "mt-2" > { gettext ( this . state . errorInfo ) } < / A l e r t > }
< Button onClick = { this . generateShareLink } className = "mt-2" > { gettext ( 'Generate' ) } < / B u t t o n >
< / F o r m >
{ shareLinks . length > 0 && (
2023-03-30 17:31:31 +08:00
< table className = "table-hover" >
2023-03-28 16:29:34 +08:00
< thead >
< tr >
< th width = "28%" > { gettext ( 'Link' ) } < / t h >
< th width = "30%" > { gettext ( 'Permission' ) } < / t h >
< th width = "30%" > { gettext ( 'Expiration' ) } < / t h >
< th width = "12%" > < / t h >
< / t r >
< / t h e a d >
< tbody >
{
shareLinks . map ( ( item , index ) => {
return (
< LinkItem
key = { index }
item = { item }
permissionOptions = { permissionOptions }
showLinkDetails = { this . showLinkDetails }
/ >
) ;
} )
}
< / t b o d y >
< / t a b l e >
2019-08-20 12:00:58 +08:00
) }
2023-03-28 16:29:34 +08:00
< / F r a g m e n t >
2018-12-06 11:28:16 +08:00
) ;
}
}
}
2023-03-28 16:29:34 +08:00
class LinkItem extends React . Component {
constructor ( props ) {
super ( props ) ;
2023-03-30 17:31:31 +08:00
this . state = {
isItemOpVisible : false
} ;
}
onMouseOver = ( ) => {
this . setState ( {
isItemOpVisible : true
} ) ;
}
onMouseOut = ( ) => {
this . setState ( {
isItemOpVisible : false
} ) ;
2023-03-28 16:29:34 +08:00
}
cutLink = ( link ) => {
let length = link . length ;
return link . slice ( 0 , 9 ) + '...' + link . slice ( length - 5 ) ;
}
2023-03-30 17:31:31 +08:00
viewDetails = ( e ) => {
e . preventDefault ( ) ;
2023-03-28 16:29:34 +08:00
this . props . showLinkDetails ( this . props . item ) ;
}
render ( ) {
2023-03-30 17:31:31 +08:00
const { isItemOpVisible } = this . state ;
2023-03-28 16:29:34 +08:00
const { item , permissionOptions } = this . props ;
const { permissions , link , expire _date } = item ;
const currentPermission = Utils . getShareLinkPermissionStr ( permissions ) ;
return (
2023-03-30 17:31:31 +08:00
< tr onMouseOver = { this . onMouseOver } onMouseOut = { this . onMouseOut } >
2023-03-28 16:29:34 +08:00
< td > { this . cutLink ( link ) } < / t d >
< td >
{ ( isPro && permissions ) && (
< ShareLinkPermissionEditor
isTextMode = { true }
isEditIconShow = { false }
currentPermission = { currentPermission }
permissionOptions = { permissionOptions }
onPermissionChanged = { ( ) => { } }
/ >
) }
< / t d >
< td >
{ expire _date ? moment ( expire _date ) . format ( 'YYYY-MM-DD HH:mm' ) : '--' }
< / t d >
< td >
2023-03-30 17:31:31 +08:00
< a href = "#" role = "button" onClick = { this . viewDetails } className = { isItemOpVisible ? '' : 'invisible' } > { gettext ( 'Details' ) } < / a >
2023-03-28 16:29:34 +08:00
< / t d >
< / t r >
) ;
}
}
LinkItem . propTypes = {
item : PropTypes . object . isRequired ,
permissionOptions : PropTypes . array ,
showLinkDetails : PropTypes . func . isRequired
} ;
2023-03-30 17:31:31 +08:00
class LinkDetails extends React . Component {
constructor ( props ) {
super ( props ) ;
this . state = {
storedPasswordVisible : false ,
isEditingExpiration : false ,
isExpirationEditIconShow : false ,
expType : 'by-days' ,
expireDays : this . props . defaultExpireDays ,
expDate : null ,
isOpIconShown : false ,
isNoticeMessageShow : false ,
isSendLinkShown : false
} ;
}
onCopySharedLink = ( ) => {
const { sharedLinkInfo } = this . props ;
copy ( sharedLinkInfo . link ) ;
toaster . success ( gettext ( 'Share link is copied to the clipboard.' ) ) ;
this . props . closeShareDialog ( ) ;
}
onCopyDownloadLink = ( ) => {
const { sharedLinkInfo } = this . props ;
copy ( ` ${ sharedLinkInfo . link } ?dl=1 ` ) ;
toaster . success ( gettext ( 'Direct download link is copied to the clipboard.' ) ) ;
this . props . closeShareDialog ( ) ;
}
toggleStoredPasswordVisible = ( ) => {
this . setState ( {
storedPasswordVisible : ! this . state . storedPasswordVisible
} ) ;
}
handleMouseOverExpirationEditIcon = ( ) => {
this . setState ( { isExpirationEditIconShow : true } ) ;
}
handleMouseOutExpirationEditIcon = ( ) => {
this . setState ( { isExpirationEditIconShow : false } ) ;
}
editingExpirationToggle = ( ) => {
this . setState ( { isEditingExpiration : ! this . state . isEditingExpiration } ) ;
}
setExpType = ( e ) => {
this . setState ( {
expType : e . target . value
} ) ;
}
onExpDateChanged = ( value ) => {
this . setState ( {
expDate : value
} ) ;
}
onExpireDaysChanged = ( e ) => {
let day = e . target . value . trim ( ) ;
this . setState ( { expireDays : day } ) ;
}
updateExpiration = ( ) => {
const { sharedLinkInfo } = this . props ;
const { expType , expireDays , expDate } = this . state ;
let expirationTime = '' ;
if ( expType == 'by-days' ) {
expirationTime = moment ( ) . add ( parseInt ( expireDays ) , 'days' ) . format ( ) ;
} else {
expirationTime = expDate . format ( ) ;
}
seafileAPI . updateShareLink ( sharedLinkInfo . token , '' , expirationTime ) . then ( ( res ) => {
this . setState ( {
isEditingExpiration : false
} ) ;
this . props . updateLink ( new ShareLink ( res . data ) ) ;
} ) . catch ( ( error ) => {
let errMessage = Utils . getErrorMsg ( error ) ;
toaster . danger ( errMessage ) ;
} ) ;
}
handleMouseOver = ( ) => {
this . setState ( { isOpIconShown : true } ) ;
}
handleMouseOut = ( ) => {
this . setState ( { isOpIconShown : false } ) ;
}
changePerm = ( permission ) => {
const { sharedLinkInfo } = this . props ;
const { permissionDetails } = Utils . getShareLinkPermissionObject ( permission ) ;
seafileAPI . updateShareLink ( sharedLinkInfo . token , JSON . stringify ( permissionDetails ) ) . then ( ( res ) => {
this . props . updateLink ( new ShareLink ( res . data ) ) ;
} ) . catch ( ( error ) => {
let errMessage = Utils . getErrorMsg ( error ) ;
toaster . danger ( errMessage ) ;
} ) ;
}
onNoticeMessageToggle = ( ) => {
this . setState ( { isNoticeMessageShow : ! this . state . isNoticeMessageShow } ) ;
}
toggleSendLink = ( ) => {
this . setState ( { isSendLinkShown : ! this . state . isSendLinkShown } ) ;
}
render ( ) {
const { sharedLinkInfo , permissionOptions } = this . props ;
const { isOpIconShown } = this . state ;
const currentPermission = Utils . getShareLinkPermissionStr ( sharedLinkInfo . permissions ) ;
return (
< div >
< button className = "fa fa-arrow-left back-icon border-0 bg-transparent text-secondary p-0" onClick = { this . props . showLinkDetails . bind ( this , null ) } title = { gettext ( 'Back' ) } aria - label = { gettext ( 'Back' ) } > < / b u t t o n >
< dl >
< dt className = "text-secondary font-weight-normal" > { gettext ( 'Link:' ) } < / d t >
< dd >
< SharedLink
link = { sharedLinkInfo . link }
linkExpired = { sharedLinkInfo . is _expired }
copyLink = { this . onCopySharedLink }
/ >
< / d d >
{ ! sharedLinkInfo . is _dir && sharedLinkInfo . permissions . can _download && ( // just for file
< >
< dt className = "text-secondary font-weight-normal" > { gettext ( 'Direct Download Link:' ) } < / d t >
< dd >
< SharedLink
link = { ` ${ sharedLinkInfo . link } ?dl=1 ` }
linkExpired = { sharedLinkInfo . is _expired }
copyLink = { this . onCopyDownloadLink }
/ >
< / d d >
< / >
) }
{ sharedLinkInfo . password && (
< >
< dt className = "text-secondary font-weight-normal" > { gettext ( 'Password:' ) } < / d t >
< dd className = "d-flex" >
< div className = "d-flex align-items-center" >
< input id = "stored-password" className = "border-0 mr-1" type = "text" value = { this . state . storedPasswordVisible ? sharedLinkInfo . password : '****************************************' } readOnly = { true } size = { Math . max ( sharedLinkInfo . password . length , 10 ) } / >
< span tabIndex = "0" role = "button" aria - label = { this . state . storedPasswordVisible ? gettext ( 'Hide' ) : gettext ( 'Show' ) } onKeyDown = { this . onIconKeyDown } onClick = { this . toggleStoredPasswordVisible } className = { ` eye-icon fas ${ this . state . storedPasswordVisible ? 'fa-eye' : 'fa-eye-slash' } ` } > < / s p a n >
< / d i v >
< / d d >
< / >
) }
{ sharedLinkInfo . expire _date && (
< >
< dt className = "text-secondary font-weight-normal" > { gettext ( 'Expiration Date:' ) } < / d t >
{ ! this . state . isEditingExpiration &&
< dd style = { { width : '250px' } } onMouseEnter = { this . handleMouseOverExpirationEditIcon } onMouseLeave = { this . handleMouseOutExpirationEditIcon } >
{ moment ( sharedLinkInfo . expire _date ) . format ( 'YYYY-MM-DD HH:mm:ss' ) }
{ this . state . isExpirationEditIconShow && (
< a href = "#"
role = "button"
aria - label = { gettext ( 'Edit' ) }
title = { gettext ( 'Edit' ) }
className = "fa fa-pencil-alt attr-action-icon"
onClick = { this . editingExpirationToggle } >
< / a >
) }
< / d d >
}
{ this . state . isEditingExpiration &&
< dd >
< div className = "ml-4" >
< SetLinkExpiration
minDays = { shareLinkExpireDaysMin }
maxDays = { shareLinkExpireDaysMax }
defaultDays = { shareLinkExpireDaysDefault }
expType = { this . state . expType }
setExpType = { this . setExpType }
expireDays = { this . state . expireDays }
onExpireDaysChanged = { this . onExpireDaysChanged }
expDate = { this . state . expDate }
onExpDateChanged = { this . onExpDateChanged }
/ >
< div className = { this . state . expType == 'by-days' ? 'mt-2' : 'mt-3' } >
< button className = "btn btn-primary mr-2" onClick = { this . updateExpiration } > { gettext ( 'Update' ) } < / b u t t o n >
< button className = "btn btn-secondary" onClick = { this . editingExpirationToggle } > { gettext ( 'Cancel' ) } < / b u t t o n >
< / d i v >
< / d i v >
< / d d >
}
< / >
) }
{ ( isPro && sharedLinkInfo . permissions ) && (
< >
< dt className = "text-secondary font-weight-normal" > { gettext ( 'Permission:' ) } < / d t >
< dd style = { { width : '250px' } } onMouseEnter = { this . handleMouseOver } onMouseLeave = { this . handleMouseOut } >
< ShareLinkPermissionEditor
isTextMode = { true }
isEditIconShow = { isOpIconShown && ! sharedLinkInfo . is _expired }
currentPermission = { currentPermission }
permissionOptions = { permissionOptions }
onPermissionChanged = { this . changePerm }
/ >
< / d d >
< / >
) }
< / d l >
{ ( canSendShareLinkEmail && ! this . state . isSendLinkShown && ! this . state . isNoticeMessageShow ) &&
< Button onClick = { this . toggleSendLink } className = 'mr-2' > { gettext ( 'Send' ) } < / B u t t o n >
}
{ this . state . isSendLinkShown &&
< SendLink
linkType = 'shareLink'
token = { sharedLinkInfo . token }
toggleSendLink = { this . toggleSendLink }
closeShareDialog = { this . props . closeShareDialog }
/ >
}
{ ( ! this . state . isSendLinkShown && ! this . state . isNoticeMessageShow ) &&
< Button onClick = { this . onNoticeMessageToggle } > { gettext ( 'Delete' ) } < / B u t t o n >
}
{ this . state . isNoticeMessageShow &&
< div className = "alert alert-warning" >
< h4 className = "alert-heading" > { gettext ( 'Are you sure you want to delete the share link?' ) } < / h 4 >
< p className = "mb-4" > { gettext ( 'If the share link is deleted, no one will be able to access it any more.' ) } < / p >
< button className = "btn btn-primary" onClick = { this . props . deleteLink } > { gettext ( 'Delete' ) } < / b u t t o n > { ' ' }
< button className = "btn btn-secondary" onClick = { this . onNoticeMessageToggle } > { gettext ( 'Cancel' ) } < / b u t t o n >
< / d i v >
}
< / d i v >
) ;
}
}
LinkDetails . propTypes = {
sharedLinkInfo : PropTypes . object . isRequired ,
permissionOptions : PropTypes . array . isRequired ,
defaultExpireDays : PropTypes . oneOfType ( [
PropTypes . string ,
PropTypes . number
] ) ,
showLinkDetails : PropTypes . func . isRequired ,
updateLink : PropTypes . func . isRequired ,
deleteLink : PropTypes . func . isRequired ,
closeShareDialog : PropTypes . func . isRequired
} ;
ShareLinkPanel . propTypes = propTypes ;
2018-12-06 11:28:16 +08:00
2023-03-30 17:31:31 +08:00
export default ShareLinkPanel ;