1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-07 09:51:26 +00:00

Merge branch '10.0'

This commit is contained in:
lian
2023-08-18 09:47:15 +08:00
10 changed files with 164 additions and 58 deletions

View File

@@ -59,21 +59,6 @@ class DirentListItem extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = {
isOperationShow: false,
highlight: false,
isZipDialogOpen: false,
isMoveDialogShow: false,
isCopyDialogShow: false,
isShareDialogShow: false,
isMutipleOperation: false,
isShowTagTooltip: false,
isDragTipShow: false,
isDropTipshow: false,
isEditFileTagShow: false,
isPermissionDialogOpen: false,
isOpMenuOpen: false // for mobile
};
const { dirent } = this.props; const { dirent } = this.props;
const { isCustomPermission, customPermission } = Utils.getUserPermission(dirent.permission); const { isCustomPermission, customPermission } = Utils.getUserPermission(dirent.permission);
@@ -86,6 +71,23 @@ class DirentListItem extends React.Component {
this.canPreview = preview || modify; this.canPreview = preview || modify;
this.canDrag = modify; this.canDrag = modify;
} }
this.state = {
isOperationShow: false,
highlight: false,
isZipDialogOpen: false,
isMoveDialogShow: false,
isCopyDialogShow: false,
isShareDialogShow: false,
isMutipleOperation: false,
canDrag: this.canDrag,
isShowTagTooltip: false,
isDragTipShow: false,
isDropTipshow: false,
isEditFileTagShow: false,
isPermissionDialogOpen: false,
isOpMenuOpen: false // for mobile
};
} }
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
@@ -115,7 +117,7 @@ class DirentListItem extends React.Component {
isOperationShow: true, isOperationShow: true,
}); });
} }
if (this.canDrag) { if (this.state.canDrag) {
this.setState({isDragTipShow: true}); this.setState({isDragTipShow: true});
} }
} }
@@ -127,7 +129,7 @@ class DirentListItem extends React.Component {
isOperationShow: true, isOperationShow: true,
}); });
} }
if (this.canDrag) { if (this.state.canDrag) {
this.setState({isDragTipShow: true}); this.setState({isDragTipShow: true});
} }
} }
@@ -314,6 +316,7 @@ class DirentListItem extends React.Component {
this.setState({ this.setState({
isOperationShow: false, isOperationShow: false,
isRenameing: true, isRenameing: true,
canDrag: false
}); });
} }
@@ -323,7 +326,10 @@ class DirentListItem extends React.Component {
} }
onRenameCancel = () => { onRenameCancel = () => {
this.setState({isRenameing: false}); this.setState({
isRenameing: false,
canDrag: this.canDrag // set it back to the initial value
});
this.unfreezeItem(); this.unfreezeItem();
} }
@@ -479,7 +485,7 @@ class DirentListItem extends React.Component {
} }
onItemDragStart = (e) => { onItemDragStart = (e) => {
if (Utils.isIEBrower() || !this.canDrag) { if (Utils.isIEBrower() || !this.state.canDrag) {
return false; return false;
} }
e.dataTransfer.effectAllowed = 'move'; e.dataTransfer.effectAllowed = 'move';
@@ -509,7 +515,7 @@ class DirentListItem extends React.Component {
} }
onItemDragEnter = (e) => { onItemDragEnter = (e) => {
if (Utils.isIEBrower() || !this.canDrag) { if (Utils.isIEBrower() || !this.state.canDrag) {
return false; return false;
} }
if (this.props.dirent.type === 'dir') { if (this.props.dirent.type === 'dir') {
@@ -519,7 +525,7 @@ class DirentListItem extends React.Component {
} }
onItemDragOver = (e) => { onItemDragOver = (e) => {
if (Utils.isIEBrower() || !this.canDrag) { if (Utils.isIEBrower() || !this.state.canDrag) {
return false; return false;
} }
if (e.dataTransfer.dropEffect === 'copy') { if (e.dataTransfer.dropEffect === 'copy') {
@@ -530,7 +536,7 @@ class DirentListItem extends React.Component {
} }
onItemDragLeave = (e) => { onItemDragLeave = (e) => {
if (Utils.isIEBrower() || !this.canDrag) { if (Utils.isIEBrower() || !this.state.canDrag) {
return false; return false;
} }
@@ -541,7 +547,7 @@ class DirentListItem extends React.Component {
} }
onItemDragDrop = (e) => { onItemDragDrop = (e) => {
if (Utils.isIEBrower() || !this.canDrag) { if (Utils.isIEBrower() || !this.state.canDrag) {
return false; return false;
} }
this.setState({isDropTipshow: false}); this.setState({isDropTipshow: false});
@@ -698,10 +704,11 @@ class DirentListItem extends React.Component {
let lockedInfo = gettext('locked by {name}').replace('{name}', dirent.lock_owner_name); let lockedInfo = gettext('locked by {name}').replace('{name}', dirent.lock_owner_name);
const isDesktop = Utils.isDesktop(); const isDesktop = Utils.isDesktop();
const { canDrag } = this.state;
const desktopItem = ( const desktopItem = (
<tr <tr
className={trClass} className={trClass}
draggable={this.canDrag} draggable={canDrag}
onFocus={this.onMouseEnter} onFocus={this.onMouseEnter}
onMouseEnter={this.onMouseEnter} onMouseEnter={this.onMouseEnter}
onMouseOver={this.onMouseOver} onMouseOver={this.onMouseOver}

View File

@@ -6,6 +6,7 @@ import '../../css/select-editor.css';
const propTypes = { const propTypes = {
isTextMode: PropTypes.bool.isRequired, // there will be two mode. first: text and select. second: just select isTextMode: PropTypes.bool.isRequired, // there will be two mode. first: text and select. second: just select
isEditing: PropTypes.bool,
isEditIconShow: PropTypes.bool.isRequired, isEditIconShow: PropTypes.bool.isRequired,
options: PropTypes.array.isRequired, options: PropTypes.array.isRequired,
currentOption: PropTypes.string.isRequired, currentOption: PropTypes.string.isRequired,
@@ -22,12 +23,13 @@ class SelectEditor extends React.Component {
static defaultProps = { static defaultProps = {
enableAddCustomPermission: false, enableAddCustomPermission: false,
isEditing: false,
} }
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
isEditing: false, isEditing: props.isEditing,
options: [] options: []
}; };
this.options = []; this.options = [];

View File

@@ -10,6 +10,7 @@ import { isPro } from '../../utils/constants';
const propTypes = { const propTypes = {
repoID: PropTypes.string, repoID: PropTypes.string,
isTextMode: PropTypes.bool.isRequired, isTextMode: PropTypes.bool.isRequired,
isEditing: PropTypes.bool,
isEditIconShow: PropTypes.bool.isRequired, isEditIconShow: PropTypes.bool.isRequired,
permissions: PropTypes.array.isRequired, permissions: PropTypes.array.isRequired,
currentPermission: PropTypes.string.isRequired, currentPermission: PropTypes.string.isRequired,
@@ -20,6 +21,10 @@ const propTypes = {
class SharePermissionEditor extends React.Component { class SharePermissionEditor extends React.Component {
static defaultProps = {
isEditing: false,
}
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
@@ -120,6 +125,7 @@ class SharePermissionEditor extends React.Component {
return ( return (
<SelectEditor <SelectEditor
isTextMode={this.props.isTextMode} isTextMode={this.props.isTextMode}
isEditing={this.props.isEditing}
isEditIconShow={this.props.isEditIconShow} isEditIconShow={this.props.isEditIconShow}
options={this.getPermissions()} options={this.getPermissions()}
currentOption={this.props.currentPermission} currentOption={this.props.currentPermission}

View File

@@ -1,5 +1,5 @@
.dirents-more-menu { .dirents-more-menu {
margin-left: -9px; margin-left: 0;
padding: 4px 15px; padding: 4px 15px;
border: 1px solid #ccc; border: 1px solid #ccc;
border-top-right-radius: 2px !important; border-top-right-radius: 2px !important;

View File

@@ -7,6 +7,7 @@ class SharedFolderInfo {
this.repo_name = object.repo_name; this.repo_name = object.repo_name;
this.share_type = object.share_type; this.share_type = object.share_type;
this.share_permission = object.share_permission; this.share_permission = object.share_permission;
this.share_permission_name = object.share_permission_name;
if (object.share_type === 'group') { if (object.share_type === 'group') {
this.group_id = object.group_id; this.group_id = object.group_id;
this.group_name = object.group_name; this.group_name = object.group_name;

View File

@@ -5,6 +5,7 @@ class SharedRepoInfo {
this.repo_name = object.repo_name; this.repo_name = object.repo_name;
this.share_type = object.share_type; this.share_type = object.share_type;
this.share_permission = object.share_permission; this.share_permission = object.share_permission;
this.share_permission_name = object.share_permission_name;
this.modifier_name = object.modifier_name; this.modifier_name = object.modifier_name;
this.modifier_email = object.modifier_email; this.modifier_email = object.modifier_email;
this.modifier_contact_email = object.modifier_contact_email; this.modifier_contact_email = object.modifier_contact_email;

View File

@@ -78,10 +78,12 @@ class Item extends Component {
super(props); super(props);
this.state = { this.state = {
share_permission: this.props.item.share_permission, share_permission: this.props.item.share_permission,
share_permission_name: this.props.item.share_permission_name,
isOpIconShown: false, isOpIconShown: false,
isOpMenuOpen: false, // for mobile isOpMenuOpen: false, // for mobile
isPermSelectDialogOpen: false, // for mobile isPermSelectDialogOpen: false, // for mobile
unshared: false unshared: false,
isShowPermEditor: false,
}; };
this.permissions = ['rw', 'r']; this.permissions = ['rw', 'r'];
@@ -170,13 +172,18 @@ class Item extends Component {
}); });
} }
onEditPermission = (event) => {
event.nativeEvent.stopImmediatePropagation();
this.setState({isShowPermEditor: true});
}
render() { render() {
if (this.state.unshared) { if (this.state.unshared) {
return null; return null;
} }
const item = this.props.item; const item = this.props.item;
let { share_permission, isOpIconShown, isPermSelectDialogOpen } = this.state; let { share_permission, share_permission_name, isOpIconShown, isPermSelectDialogOpen, isShowPermEditor } = this.state;
let is_readonly = false; let is_readonly = false;
if (share_permission == 'r' || share_permission == 'preview') { if (share_permission == 'r' || share_permission == 'preview') {
@@ -201,14 +208,31 @@ class Item extends Component {
<span title={item.contact_email}>{item.user_name}</span> : item.group_name} <span title={item.contact_email}>{item.user_name}</span> : item.group_name}
</td> </td>
<td> <td>
{!isShowPermEditor && (
<div>
<span>{Utils.sharePerms(share_permission) || share_permission_name}</span>
{isOpIconShown && (
<a href="#"
role="button"
aria-label={gettext('Edit')}
title={gettext('Edit')}
className="fa fa-pencil-alt attr-action-icon"
onClick={this.onEditPermission}>
</a>
)}
</div>
)}
{isShowPermEditor && (
<SharePermissionEditor <SharePermissionEditor
repoID={item.repo_id} repoID={item.repo_id}
isTextMode={true} isTextMode={true}
isEditIconShow={isOpIconShown} isEditIconShow={isOpIconShown}
isEditing={true}
currentPermission={share_permission} currentPermission={share_permission}
permissions={this.permissions} permissions={this.permissions}
onPermissionChanged={this.changePerm} onPermissionChanged={this.changePerm}
/> />
)}
</td> </td>
<td><a href="#" role="button" aria-label={gettext('Unshare')} className={`action-icon sf2-icon-x3 ${isOpIconShown ? '': 'invisible'}`} title={gettext('Unshare')} onClick={this.unshare}></a></td> <td><a href="#" role="button" aria-label={gettext('Unshare')} className={`action-icon sf2-icon-x3 ${isOpIconShown ? '': 'invisible'}`} title={gettext('Unshare')} onClick={this.unshare}></a></td>
</tr> </tr>

View File

@@ -83,11 +83,13 @@ class Item extends Component {
let item = this.props.item; let item = this.props.item;
this.state = { this.state = {
share_permission: item.share_permission, share_permission: item.share_permission,
share_permission_name: item.share_permission_name,
is_admin: item.is_admin, is_admin: item.is_admin,
isOpIconShown: false, isOpIconShown: false,
isOpMenuOpen: false, // for mobile isOpMenuOpen: false, // for mobile
isPermSelectDialogOpen: false, // for mobile isPermSelectDialogOpen: false, // for mobile
unshared: false unshared: false,
isShowPermEditor: false,
}; };
let permissions = ['rw', 'r']; let permissions = ['rw', 'r'];
this.permissions = permissions; this.permissions = permissions;
@@ -171,12 +173,17 @@ class Item extends Component {
}); });
} }
onEditPermission = (event) => {
event.nativeEvent.stopImmediatePropagation();
this.setState({isShowPermEditor: true});
}
render() { render() {
if (this.state.unshared) { if (this.state.unshared) {
return null; return null;
} }
let { share_permission, is_admin, isOpIconShown, isPermSelectDialogOpen } = this.state; let { share_permission, share_permission_name, is_admin, isOpIconShown, isPermSelectDialogOpen, isShowPermEditor } = this.state;
let item = this.props.item; let item = this.props.item;
Object.assign(item, { Object.assign(item, {
share_permission: share_permission, share_permission: share_permission,
@@ -215,14 +222,31 @@ class Item extends Component {
{item.share_type == 'personal' ? <span title={item.contact_email}>{shareTo}</span> : shareTo} {item.share_type == 'personal' ? <span title={item.contact_email}>{shareTo}</span> : shareTo}
</td> </td>
<td> <td>
{!isShowPermEditor && (
<div>
<span>{Utils.sharePerms(share_permission) || share_permission_name}</span>
{isOpIconShown && (
<a href="#"
role="button"
aria-label={gettext('Edit')}
title={gettext('Edit')}
className="fa fa-pencil-alt attr-action-icon"
onClick={this.onEditPermission}>
</a>
)}
</div>
)}
{isShowPermEditor && (
<SharePermissionEditor <SharePermissionEditor
repoID={item.repo_id} repoID={item.repo_id}
isTextMode={true} isTextMode={true}
isEditing={true}
isEditIconShow={this.state.isOpIconShown} isEditIconShow={this.state.isOpIconShown}
currentPermission={share_permission} currentPermission={share_permission}
permissions={this.permissions} permissions={this.permissions}
onPermissionChanged={this.changePerm} onPermissionChanged={this.changePerm}
/> />
)}
</td> </td>
<td><a href="#" role="button" aria-label={gettext('Unshare')} className={`action-icon sf2-icon-x3 ${isOpIconShown ? '': 'invisible'}`} title={gettext('Unshare')} onClick={this.unshare}></a></td> <td><a href="#" role="button" aria-label={gettext('Unshare')} className={`action-icon sf2-icon-x3 ${isOpIconShown ? '': 'invisible'}`} title={gettext('Unshare')} onClick={this.unshare}></a></td>
</tr> </tr>

View File

@@ -16,9 +16,11 @@ from seahub.api2.throttling import UserRateThrottle
from seahub.profile.models import Profile from seahub.profile.models import Profile
from seahub.utils import is_org_context from seahub.utils import is_org_context
from seahub.base.templatetags.seahub_tags import email2nickname from seahub.base.templatetags.seahub_tags import email2nickname
from seahub.share.models import CustomSharePermissions
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class SharedFolders(APIView): class SharedFolders(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication) authentication_classes = (TokenAuthentication, SessionAuthentication)
@@ -48,6 +50,21 @@ class SharedFolders(APIView):
error_msg = 'Internal Server Error' error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
repo_id_list = []
for repo in shared_repos:
if repo.is_virtual:
continue
repo_id = repo.repo_id
repo_id_list.append(repo_id)
custom_permission_dict = {}
custom_permissions = CustomSharePermissions.objects.filter(repo_id__in=repo_id_list)
for custom_permission in custom_permissions:
custom_id = f'custom-{custom_permission.id}'
custom_permission_dict[custom_id] = custom_permission.name
returned_result = [] returned_result = []
shared_repos.sort(key=lambda x: x.repo_name) shared_repos.sort(key=lambda x: x.repo_name)
for repo in shared_repos: for repo in shared_repos:
@@ -61,6 +78,7 @@ class SharedFolders(APIView):
result['folder_name'] = repo.name result['folder_name'] = repo.name
result['share_type'] = repo.share_type result['share_type'] = repo.share_type
result['share_permission'] = repo.permission result['share_permission'] = repo.permission
result['share_permission_name'] = custom_permission_dict.get(repo.permission, '')
if repo.share_type == 'personal': if repo.share_type == 'personal':
result['user_name'] = email2nickname(repo.user) result['user_name'] = email2nickname(repo.user)
@@ -73,10 +91,15 @@ class SharedFolders(APIView):
if not group: if not group:
if is_org_context(request): if is_org_context(request):
seafile_api.org_unshare_subdir_for_group(org_id, seafile_api.org_unshare_subdir_for_group(org_id,
repo.repo_id, repo.origin_path, username, repo.group_id) repo.repo_id,
repo.origin_path,
username,
repo.group_id)
else: else:
seafile_api.unshare_subdir_for_group( seafile_api.unshare_subdir_for_group(repo.repo_id,
repo.repo_id, repo.origin_path, username, repo.group_id) repo.origin_path,
username,
repo.group_id)
continue continue
result['group_id'] = repo.group_id result['group_id'] = repo.group_id

View File

@@ -17,13 +17,15 @@ from seahub.profile.models import Profile
from seahub.utils import is_org_context, is_valid_username, send_perm_audit_msg from seahub.utils import is_org_context, is_valid_username, send_perm_audit_msg
from seahub.utils.repo import get_available_repo_perms from seahub.utils.repo import get_available_repo_perms
from seahub.base.templatetags.seahub_tags import email2nickname, email2contact_email from seahub.base.templatetags.seahub_tags import email2nickname, email2contact_email
from seahub.share.models import ExtraSharePermission, ExtraGroupsSharePermission from seahub.share.models import ExtraSharePermission, ExtraGroupsSharePermission, \
CustomSharePermissions
from seahub.share.utils import update_user_dir_permission, update_group_dir_permission,\ from seahub.share.utils import update_user_dir_permission, update_group_dir_permission,\
check_user_share_out_permission, check_group_share_out_permission, \ check_user_share_out_permission, check_group_share_out_permission, \
normalize_custom_permission_name normalize_custom_permission_name
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class SharedRepos(APIView): class SharedRepos(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication) authentication_classes = (TokenAuthentication, SessionAuthentication)
@@ -55,10 +57,26 @@ class SharedRepos(APIView):
error_msg = 'Internal Server Error' error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
repo_id_list = []
for repo in shared_repos:
if repo.is_virtual:
continue
repo_id = repo.repo_id
repo_id_list.append(repo_id)
custom_permission_dict = {}
custom_permissions = CustomSharePermissions.objects.filter(repo_id__in=repo_id_list)
for custom_permission in custom_permissions:
custom_id = f'custom-{custom_permission.id}'
custom_permission_dict[custom_id] = custom_permission.name
returned_result = [] returned_result = []
shared_repos.sort(key=lambda x: x.repo_name) shared_repos.sort(key=lambda x: x.repo_name)
usernames = [] usernames = []
gids = [] gids = []
for repo in shared_repos: for repo in shared_repos:
if repo.is_virtual: if repo.is_virtual:
continue continue
@@ -69,6 +87,7 @@ class SharedRepos(APIView):
result['encrypted'] = repo.encrypted result['encrypted'] = repo.encrypted
result['share_type'] = repo.share_type result['share_type'] = repo.share_type
result['share_permission'] = repo.permission result['share_permission'] = repo.permission
result['share_permission_name'] = custom_permission_dict.get(repo.permission, '')
result['modifier_email'] = repo.last_modifier result['modifier_email'] = repo.last_modifier
result['modifier_name'] = email2nickname(repo.last_modifier) result['modifier_name'] = email2nickname(repo.last_modifier)
result['modifier_contact_email'] = email2contact_email(repo.last_modifier) result['modifier_contact_email'] = email2contact_email(repo.last_modifier)
@@ -295,7 +314,6 @@ class SharedRepo(APIView):
permission = check_group_share_out_permission(repo_id, '/', group_id, is_org) permission = check_group_share_out_permission(repo_id, '/', group_id, is_org)
try: try:
if is_org: if is_org:
seaserv.del_org_group_repo(repo_id, org_id, group_id) seaserv.del_org_group_repo(repo_id, org_id, group_id)