diff --git a/frontend/src/components/cur-dir-path/dir-tool.js b/frontend/src/components/cur-dir-path/dir-tool.js index c6002ea559..f6515a4334 100644 --- a/frontend/src/components/cur-dir-path/dir-tool.js +++ b/frontend/src/components/cur-dir-path/dir-tool.js @@ -12,7 +12,7 @@ import ListTaggedFilesDialog from '../dialog/list-taggedfiles-dialog'; const propTypes = { repoID: PropTypes.string.isRequired, repoName: PropTypes.string.isRequired, - permission: PropTypes.bool.isRequired, + userPerm: PropTypes.string, currentPath: PropTypes.string.isRequired, updateUsedRepoTags: PropTypes.func.isRequired, onDeleteRepoTag: PropTypes.func.isRequired, @@ -81,12 +81,12 @@ class DirTool extends React.Component { } render() { - let { repoID, repoName, permission, currentPath } = this.props; + let { repoID, repoName, userPerm, currentPath } = this.props; let isFile = this.isMarkdownFile(currentPath); let name = Utils.getFileName(currentPath); let trashUrl = siteRoot + 'repo/' + repoID + '/trash/'; let historyUrl = siteRoot + 'repo/history/' + repoID + '/'; - if (permission) { + if (userPerm === 'rw') { if (!isFile) { if (name) { // name not '' is not root path trashUrl = siteRoot + 'repo/' + repoID + '/trash/?path=' + encodeURIComponent(currentPath); diff --git a/frontend/src/components/cur-dir-path/index.js b/frontend/src/components/cur-dir-path/index.js index 76d550b22e..ae8ca89fed 100644 --- a/frontend/src/components/cur-dir-path/index.js +++ b/frontend/src/components/cur-dir-path/index.js @@ -8,7 +8,7 @@ import DirTool from './dir-tool'; const propTypes = { repoID: PropTypes.string.isRequired, repoName: PropTypes.string.isRequired, - permission: PropTypes.bool.isRequired, + userPerm: PropTypes.string, currentPath: PropTypes.string.isRequired, onPathClick: PropTypes.func.isRequired, onTabNavClick: PropTypes.func, @@ -52,7 +52,7 @@ class CurDirPath extends React.Component { { + this.props.addCustomPermission(permission_name, permission_desc, permission) + } + + render() { + return ( + + ); + } + +} + +AddCustomPermission.propTypes = propTypes; + +export default AddCustomPermission; diff --git a/frontend/src/components/dialog/custom-permission/custom-permission-editor.js b/frontend/src/components/dialog/custom-permission/custom-permission-editor.js new file mode 100644 index 0000000000..e6010d7e7c --- /dev/null +++ b/frontend/src/components/dialog/custom-permission/custom-permission-editor.js @@ -0,0 +1,208 @@ +import React, { Fragment } from 'react'; +import PropTypes from 'prop-types'; +import { Alert, FormGroup, Input, Label, Tooltip } from 'reactstrap'; +import { gettext } from '../../../utils/constants'; +import Loading from '../../loading'; + +const propTypes = { + mode: PropTypes.string, + permission: PropTypes.object, + onChangeMode: PropTypes.func.isRequired, + onUpdateCustomPermission: PropTypes.func.isRequired, +}; + +class CustomPermissionEditor extends React.Component { + + static defaultProps = { + mode: 'add' + } + + constructor(props) { + super(props); + this.state = { + isLoading: true, + permission_name: '', + permission_desc: '', + permission: { + upload: false, + download: false, + modify: false, + copy: false, + delete: false, + preview: false, + download_external_link: false, + }, + errMessage: '', + tooltipOpen: false, + }; + } + + componentDidMount() { + const { permission } = this.props; + if (permission) { + this.setState({ + permission_name: permission.name, + permission_desc: permission.description, + permission: permission.permission, + isLoading: false + }); + } else { + this.setState({isLoading: false}); + } + + } + + onChangePermissionName = (evt) => { + const { permission_name } = this.state; + const newName = evt.target.value; + if (newName === permission_name) return; + this.setState({permission_name: newName}); + } + + onChangePermissionDescription = (evt) => { + const { permission_desc } = this.state; + const newDescription = evt.target.value; + if (newDescription === permission_desc) return; + this.setState({permission_desc: newDescription}); + } + + onChangePermission = (type) => { + return () => { + const { permission } = this.state; + const value = !permission[type]; + const newPermission = Object.assign({}, permission, {[type]: value}); + this.setState({permission: newPermission}); + } + } + + validParams = () => { + const { permission_name, permission_desc } = this.state; + let isValid = false; + let errMessage = ''; + if (!permission_name || !permission_name.trim()) { + errMessage = gettext('Name is required'); + return { isValid, errMessage }; + } + if (!permission_desc || !permission_desc.trim()) { + errMessage = gettext('Description is required'); + return { isValid, errMessage }; + } + + isValid = true; + return { isValid }; + } + + onUpdateCustomPermission = () => { + const { permission_name, permission_desc, permission } = this.state; + const { isValid, errMessage } = this.validParams(); + if (!isValid) { + this.setState({errMessage}); + return; + } + this.props.onUpdateCustomPermission(permission_name, permission_desc, permission); + } + + toggle = () => { + this.setState({tooltipOpen: !this.state.tooltipOpen}); + } + + render() { + + const { mode } = this.props; + const title = mode === 'add' ? gettext('Add permission') : gettext('Edit permission'); + + const { isLoading, permission_name, permission_desc, permission, errMessage } = this.state; + + return ( +
+
+
+
+ +
+
{title}
+
+
+ +
+
+
+ {isLoading && } + {!isLoading && ( + +
+ + + + + + + + +
+ {errMessage && {errMessage}} +
+ + + + + + + + + + + + + + + + + + + + + +
+
+ )} +
+
+ ); + } + +} + +CustomPermissionEditor.propTypes = propTypes; + +export default CustomPermissionEditor; diff --git a/frontend/src/components/dialog/custom-permission/custom-permission-item.js b/frontend/src/components/dialog/custom-permission/custom-permission-item.js new file mode 100644 index 0000000000..f31cadd6be --- /dev/null +++ b/frontend/src/components/dialog/custom-permission/custom-permission-item.js @@ -0,0 +1,67 @@ +import React, { Fragment } from 'react'; +import PropTypes from 'prop-types'; + +const propTypes = { + permission: PropTypes.object.isRequired, + onEditCustomPermission: PropTypes.func.isRequired, + onDeleteCustomPermission: PropTypes.func.isRequired, +}; + +class CustomPermissionItem extends React.Component { + + constructor(props) { + super(props); + this.state = { + isShowOperations: false + }; + } + + onMouseEnter = () => { + this.setState({isShowOperations: true}); + } + + onMouseOver = () => { + this.setState({isShowOperations: true}); + } + + onMouseLeave = () => { + this.setState({isShowOperations: false}); + } + + onEditCustomPermission = () => { + const { permission } = this.props; + this.props.onEditCustomPermission(permission) + } + + onDeleteCustomPermission = () => { + const { permission } = this.props; + this.props.onDeleteCustomPermission(permission) + } + + render() { + const { permission } = this.props; + const { id, name, description } = permission; + return ( + + {name} + {description} + + {this.state.isShowOperations && ( + + + + + + + + + )} + + + ); + } +} + +CustomPermissionItem.propTypes = propTypes; + +export default CustomPermissionItem; diff --git a/frontend/src/components/dialog/custom-permission/custom-permission-manager.js b/frontend/src/components/dialog/custom-permission/custom-permission-manager.js new file mode 100644 index 0000000000..98526ac255 --- /dev/null +++ b/frontend/src/components/dialog/custom-permission/custom-permission-manager.js @@ -0,0 +1,159 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import ListCustomPermission from './list-custom-permissions'; +import AddCustomPermission from './add-custom-permission'; +import EditCustomPermission from './edit-custom-permission'; +import Loading from '../../loading'; +import { seafileAPI } from '../../../utils/seafile-api'; +import toaster from '../../toast'; +import { Utils } from '../../../utils/utils'; +import CustomPermission from '../../../models/custom-permission'; + +const propTypes = { + repoID: PropTypes.string.isRequired +}; + +const MANAGER_STATE = { + LIST: 'list', + ADD: 'add', + EDIT: 'edit', +}; + +class CustomPermissionManager extends React.Component { + + constructor(props) { + super(props); + this.state = { + currentMode: MANAGER_STATE.LIST, + isLoading: true, + permissions: [], + currentPermission: null, + }; + } + + componentDidMount() { + this.listCustomPermissions(); + } + + listCustomPermissions = () => { + const { repoID } = this.props; + seafileAPI.listCustomPermissions(repoID).then(res => { + const permissions = res.data.permission_list.map(item => new CustomPermission(item)); + this.setState({ + isLoading: false, + permissions: permissions + }); + }).catch(error => { + let errMessage = Utils.getErrorMsg(error); + toaster.danger(errMessage); + this.setState({isLoading: false}); + }); + } + + addCustomPermission = (permission_name, permission_desc, permission) => { + const { repoID } = this.props; + seafileAPI.createCustomPermission(repoID, permission_name, permission_desc, permission).then(res => { + const { permissions } = this.state; + const customPermission = new CustomPermission(res.data.permission); + permissions.unshift(customPermission) + this.setState({ + permissions, + currentMode: MANAGER_STATE.LIST + }); + }).catch(error => { + let errMessage = Utils.getErrorMsg(error); + toaster.danger(errMessage); + }); + } + + editCustomPermission = (newPermission) => { + const { repoID } = this.props; + seafileAPI.updateCustomPermission(repoID, newPermission).then(res => { + const customPermission = new CustomPermission(res.data.permission); + const { permissions } = this.state; + const newPermissions = permissions.map(item => { + if (item.id === customPermission.id) { + return customPermission; + } + return item; + }); + this.setState({ + permissions: newPermissions, + currentMode: MANAGER_STATE.LIST + }); + }).catch(error => { + let errMessage = Utils.getErrorMsg(error); + toaster.danger(errMessage); + }); + } + + deleteCustomPermission = (permission) => { + const { repoID } = this.props; + const { id: permissionID } = permission; + seafileAPI.deleteCustomPermission(repoID, permissionID).then(res => { + const { permissions } = this.state; + const newPermissions = permissions.filter(permission => permission.id !== permissionID); + this.setState({permissions: newPermissions}); + }).catch(error => { + let errMessage = Utils.getErrorMsg(error); + toaster.danger(errMessage); + }); + } + + onChangeMode = () => { + this.setState({currentMode: MANAGER_STATE.LIST}); + } + + onAddCustomPermission = () => { + this.setState({currentMode: MANAGER_STATE.ADD}); + } + + onEditCustomPermission = (permission) => { + this.setState({ + currentMode: MANAGER_STATE.EDIT, + currentPermission: permission + }); + } + + onDeleteCustomPermission = (permission) => { + this.deleteCustomPermission(permission); + } + + render() { + + if (this.state.isLoading) { + return + } + + const { currentMode, permissions, currentPermission } = this.state; + return ( +
+ {currentMode === MANAGER_STATE.LIST && ( + + )} + {currentMode === MANAGER_STATE.ADD && ( + + )} + {currentMode === MANAGER_STATE.EDIT && ( + + )} +
+ ); + } +} + +CustomPermissionManager.propTypes = propTypes; + +export default CustomPermissionManager; diff --git a/frontend/src/components/dialog/custom-permission/edit-custom-permission.js b/frontend/src/components/dialog/custom-permission/edit-custom-permission.js new file mode 100644 index 0000000000..74a746cb04 --- /dev/null +++ b/frontend/src/components/dialog/custom-permission/edit-custom-permission.js @@ -0,0 +1,37 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import CustomPermissionEditor from './custom-permission-editor'; + +const propTypes = { + permission: PropTypes.object.isRequired, + onChangeMode: PropTypes.func.isRequired, + editCustomPermission: PropTypes.func.isRequired +}; + +class EditCustomPermission extends React.Component { + + onUpdateCustomPermission = (permission_name, permission_desc, permission) => { + const { permission: editPermission } = this.props; + const newPermission = Object.assign({}, editPermission, { + name: permission_name, + description: permission_desc, + permission: permission + }); + this.props.editCustomPermission(newPermission); + } + + render() { + return ( + + ); + } +} + +EditCustomPermission.propTypes = propTypes; + +export default EditCustomPermission; diff --git a/frontend/src/components/dialog/custom-permission/list-custom-permissions.js b/frontend/src/components/dialog/custom-permission/list-custom-permissions.js new file mode 100644 index 0000000000..1cc649c441 --- /dev/null +++ b/frontend/src/components/dialog/custom-permission/list-custom-permissions.js @@ -0,0 +1,60 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import CustomPermissionItem from './custom-permission-item'; +import { gettext } from '../../../utils/constants'; + +const propTypes = { + permissions: PropTypes.array.isRequired, + onAddCustomPermission: PropTypes.func.isRequired, + onEditCustomPermission: PropTypes.func.isRequired, + onDeleteCustomPermission: PropTypes.func.isRequired, +}; + +class ListCustomPermissions extends React.Component { + + render() { + const { permissions } = this.props; + + return ( +
+
+
{gettext('Permission')}
+
+ +
+
+
+ + + + + + + + +
{gettext('Permission name')}{gettext('Description')}
+
+ + + {permissions.map(permission => { + return ( + + ); + })} + +
+
+
+
+ ); + } +} + +ListCustomPermissions.propTypes = propTypes; + +export default ListCustomPermissions; diff --git a/frontend/src/components/dialog/generate-share-link.js b/frontend/src/components/dialog/generate-share-link.js index 80584bca74..bda28c9876 100644 --- a/frontend/src/components/dialog/generate-share-link.js +++ b/frontend/src/components/dialog/generate-share-link.js @@ -18,6 +18,7 @@ const propTypes = { itemPath: PropTypes.string.isRequired, repoID: PropTypes.string.isRequired, closeShareDialog: PropTypes.func.isRequired, + userPerm: PropTypes.string, }; const inputWidth = Utils.isDesktop() ? 250 : 210; @@ -367,6 +368,9 @@ class GenerateShareLink extends React.Component { passwordLengthTip = passwordLengthTip.replace('{passwordLength}', shareLinkPasswordMinLength) .replace('{shareLinkPasswordStrengthLevel}', shareLinkPasswordStrengthLevel); + const { userPerm } = this.props; + const { isCustomPermission } = Utils.getUserPermission(userPerm); + if (this.state.sharedLinkInfo) { let sharedLinkInfo = this.state.sharedLinkInfo; let currentPermission = Utils.getShareLinkPermissionStr(sharedLinkInfo.permissions); @@ -525,7 +529,7 @@ class GenerateShareLink extends React.Component { } - {isPro && ( + {(isPro && !isCustomPermission) && (