diff --git a/frontend/src/components/dialog/generate-share-link.js b/frontend/src/components/dialog/generate-share-link.js
index 28d96fc88d..88a0ad3364 100644
--- a/frontend/src/components/dialog/generate-share-link.js
+++ b/frontend/src/components/dialog/generate-share-link.js
@@ -51,6 +51,8 @@ class GenerateShareLink extends React.Component {
isShowPasswordInput: shareLinkForceUsePassword ? true : false,
isPasswordVisible: false,
isExpireChecked: !this.isExpireDaysNoLimit,
+ isExpirationEditIconShow: false,
+ isEditingExpiration: false,
setExp: 'by-days',
expireDays: this.defaultExpireDays,
expDate: null,
@@ -337,6 +339,46 @@ class GenerateShareLink extends React.Component {
return true;
}
+ handleMouseOverExpirationEditIcon = () => {
+ this.setState({isExpirationEditIconShow: true});
+ }
+
+ handleMouseOutExpirationEditIcon = () => {
+ this.setState({isExpirationEditIconShow: false});
+ }
+
+ editingExpirationToggle = () => {
+ this.setState({isEditingExpiration: !this.state.isEditingExpiration});
+ }
+
+ updateExpiration = (e) => {
+
+ e.preventDefault();
+ e.nativeEvent.stopImmediatePropagation();
+
+ let { setExp, expireDays, expDate } = this.state;
+
+ let expirationTime = '';
+ if (setExp == 'by-days') {
+ expirationTime = moment().add(parseInt(expireDays), 'days').format();
+ } else {
+ expirationTime = expDate.format();
+ }
+
+ seafileAPI.updateShareLink(this.state.sharedLinkInfo.token, '', expirationTime).then((res) => {
+ let sharedLinkInfo = new ShareLink(res.data);
+ this.setState({
+ sharedLinkInfo: sharedLinkInfo,
+ isEditingExpiration: false,
+ });
+ let message = gettext('Successfully update expiration.');
+ toaster.success(message);
+ }).catch((error) => {
+ let errMessage = Utils.getErrorMsg(error);
+ toaster.danger(errMessage);
+ });
+ }
+
onNoticeMessageToggle = () => {
this.setState({isNoticeMessageShow: !this.state.isNoticeMessageShow});
}
@@ -421,7 +463,59 @@ class GenerateShareLink extends React.Component {
{sharedLinkInfo.expire_date && (
{gettext('Expiration Date:')}
- {moment(sharedLinkInfo.expire_date).format('YYYY-MM-DD HH:mm:ss')}
+ {!this.state.isEditingExpiration &&
+
+ {moment(sharedLinkInfo.expire_date).format('YYYY-MM-DD HH:mm:ss')}
+ {this.state.isExpirationEditIconShow && (
+
+
+ )}
+
+ }
+ {this.state.isEditingExpiration &&
+
+
+
+ {this.state.setExp == 'by-days' && (
+
+
+
+
+ {gettext('days')}
+
+
+ {!this.state.isExpireDaysNoLimit && (
+ {this.expirationLimitTip}
+ )}
+
+ )}
+
+
+
+ {this.state.setExp == 'by-date' && (
+
+ )}
+
+ {' '}
+
+
+ }
)}
diff --git a/frontend/src/components/dialog/generate-upload-link.js b/frontend/src/components/dialog/generate-upload-link.js
index ce44d629db..463db4f643 100644
--- a/frontend/src/components/dialog/generate-upload-link.js
+++ b/frontend/src/components/dialog/generate-upload-link.js
@@ -50,6 +50,8 @@ class GenerateUploadLink extends React.Component {
sharedUploadInfo: null,
isSendLinkShown: false,
isExpireChecked: !this.isExpireDaysNoLimit,
+ isExpirationEditIconShow: false,
+ isEditingExpiration: false,
setExp: 'by-days',
expireDays: this.defaultExpireDays,
expDate: null
@@ -195,11 +197,11 @@ class GenerateUploadLink extends React.Component {
this.setState({isExpireChecked: e.target.checked});
}
- setExp = (e) => {
- this.setState({
- setExp: e.target.value
- });
- }
+ setExp = (e) => {
+ this.setState({
+ setExp: e.target.value
+ });
+ }
disabledDate = (current) => {
if (!current) {
@@ -240,6 +242,46 @@ class GenerateUploadLink extends React.Component {
this.props.closeShareDialog();
}
+ handleMouseOverExpirationEditIcon = () => {
+ this.setState({isExpirationEditIconShow: true});
+ }
+
+ handleMouseOutExpirationEditIcon = () => {
+ this.setState({isExpirationEditIconShow: false});
+ }
+
+ editExpirationToggle = () => {
+ this.setState({isEditingExpiration: !this.state.isEditingExpiration});
+ }
+
+ updateExpiration = (e) => {
+
+ e.preventDefault();
+ e.nativeEvent.stopImmediatePropagation();
+
+ let { setExp, expireDays, expDate } = this.state;
+
+ let expirationTime = '';
+ if (setExp == 'by-days') {
+ expirationTime = moment().add(parseInt(expireDays), 'days').format();
+ } else {
+ expirationTime = expDate.format();
+ }
+
+ seafileAPI.updateUploadLink(this.state.sharedUploadInfo.token, expirationTime).then((res) => {
+ let sharedUploadInfo = new UploadLink(res.data);
+ this.setState({
+ sharedUploadInfo: sharedUploadInfo,
+ isEditingExpiration: false,
+ });
+ let message = gettext('Successfully update expiration.');
+ toaster.success(message);
+ }).catch((error) => {
+ let errMessage = Utils.getErrorMsg(error);
+ toaster.danger(errMessage);
+ });
+ }
+
deleteUploadLink = () => {
let sharedUploadInfo = this.state.sharedUploadInfo;
seafileAPI.deleteUploadLink(sharedUploadInfo.token).then(() => {
@@ -302,7 +344,59 @@ class GenerateUploadLink extends React.Component {
{sharedUploadInfo.expire_date && (
{gettext('Expiration Date:')}
- {moment(sharedUploadInfo.expire_date).format('YYYY-MM-DD HH:mm:ss')}
+ {!this.state.isEditingExpiration &&
+
+ {moment(sharedUploadInfo.expire_date).format('YYYY-MM-DD HH:mm:ss')}
+ {this.state.isExpirationEditIconShow && (
+
+
+ )}
+
+ }
+ {this.state.isEditingExpiration &&
+
+
+
+ {this.state.setExp == 'by-days' && (
+
+
+
+
+ {gettext('days')}
+
+
+ {!this.state.isExpireDaysNoLimit && (
+ {this.expirationLimitTip}
+ )}
+
+ )}
+
+
+
+ {this.state.setExp == 'by-date' && (
+
+ )}
+
+ {' '}
+
+
+ }
)}
diff --git a/seahub/api2/endpoints/share_links.py b/seahub/api2/endpoints/share_links.py
index 7e84ce9d52..7178d4a281 100644
--- a/seahub/api2/endpoints/share_links.py
+++ b/seahub/api2/endpoints/share_links.py
@@ -447,19 +447,12 @@ class ShareLink(APIView):
return Response(link_info)
def put(self, request, token):
- """ Update share link, currently only available for permission.
+ """ Update share link's permission and expiration.
Permission checking:
share link creater
"""
- # argument check
- try:
- perm = check_permissions_arg(request)
- except Exception:
- error_msg = 'permissions invalud.'
- return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
-
# resource check
try:
fs = FileShare.objects.get(token=token)
@@ -500,26 +493,102 @@ class ShareLink(APIView):
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
- if repo_folder_permission in (PERMISSION_PREVIEW_EDIT, PERMISSION_PREVIEW) \
- and perm != FileShare.PERM_VIEW_ONLY:
- error_msg = 'Permission denied.'
- return api_error(status.HTTP_403_FORBIDDEN, error_msg)
+ # argument check
+ permissions = request.data.get('permissions', '')
+ if permissions:
+ try:
+ perm = check_permissions_arg(request)
+ except Exception:
+ error_msg = 'permissions invalud.'
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
- if repo_folder_permission in (PERMISSION_READ) \
- and perm not in (FileShare.PERM_VIEW_DL, FileShare.PERM_VIEW_ONLY):
- error_msg = 'Permission denied.'
- return api_error(status.HTTP_403_FORBIDDEN, error_msg)
-
- if fs.s_type == 'f':
- file_name = os.path.basename(fs.path.rstrip('/'))
- can_edit, error_msg = can_edit_file(file_name, dirent.size, repo)
- if not can_edit and perm in (FileShare.PERM_EDIT_DL, FileShare.PERM_EDIT_ONLY):
+ if repo_folder_permission in (PERMISSION_PREVIEW_EDIT, PERMISSION_PREVIEW) \
+ and perm != FileShare.PERM_VIEW_ONLY:
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
- # update share link permission
- fs.permission = perm
- fs.save()
+ if repo_folder_permission in (PERMISSION_READ) \
+ and perm not in (FileShare.PERM_VIEW_DL, FileShare.PERM_VIEW_ONLY):
+ error_msg = 'Permission denied.'
+ return api_error(status.HTTP_403_FORBIDDEN, error_msg)
+
+ if fs.s_type == 'f':
+ file_name = os.path.basename(fs.path.rstrip('/'))
+ can_edit, error_msg = can_edit_file(file_name, dirent.size, repo)
+ if not can_edit and perm in (FileShare.PERM_EDIT_DL, FileShare.PERM_EDIT_ONLY):
+ error_msg = 'Permission denied.'
+ return api_error(status.HTTP_403_FORBIDDEN, error_msg)
+
+ # update share link permission
+ fs.permission = perm
+ fs.save()
+
+ expire_days = request.data.get('expire_days', '')
+ expiration_time = request.data.get('expiration_time', '')
+
+ if expire_days and expiration_time:
+ error_msg = 'Can not pass expire_days and expiration_time at the same time.'
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ if expire_days:
+
+ try:
+ expire_days = int(expire_days)
+ except ValueError:
+ error_msg = 'expire_days invalid.'
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ if expire_days <= 0:
+ error_msg = 'expire_days invalid.'
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ if SHARE_LINK_EXPIRE_DAYS_MIN > 0:
+ if expire_days < SHARE_LINK_EXPIRE_DAYS_MIN:
+ error_msg = _('Expire days should be greater or equal to %s') % \
+ SHARE_LINK_EXPIRE_DAYS_MIN
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ if SHARE_LINK_EXPIRE_DAYS_MAX > 0:
+ if expire_days > SHARE_LINK_EXPIRE_DAYS_MAX:
+ error_msg = _('Expire days should be less than or equal to %s') % \
+ SHARE_LINK_EXPIRE_DAYS_MAX
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ expire_date = timezone.now() + relativedelta(days=expire_days)
+ fs.expire_date = expire_date
+ fs.save()
+
+ if expiration_time:
+
+ try:
+ expire_date = dateutil.parser.isoparse(expiration_time)
+ except Exception as e:
+ logger.error(e)
+ error_msg = 'expiration_time invalid, should be iso format, for example: 2020-05-17T10:26:22+08:00'
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ expire_date = expire_date.astimezone(get_current_timezone()).replace(tzinfo=None)
+
+ if SHARE_LINK_EXPIRE_DAYS_MIN > 0:
+ expire_date_min_limit = timezone.now() + relativedelta(days=SHARE_LINK_EXPIRE_DAYS_MIN)
+ expire_date_min_limit = expire_date_min_limit.replace(hour=0).replace(minute=0).replace(second=0)
+
+ if expire_date < expire_date_min_limit:
+ error_msg = _('Expiration time should be later than %s.') % \
+ expire_date_min_limit.strftime("%Y-%m-%d %H:%M:%S")
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ if SHARE_LINK_EXPIRE_DAYS_MAX > 0:
+ expire_date_max_limit = timezone.now() + relativedelta(days=SHARE_LINK_EXPIRE_DAYS_MAX)
+ expire_date_max_limit = expire_date_max_limit.replace(hour=23).replace(minute=59).replace(second=59)
+
+ if expire_date > expire_date_max_limit:
+ error_msg = _('Expiration time should be earlier than %s.') % \
+ expire_date_max_limit.strftime("%Y-%m-%d %H:%M:%S")
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ fs.expire_date = expire_date
+ fs.save()
link_info = get_share_link_info(fs)
return Response(link_info)
diff --git a/seahub/api2/endpoints/upload_links.py b/seahub/api2/endpoints/upload_links.py
index 2d616350f0..935d2e4c9e 100644
--- a/seahub/api2/endpoints/upload_links.py
+++ b/seahub/api2/endpoints/upload_links.py
@@ -294,6 +294,88 @@ class UploadLink(APIView):
link_info = get_upload_link_info(uls)
return Response(link_info)
+ def put(self, request, token):
+ """ Update upload link's expiration.
+
+ Permission checking:
+ upload link creater
+ """
+
+ try:
+ uls = UploadLinkShare.objects.get(token=token)
+ except UploadLinkShare.DoesNotExist:
+ error_msg = 'token %s not found.' % token
+ return api_error(status.HTTP_404_NOT_FOUND, error_msg)
+
+ expire_days = request.data.get('expire_days', '')
+ expiration_time = request.data.get('expiration_time', '')
+ if expire_days and expiration_time:
+ error_msg = 'Can not pass expire_days and expiration_time at the same time.'
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ expire_date = None
+ if expire_days:
+ try:
+ expire_days = int(expire_days)
+ except ValueError:
+ error_msg = 'expire_days invalid.'
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ if expire_days <= 0:
+ error_msg = 'expire_days invalid.'
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ if UPLOAD_LINK_EXPIRE_DAYS_MIN > 0:
+ if expire_days < UPLOAD_LINK_EXPIRE_DAYS_MIN:
+ error_msg = _('Expire days should be greater or equal to %s') % \
+ UPLOAD_LINK_EXPIRE_DAYS_MIN
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ if UPLOAD_LINK_EXPIRE_DAYS_MAX > 0:
+ if expire_days > UPLOAD_LINK_EXPIRE_DAYS_MAX:
+ error_msg = _('Expire days should be less than or equal to %s') % \
+ UPLOAD_LINK_EXPIRE_DAYS_MAX
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ expire_date = timezone.now() + relativedelta(days=expire_days)
+ uls.expire_date = expire_date
+ uls.save()
+
+ elif expiration_time:
+
+ try:
+ expire_date = dateutil.parser.isoparse(expiration_time)
+ except Exception as e:
+ logger.error(e)
+ error_msg = 'expiration_time invalid, should be iso format, for example: 2020-05-17T10:26:22+08:00'
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ expire_date = expire_date.astimezone(get_current_timezone()).replace(tzinfo=None)
+
+ if UPLOAD_LINK_EXPIRE_DAYS_MIN > 0:
+ expire_date_min_limit = timezone.now() + relativedelta(days=UPLOAD_LINK_EXPIRE_DAYS_MIN)
+ expire_date_min_limit = expire_date_min_limit.replace(hour=0).replace(minute=0).replace(second=0)
+
+ if expire_date < expire_date_min_limit:
+ error_msg = _('Expiration time should be later than %s.') % \
+ expire_date_min_limit.strftime("%Y-%m-%d %H:%M:%S")
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ if UPLOAD_LINK_EXPIRE_DAYS_MAX > 0:
+ expire_date_max_limit = timezone.now() + relativedelta(days=UPLOAD_LINK_EXPIRE_DAYS_MAX)
+ expire_date_max_limit = expire_date_max_limit.replace(hour=23).replace(minute=59).replace(second=59)
+
+ if expire_date > expire_date_max_limit:
+ error_msg = _('Expiration time should be earlier than %s.') % \
+ expire_date_max_limit.strftime("%Y-%m-%d %H:%M:%S")
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ uls.expire_date = expire_date
+ uls.save()
+
+ link_info = get_upload_link_info(uls)
+ return Response(link_info)
+
def delete(self, request, token):
""" Delete upload link.