2019-08-15 20:59:10 +08:00
import React , { Fragment , Component } from 'react' ;
2023-09-13 08:40:50 +08:00
import PropTypes from 'prop-types' ;
2022-12-29 12:21:47 +08:00
import { Link } from '@gatsbyjs/reach-router' ;
2019-08-15 20:59:10 +08:00
import { Dropdown , DropdownToggle , DropdownItem } from 'reactstrap' ;
2024-12-27 11:22:42 +08:00
import classnames from 'classnames' ;
2018-11-19 11:53:44 +08:00
import { seafileAPI } from '../../utils/seafile-api' ;
import { Utils } from '../../utils/utils' ;
2019-12-05 15:45:16 +08:00
import { gettext , siteRoot , isPro } from '../../utils/constants' ;
2019-08-15 20:59:10 +08:00
import Loading from '../../components/loading' ;
2019-06-10 17:30:10 +08:00
import EmptyTip from '../../components/empty-tip' ;
2019-08-15 20:59:10 +08:00
import toaster from '../../components/toast' ;
2019-01-11 12:41:30 +08:00
import SharePermissionEditor from '../../components/select-editor/share-permission-editor' ;
2018-12-27 10:24:34 +08:00
import SharedFolderInfo from '../../models/shared-folder-info' ;
2019-08-15 20:59:10 +08:00
import PermSelect from '../../components/dialog/perm-select' ;
2024-12-27 11:22:42 +08:00
import FixedWidthTable from '../../components/common/fixed-width-table' ;
2018-11-19 11:53:44 +08:00
class Content extends Component {
2019-01-02 16:17:39 +08:00
sortByName = ( e ) => {
e . preventDefault ( ) ;
const sortBy = 'name' ;
const sortOrder = this . props . sortOrder == 'asc' ? 'desc' : 'asc' ;
this . props . sortItems ( sortBy , sortOrder ) ;
2023-09-13 08:40:50 +08:00
} ;
2019-01-02 16:17:39 +08:00
2018-11-19 11:53:44 +08:00
render ( ) {
2019-01-02 16:17:39 +08:00
const { loading , errorMsg , items , sortBy , sortOrder } = this . props ;
2018-11-19 11:53:44 +08:00
if ( loading ) {
2019-08-15 20:59:10 +08:00
return < Loading / > ;
2024-12-27 11:22:42 +08:00
}
if ( errorMsg ) {
2018-11-19 11:53:44 +08:00
return < p className = "error text-center" > { errorMsg } < / p > ;
2024-12-27 11:22:42 +08:00
}
if ( ! items . length ) {
return (
2024-08-20 11:19:11 +08:00
< EmptyTip
title = { gettext ( 'No folders shared' ) }
text = { gettext ( 'You have not shared any folders with other users yet. You can share a folder with other users by clicking the share icon to the right of a folder\'s name.' ) }
>
2019-06-10 17:30:10 +08:00
< / E m p t y T i p >
2018-11-19 11:53:44 +08:00
) ;
}
2024-12-27 11:22:42 +08:00
// sort
const sortByName = sortBy == 'name' ;
const sortIcon = sortOrder == 'asc' ? < span className = "sf3-font sf3-font-down rotate-180 d-inline-block" > < / s p a n > : < s p a n c l a s s N a m e = " s f 3 - f o n t s f 3 - f o n t - d o w n " > < / s p a n > ;
const isDesktop = Utils . isDesktop ( ) ;
return (
< FixedWidthTable
className = { classnames ( 'table-hover' , { 'table-thead-hidden' : ! isDesktop } ) }
headers = { isDesktop ? [
{ isFixed : true , width : 40 } , // icon
{ isFixed : false , width : 0.35 , children : ( < a className = "d-block table-sort-op" href = "#" onClick = { this . sortByName } > { gettext ( 'Name' ) } { sortByName && sortIcon } < / a > ) } ,
{ isFixed : false , width : 0.3 , children : gettext ( 'Share To' ) } ,
{ isFixed : false , width : 0.25 , children : gettext ( 'Permission' ) } ,
{ isFixed : false , width : 0.1 } ,
] : [
{ isFixed : false , width : 0.12 } ,
{ isFixed : false , width : 0.8 } ,
{ isFixed : false , width : 0.08 } ,
] }
>
{ items . map ( ( item , index ) => {
return ( < Item key = { index } isDesktop = { isDesktop } item = { item } / > ) ;
} ) }
< / F i x e d W i d t h T a b l e >
) ;
2018-11-19 11:53:44 +08:00
}
}
2023-09-13 08:40:50 +08:00
Content . propTypes = {
loading : PropTypes . bool . isRequired ,
errorMsg : PropTypes . string . isRequired ,
items : PropTypes . array . isRequired ,
sortItems : PropTypes . func . isRequired ,
sortBy : PropTypes . string . isRequired ,
sortOrder : PropTypes . string . isRequired ,
} ;
2018-11-19 11:53:44 +08:00
class Item extends Component {
constructor ( props ) {
super ( props ) ;
this . state = {
2018-12-27 10:24:34 +08:00
share _permission : this . props . item . share _permission ,
2023-08-17 23:19:19 +08:00
share _permission _name : this . props . item . share _permission _name ,
2019-08-15 20:59:10 +08:00
isOpIconShown : false ,
isOpMenuOpen : false , // for mobile
isPermSelectDialogOpen : false , // for mobile
2023-08-17 23:19:19 +08:00
unshared : false ,
isShowPermEditor : false ,
2018-11-19 11:53:44 +08:00
} ;
2018-12-26 16:07:22 +08:00
this . permissions = [ 'rw' , 'r' ] ;
if ( isPro ) {
2019-08-15 20:59:10 +08:00
this . permissions . push ( 'cloud-edit' , 'preview' ) ;
2018-12-26 16:07:22 +08:00
}
2018-11-19 11:53:44 +08:00
}
2019-08-15 20:59:10 +08:00
toggleOpMenu = ( ) => {
this . setState ( {
isOpMenuOpen : ! this . state . isOpMenuOpen
} ) ;
2023-09-13 08:40:50 +08:00
} ;
2019-08-15 20:59:10 +08:00
togglePermSelectDialog = ( ) => {
this . setState ( {
isPermSelectDialogOpen : ! this . state . isPermSelectDialogOpen
} ) ;
2023-09-13 08:40:50 +08:00
} ;
2019-08-15 20:59:10 +08:00
2018-12-26 16:07:22 +08:00
onMouseEnter = ( ) => {
2024-07-18 11:58:42 +08:00
this . setState ( { isOpIconShown : true } ) ;
2023-09-13 08:40:50 +08:00
} ;
2018-11-19 11:53:44 +08:00
2018-12-26 16:07:22 +08:00
onMouseLeave = ( ) => {
2024-07-18 11:58:42 +08:00
this . setState ( { isOpIconShown : false } ) ;
2023-09-13 08:40:50 +08:00
} ;
2018-11-19 11:53:44 +08:00
2018-12-26 16:07:22 +08:00
unshare = ( e ) => {
2018-11-19 11:53:44 +08:00
e . preventDefault ( ) ;
2019-08-15 20:59:10 +08:00
2018-12-27 10:24:34 +08:00
const item = this . props . item ;
2019-08-15 20:59:10 +08:00
let options = {
'p' : item . path
} ;
if ( item . share _type == 'personal' ) {
Object . assign ( options , {
'share_type' : 'user' ,
'username' : item . user _email
} ) ;
} else {
Object . assign ( options , {
'share_type' : item . share _type , // 'group'
'group_id' : item . group _id
} ) ;
}
seafileAPI . unshareFolder ( item . repo _id , options ) . then ( ( res ) => {
this . setState ( {
unshared : true
} ) ;
let message = gettext ( 'Successfully unshared {name}' ) . replace ( '{name}' , item . folder _name ) ;
toaster . success ( message ) ;
} ) . catch ( error => {
let errMessage = Utils . getErrorMsg ( error ) ;
toaster ( errMessage ) ;
} ) ;
2023-09-13 08:40:50 +08:00
} ;
2018-11-19 11:53:44 +08:00
2018-12-26 16:07:22 +08:00
changePerm = ( permission ) => {
2019-08-15 20:59:10 +08:00
const item = this . props . item ;
2018-11-19 11:53:44 +08:00
const postData = {
2020-11-02 13:56:35 +08:00
'permission' : permission
2018-11-19 11:53:44 +08:00
} ;
let options = {
2019-08-15 20:59:10 +08:00
'p' : item . path
2018-11-19 11:53:44 +08:00
} ;
2019-08-15 20:59:10 +08:00
if ( item . share _type == 'personal' ) {
Object . assign ( options , {
'share_type' : 'user' ,
'username' : item . user _email
} ) ;
2018-12-27 10:24:34 +08:00
} else {
2019-08-15 20:59:10 +08:00
Object . assign ( options , {
'share_type' : item . share _type , // 'group'
'group_id' : item . group _id
} ) ;
2018-11-19 11:53:44 +08:00
}
2018-12-27 10:24:34 +08:00
seafileAPI . updateFolderSharePerm ( item . repo _id , postData , options ) . then ( ( res ) => {
2024-07-18 11:58:42 +08:00
this . setState ( { share _permission : permission } ) ;
2019-08-15 20:59:10 +08:00
toaster . success ( gettext ( 'Successfully modified permission.' ) ) ;
2018-11-19 11:53:44 +08:00
} ) . catch ( ( error ) => {
2019-07-16 10:01:09 +08:00
let errMessage = Utils . getErrorMsg ( error ) ;
toaster . danger ( errMessage ) ;
2018-11-19 11:53:44 +08:00
} ) ;
2023-09-13 08:40:50 +08:00
} ;
2018-11-19 11:53:44 +08:00
2023-08-17 23:19:19 +08:00
onEditPermission = ( event ) => {
event . nativeEvent . stopImmediatePropagation ( ) ;
2024-07-18 11:58:42 +08:00
this . setState ( { isShowPermEditor : true } ) ;
2023-09-13 08:40:50 +08:00
} ;
2023-08-17 23:19:19 +08:00
2019-08-15 20:59:10 +08:00
render ( ) {
if ( this . state . unshared ) {
return null ;
}
const item = this . props . item ;
2023-08-17 23:19:19 +08:00
let { share _permission , share _permission _name , isOpIconShown , isPermSelectDialogOpen , isShowPermEditor } = this . state ;
2019-08-15 20:59:10 +08:00
2018-11-19 11:53:44 +08:00
let is _readonly = false ;
if ( share _permission == 'r' || share _permission == 'preview' ) {
is _readonly = true ;
}
2020-11-02 13:56:35 +08:00
let iconUrl = Utils . getFolderIconUrl ( is _readonly ) ;
2018-12-27 10:24:34 +08:00
let iconTitle = Utils . getFolderIconTitle ( {
2018-11-19 11:53:44 +08:00
'permission' : share _permission
} ) ;
2019-08-15 20:59:10 +08:00
let folderUrl = ` ${ siteRoot } library/ ${ item . repo _id } / ${ encodeURIComponent ( item . repo _name ) } ${ Utils . encodePath ( item . path ) } ` ;
2018-11-19 11:53:44 +08:00
2021-09-13 10:37:07 +08:00
// custom defined permission
if ( share _permission . startsWith ( 'custom-' ) ) {
share _permission = share _permission . slice ( 7 ) ;
}
2019-08-15 20:59:10 +08:00
2024-07-25 11:04:36 +08:00
if ( this . props . isDesktop ) {
return (
< tr onMouseEnter = { this . onMouseEnter } onMouseLeave = { this . onMouseLeave } onFocus = { this . onMouseEnter } >
2024-12-27 11:22:42 +08:00
< td className = "pl10 pr-2" > < img src = { iconUrl } title = { iconTitle } alt = { iconTitle } width = "24" / > < / t d >
2024-07-25 11:04:36 +08:00
< td > < Link to = { folderUrl } > { item . folder _name } < / L i n k > < / t d >
2019-08-15 20:59:10 +08:00
< td >
2024-07-25 11:04:36 +08:00
{ item . share _type == 'personal' ?
< span title = { item . contact _email } > { item . user _name } < / s p a n > : i t e m . g r o u p _ n a m e }
2019-08-15 20:59:10 +08:00
< / t d >
< td >
2024-07-25 11:04:36 +08:00
{ ! isShowPermEditor && (
< div >
< span > { Utils . sharePerms ( share _permission ) || share _permission _name } < / s p a n >
{ isOpIconShown && (
< a href = "#"
role = "button"
aria - label = { gettext ( 'Edit' ) }
title = { gettext ( 'Edit' ) }
className = "sf3-font sf3-font-rename attr-action-icon"
onClick = { this . onEditPermission } >
< / a >
) }
2019-08-15 20:59:10 +08:00
< / d i v >
2024-07-25 11:04:36 +08:00
) }
{ isShowPermEditor && (
< SharePermissionEditor
repoID = { item . repo _id }
isTextMode = { true }
isEditIconShow = { isOpIconShown }
isEditing = { true }
autoFocus = { true }
currentPermission = { share _permission }
permissions = { this . permissions }
onPermissionChanged = { this . changePerm }
/ >
) }
2019-08-15 20:59:10 +08:00
< / t d >
2024-07-25 11:04:36 +08:00
< td > < a href = "#" role = "button" aria - label = { gettext ( 'Unshare' ) } className = { ` action-icon sf2-icon-x3 ${ isOpIconShown ? '' : 'invisible' } ` } title = { gettext ( 'Unshare' ) } onClick = { this . unshare } > < / a > < / t d >
2019-08-15 20:59:10 +08:00
< / t r >
2024-07-25 11:04:36 +08:00
) ;
} else {
return (
< Fragment >
< tr >
< td > < img src = { iconUrl } title = { iconTitle } alt = { iconTitle } width = "24" / > < / t d >
< td >
< Link to = { folderUrl } > { item . folder _name } < / L i n k >
< span className = "item-meta-info-highlighted" > { Utils . sharePerms ( share _permission ) } < / s p a n >
< br / >
< span className = "item-meta-info" > { ` ${ gettext ( 'Share To:' ) } ${ item . share _type == 'personal' ? item . user _name : item . group _name } ` } < / s p a n >
< / t d >
< td >
< Dropdown isOpen = { this . state . isOpMenuOpen } toggle = { this . toggleOpMenu } >
< DropdownToggle
tag = "i"
className = "sf-dropdown-toggle sf3-font sf3-font-more-vertical ml-0"
title = { gettext ( 'More operations' ) }
aria - label = { gettext ( 'More operations' ) }
data - toggle = "dropdown"
aria - expanded = { this . state . isOpMenuOpen }
/ >
< div className = { this . state . isOpMenuOpen ? '' : 'd-none' } onClick = { this . toggleOpMenu } >
< div className = "mobile-operation-menu-bg-layer" > < / d i v >
< div className = "mobile-operation-menu" >
< DropdownItem className = "mobile-menu-item" onClick = { this . togglePermSelectDialog } > { gettext ( 'Permission' ) } < / D r o p d o w n I t e m >
< DropdownItem className = "mobile-menu-item" onClick = { this . unshare } > { gettext ( 'Unshare' ) } < / D r o p d o w n I t e m >
< / d i v >
< / d i v >
< / D r o p d o w n >
< / t d >
< / t r >
{ isPermSelectDialogOpen && (
< PermSelect
repoID = { item . repo _id }
currentPerm = { share _permission }
permissions = { this . permissions }
changePerm = { this . changePerm }
toggleDialog = { this . togglePermSelectDialog }
/ >
) }
< / F r a g m e n t >
) ;
}
2018-11-19 11:53:44 +08:00
}
}
2023-09-13 08:40:50 +08:00
Item . propTypes = {
item : PropTypes . object . isRequired ,
isDesktop : PropTypes . bool . isRequired ,
} ;
2018-11-19 11:53:44 +08:00
class ShareAdminFolders extends Component {
2018-12-26 16:07:22 +08:00
2018-11-19 11:53:44 +08:00
constructor ( props ) {
super ( props ) ;
this . state = {
loading : true ,
errorMsg : '' ,
2019-01-02 16:17:39 +08:00
items : [ ] ,
sortBy : 'name' ,
sortOrder : 'asc' // 'asc' or 'desc'
2018-11-19 11:53:44 +08:00
} ;
}
2019-01-02 16:17:39 +08:00
_sortItems = ( items , sortBy , sortOrder ) => {
let comparator ;
switch ( ` ${ sortBy } - ${ sortOrder } ` ) {
case 'name-asc' :
2024-07-18 11:58:42 +08:00
comparator = function ( a , b ) {
2019-01-02 16:17:39 +08:00
var result = Utils . compareTwoWord ( a . folder _name , b . folder _name ) ;
return result ;
2019-01-02 17:49:30 +08:00
} ;
2019-01-02 16:17:39 +08:00
break ;
case 'name-desc' :
2024-07-18 11:58:42 +08:00
comparator = function ( a , b ) {
2019-01-02 16:17:39 +08:00
var result = Utils . compareTwoWord ( a . folder _name , b . folder _name ) ;
return - result ;
2019-01-02 17:49:30 +08:00
} ;
2019-01-02 16:17:39 +08:00
break ;
}
items . sort ( comparator ) ;
return items ;
2023-09-13 08:40:50 +08:00
} ;
2019-01-02 16:17:39 +08:00
sortItems = ( sortBy , sortOrder ) => {
this . setState ( {
sortBy : sortBy ,
sortOrder : sortOrder ,
items : this . _sortItems ( this . state . items , sortBy , sortOrder )
} ) ;
2023-09-13 08:40:50 +08:00
} ;
2019-01-02 16:17:39 +08:00
2018-11-19 11:53:44 +08:00
componentDidMount ( ) {
seafileAPI . listSharedFolders ( ) . then ( ( res ) => {
2018-12-27 10:24:34 +08:00
let items = res . data . map ( item => {
return new SharedFolderInfo ( item ) ;
} ) ;
2018-11-19 11:53:44 +08:00
this . setState ( {
loading : false ,
2019-01-02 16:17:39 +08:00
items : this . _sortItems ( items , this . state . sortBy , this . state . sortOrder )
2018-11-19 11:53:44 +08:00
} ) ;
} ) . catch ( ( error ) => {
2019-12-05 15:45:16 +08:00
this . setState ( {
loading : false ,
errorMsg : Utils . getErrorMsg ( error , true ) // true: show login tip if 403
} ) ;
2018-11-19 11:53:44 +08:00
} ) ;
}
render ( ) {
return (
2018-11-26 17:53:18 +08:00
< div className = "main-panel-center" >
< div className = "cur-view-container" id = "share-admin-libs" >
< div className = "cur-view-path" >
2019-01-31 17:37:02 +08:00
< h3 className = "sf-heading" > { gettext ( 'Folders' ) } < / h 3 >
2018-11-26 17:53:18 +08:00
< / d i v >
< div className = "cur-view-content" >
2020-11-02 13:56:35 +08:00
< Content
2018-12-27 10:24:34 +08:00
errorMsg = { this . state . errorMsg }
loading = { this . state . loading }
items = { this . state . items }
2019-01-02 16:17:39 +08:00
sortBy = { this . state . sortBy }
sortOrder = { this . state . sortOrder }
sortItems = { this . sortItems }
2018-12-27 10:24:34 +08:00
/ >
2018-11-26 14:00:32 +08:00
< / d i v >
2018-11-19 11:53:44 +08:00
< / d i v >
2018-11-26 17:53:18 +08:00
< / d i v >
2018-11-19 11:53:44 +08:00
) ;
}
}
export default ShareAdminFolders ;