-
Notifications
You must be signed in to change notification settings - Fork 368
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
1,533 additions
and
28 deletions.
There are no files selected for viewing
148 changes: 148 additions & 0 deletions
148
frontend/src/components/dialog/repo-share-admin-dialog.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
import React, { Fragment } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import toaster from '../toast'; | ||
import { Utils } from '../../utils/utils'; | ||
import { seafileAPI } from '../../utils/seafile-api'; | ||
import { Modal, ModalHeader, ModalBody, TabContent, TabPane, Nav, NavItem, NavLink } from 'reactstrap'; | ||
import { gettext, username, canGenerateShareLink, canGenerateUploadLink } from '../../utils/constants'; | ||
import RepoShareAdminShareLinks from './repo-share-admin/share-links'; | ||
import RepoShareAdminUploadLinks from './repo-share-admin/upload-links'; | ||
import RepoShareAdminUserShares from './repo-share-admin/user-shares'; | ||
import RepoShareAdminGroupShares from './repo-share-admin/group-shares'; | ||
import RepoShareAdminInternalLinks from './repo-share-admin/internal-links'; | ||
|
||
const propTypes = { | ||
repo: PropTypes.object.isRequired, | ||
toggleDialog: PropTypes.func.isRequired, | ||
}; | ||
|
||
class RepoShareAdminDialog extends React.Component { | ||
|
||
constructor(props) { | ||
super(props); | ||
this.state = { | ||
activeTab: this.getInitialActiveTab(), | ||
}; | ||
} | ||
|
||
enableShareLink = !this.props.repo.encrypted && canGenerateShareLink; | ||
enableUploadLink = !this.props.repo.encrypted && canGenerateUploadLink; | ||
|
||
getInitialActiveTab = () => { | ||
if (this.enableShareLink) { | ||
return 'shareLink'; | ||
} else if (this.enableUploadLink) { | ||
return 'uploadLink'; | ||
} else { | ||
return 'shareToUser'; | ||
} | ||
} | ||
|
||
toggle = (tab) => { | ||
if (this.state.activeTab !== tab) { | ||
this.setState({ activeTab: tab }); | ||
} | ||
} | ||
|
||
onTabKeyDown = (e) => { | ||
if (e.key == 'Enter' || e.key == 'Space') { | ||
e.target.click(); | ||
} | ||
} | ||
|
||
render() { | ||
|
||
let activeTab = this.state.activeTab; | ||
let repoName = this.props.repo.repo_name; | ||
|
||
return ( | ||
<div> | ||
<Modal isOpen={true} style={{maxWidth: '760px'}} className="share-dialog" toggle={this.props.toggleDialog}> | ||
<ModalHeader toggle={this.props.toggleDialog}> | ||
<span className="op-target" title={repoName}>{repoName}</span> {gettext('Share Admin')} | ||
</ModalHeader> | ||
<ModalBody className="dialog-list-container share-dialog-content" role="tablist"> | ||
<Fragment> | ||
<div className="share-dialog-side"> | ||
<Nav pills> | ||
{this.enableShareLink && | ||
<NavItem role="tab" aria-selected={activeTab === 'shareLink'} aria-controls="share-link-panel"> | ||
<NavLink className={activeTab === 'shareLink' ? 'active' : ''} onClick={(this.toggle.bind(this, 'shareLink'))} tabIndex="0" onKeyDown={this.onTabKeyDown}> | ||
{gettext('Share Links')} | ||
</NavLink> | ||
</NavItem> | ||
} | ||
{this.enableUploadLink && | ||
<NavItem role="tab" aria-selected={activeTab === 'uploadLink'} aria-controls="upload-link-panel"> | ||
<NavLink className={activeTab === 'uploadLink' ? 'active' : ''} onClick={this.toggle.bind(this, 'uploadLink')} tabIndex="0" onKeyDown={this.onTabKeyDown}> | ||
{gettext('Upload Links')} | ||
</NavLink> | ||
</NavItem> | ||
} | ||
<NavItem role="tab" aria-selected={activeTab === 'shareToUser'} aria-controls="share-to-user-panel"> | ||
<NavLink className={activeTab === 'shareToUser' ? 'active' : ''} onClick={this.toggle.bind(this, 'shareToUser')} tabIndex="0" onKeyDown={this.onTabKeyDown}> | ||
{gettext('User Shares')} | ||
</NavLink> | ||
</NavItem> | ||
<NavItem role="tab" aria-selected={activeTab === 'shareToGroup'} aria-controls="share-to-group-panel"> | ||
<NavLink className={activeTab === 'shareToGroup' ? 'active' : ''} onClick={this.toggle.bind(this, 'shareToGroup')} tabIndex="0" onKeyDown={this.onTabKeyDown}> | ||
{gettext('Group Shares')} | ||
</NavLink> | ||
</NavItem> | ||
<NavItem role="tab" aria-selected={activeTab === 'internalLink'} aria-controls="internal-link-panel"> | ||
<NavLink className={activeTab === 'internalLink' ? 'active' : ''} onClick={this.toggle.bind(this, 'internalLink')} tabIndex="0" onKeyDown={this.onTabKeyDown}> | ||
{gettext('Internal Links')} | ||
</NavLink> | ||
</NavItem> | ||
</Nav> | ||
</div> | ||
<div className="share-dialog-main"> | ||
<TabContent activeTab={this.state.activeTab}> | ||
{(this.enableShareLink && activeTab === 'shareLink') && | ||
<TabPane tabId="shareLink" role="tabpanel" id="share-link-panel"> | ||
<RepoShareAdminShareLinks | ||
repo={this.props.repo} | ||
/> | ||
</TabPane> | ||
} | ||
{(this.enableUploadLink && activeTab === 'uploadLink') && | ||
<TabPane tabId="uploadLink" role="tabpanel" id="upload-link-panel"> | ||
<RepoShareAdminUploadLinks | ||
repo={this.props.repo} | ||
/> | ||
</TabPane> | ||
} | ||
{activeTab === 'shareToUser' && | ||
<TabPane tabId="shareToUser" role="tabpanel" id="share-to-user-panel"> | ||
<RepoShareAdminUserShares | ||
repo={this.props.repo} | ||
/> | ||
</TabPane> | ||
} | ||
{activeTab === 'shareToGroup' && | ||
<TabPane tabId="shareToGroup" role="tabpanel" id="share-to-group-panel"> | ||
<RepoShareAdminGroupShares | ||
repo={this.props.repo} | ||
/> | ||
</TabPane> | ||
} | ||
{activeTab === 'internalLink' && | ||
<TabPane tabId="internalLink" role="tabpanel" id="internal-link-panel"> | ||
<RepoShareAdminInternalLinks | ||
repo={this.props.repo} | ||
/> | ||
</TabPane> | ||
} | ||
</TabContent> | ||
</div> | ||
</Fragment> | ||
</ModalBody> | ||
</Modal> | ||
</div> | ||
); | ||
} | ||
} | ||
|
||
RepoShareAdminDialog.propTypes = propTypes; | ||
|
||
export default RepoShareAdminDialog; |
215 changes: 215 additions & 0 deletions
215
frontend/src/components/dialog/repo-share-admin/group-shares.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,215 @@ | ||
import React, { Component, Fragment } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
|
||
import { Utils } from '../../../utils/utils'; | ||
import { seafileAPI } from '../../../utils/seafile-api'; | ||
import { gettext, siteRoot, isPro } from '../../../utils/constants'; | ||
|
||
import Loading from '../../../components/loading'; | ||
import toaster from '../../../components/toast'; | ||
import EmptyTip from '../../../components/empty-tip'; | ||
import SharePermissionEditor from '../../../components/select-editor/share-permission-editor'; | ||
|
||
const itemPropTypes = { | ||
item: PropTypes.object.isRequired, | ||
deleteItem: PropTypes.func.isRequired | ||
}; | ||
|
||
class Item extends Component { | ||
|
||
constructor(props) { | ||
super(props); | ||
this.state = { | ||
permission: this.props.item.permission, | ||
isOperationShow: false, | ||
isShowPermEditor: false, | ||
}; | ||
this.permissions = ['rw', 'r']; | ||
if (isPro && this.props.item.path === '/') { | ||
this.permissions.push('admin'); | ||
} | ||
if (isPro) { | ||
this.permissions.push('cloud-edit', 'preview'); | ||
} | ||
} | ||
|
||
onMouseEnter = () => { | ||
this.setState({isOperationShow: true}); | ||
}; | ||
|
||
onMouseLeave = () => { | ||
this.setState({isOperationShow: false}); | ||
}; | ||
|
||
onDeleteLink = (e) => { | ||
e.preventDefault(); | ||
this.props.deleteItem(this.props.item); | ||
}; | ||
|
||
changePerm = (permission) => { | ||
const item = this.props.item; | ||
seafileAPI.updateShareToGroupItemPermission(item.repo_id, item.path, 'group', item.share_to, permission).then(() => { | ||
this.setState({ | ||
permission: permission, | ||
}); | ||
}).catch(error => { | ||
let errMessage = Utils.getErrorMsg(error); | ||
toaster.danger(errMessage); | ||
}); | ||
} | ||
|
||
onEditPermission = (event) => { | ||
event.nativeEvent.stopImmediatePropagation(); | ||
this.setState({isShowPermEditor: true}); | ||
} | ||
|
||
render() { | ||
|
||
let objUrl; | ||
let item = this.props.item; | ||
let path = item.path === '/' ? '/' : item.path.slice(0, item.path.length - 1); | ||
|
||
objUrl = `${siteRoot}library/${item.repo_id}/${encodeURIComponent(item.repo_name)}${Utils.encodePath(path)}`; | ||
|
||
return ( | ||
<tr onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave} onFocus={this.onMouseEnter}> | ||
<td> | ||
<a href={objUrl} target="_blank">{Utils.getFolderName(item.path)}</a> | ||
</td> | ||
<td className="name">{item.share_to_name}</td> | ||
<td className="name"> | ||
{!this.state.isShowPermEditor && ( | ||
<div> | ||
<span>{item.permission_name || Utils.sharePerms(this.state.permission)}</span> | ||
{this.state.isOperationShow && ( | ||
<a href="#" | ||
role="button" | ||
aria-label={gettext('Edit')} | ||
title={gettext('Edit')} | ||
className="fa fa-pencil-alt attr-action-icon" | ||
onClick={this.onEditPermission}> | ||
</a> | ||
)} | ||
</div> | ||
)} | ||
{this.state.isShowPermEditor && ( | ||
<SharePermissionEditor | ||
repoID={item.repo_id} | ||
isTextMode={true} | ||
isEditIconShow={this.state.isOperationShow} | ||
isEditing={true} | ||
currentPermission={this.state.permission} | ||
permissions={this.permissions} | ||
onPermissionChanged={this.changePerm} | ||
/> | ||
)} | ||
</td> | ||
<td> | ||
<span | ||
tabIndex="0" | ||
role="button" | ||
className={`sf2-icon-x3 action-icon ${this.state.isOperationShow ? '' : 'invisible'}`} | ||
onClick={this.onDeleteLink} | ||
onKeyDown={Utils.onKeyDown} | ||
title={gettext('Delete')} | ||
aria-label={gettext('Delete')} | ||
/> | ||
</td> | ||
</tr> | ||
); | ||
} | ||
} | ||
|
||
Item.propTypes = itemPropTypes; | ||
|
||
const propTypes = { | ||
repo: PropTypes.object.isRequired, | ||
}; | ||
|
||
class RepoShareAdminGroupShares extends Component { | ||
|
||
constructor(props) { | ||
super(props); | ||
this.state = { | ||
loading: true, | ||
errorMsg: '', | ||
items: [], | ||
}; | ||
} | ||
|
||
componentDidMount() { | ||
seafileAPI.getAllRepoFolderShareInfo(this.props.repo.repo_id, 'group').then((res) => { | ||
this.setState({ | ||
loading: false, | ||
items: res.data.share_info_list, | ||
}); | ||
}).catch((error) => { | ||
this.setState({ | ||
loading: false, | ||
errorMsg: Utils.getErrorMsg(error, true) // true: show login tip if 403 | ||
}); | ||
}); | ||
} | ||
|
||
deleteItem = (item) => { | ||
seafileAPI.deleteShareToGroupItem(item.repo_id, item.path, 'group', item.share_to).then(res => { | ||
let items = this.state.items.filter(shareItem => { | ||
return shareItem.path + shareItem.share_to !== item.path + item.share_to; | ||
}) | ||
this.setState({items: items}); | ||
let message = gettext('Successfully deleted 1 item'); | ||
toaster.success(message); | ||
}).catch((error) => { | ||
let errMessage = Utils.getErrorMsg(error); | ||
toaster.danger(errMessage); | ||
}); | ||
} | ||
|
||
render() { | ||
const { loading, errorMsg, items } = this.state; | ||
return ( | ||
<Fragment> | ||
<div className="main-panel-center"> | ||
<div className="cur-view-container"> | ||
<div className="cur-view-content"> | ||
{loading && <Loading />} | ||
{!loading && errorMsg && <p className="error text-center mt-8">{errorMsg}</p>} | ||
{!loading && !errorMsg && !items.length && | ||
<EmptyTip forDialog={true}> | ||
<p className="text-secondary">{gettext('No share links')}</p> | ||
</EmptyTip> | ||
} | ||
{!loading && !errorMsg && items.length > 0 && | ||
<table> | ||
<thead> | ||
<tr> | ||
<th width="30%">{gettext('Name')}</th> | ||
<th width="30%">{gettext('Group')}</th> | ||
<th width="30%">{gettext('Permission')}</th> | ||
<th width="10%"></th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{items.map((item, index) => { | ||
return ( | ||
<Item | ||
key={index} | ||
item={item} | ||
deleteItem={this.deleteItem} | ||
/> | ||
); | ||
})} | ||
</tbody> | ||
</table> | ||
} | ||
</div> | ||
</div> | ||
</div> | ||
</Fragment> | ||
); | ||
} | ||
} | ||
|
||
RepoShareAdminGroupShares.propTypes = propTypes; | ||
|
||
export default RepoShareAdminGroupShares; |
Oops, something went wrong.