1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-02 15:38:15 +00:00

update share/upload link expiration

This commit is contained in:
lian
2022-02-24 10:47:17 +08:00
parent 9de0e9b8ba
commit 2231d2d21f
4 changed files with 370 additions and 31 deletions

View File

@@ -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 && (
<FormGroup className="mb-0">
<dt className="text-secondary font-weight-normal">{gettext('Expiration Date:')}</dt>
<dd>{moment(sharedLinkInfo.expire_date).format('YYYY-MM-DD HH:mm:ss')}</dd>
{!this.state.isEditingExpiration &&
<dd style={{width:'250px'}} onMouseEnter={this.handleMouseOverExpirationEditIcon} onMouseLeave={this.handleMouseOutExpirationEditIcon}>
{moment(sharedLinkInfo.expire_date).format('YYYY-MM-DD HH:mm:ss')}
{this.state.isExpirationEditIconShow && (
<a href="#"
role="button"
aria-label={gettext('Edit')}
title={gettext('Edit')}
className="fa fa-pencil-alt attr-action-icon"
onClick={this.editingExpirationToggle}>
</a>
)}
</dd>
}
{this.state.isEditingExpiration &&
<div className="ml-4">
<FormGroup check>
<Label check>
<Input type="radio" name="set-exp" value="by-days" checked={this.state.setExp == 'by-days'} onChange={this.setExp} className="mr-1" />
<span>{gettext('Expiration days')}</span>
</Label>
{this.state.setExp == 'by-days' && (
<Fragment>
<InputGroup style={{width: inputWidth}}>
<Input type="text" value={this.state.expireDays} onChange={this.onExpireDaysChanged} />
<InputGroupAddon addonType="append">
<InputGroupText>{gettext('days')}</InputGroupText>
</InputGroupAddon>
</InputGroup>
{!this.state.isExpireDaysNoLimit && (
<FormText color="muted">{this.expirationLimitTip}</FormText>
)}
</Fragment>
)}
</FormGroup>
<FormGroup check>
<Label check>
<Input type="radio" name="set-exp" value="by-date" checked={this.state.setExp == 'by-date'} onChange={this.setExp} className="mr-1" />
<span>{gettext('Expiration time')}</span>
</Label>
{this.state.setExp == 'by-date' && (
<DateTimePicker
inputWidth={inputWidth}
disabledDate={this.disabledDate}
value={this.state.expDate}
onChange={this.onExpDateChanged}
/>
)}
</FormGroup>
<button className="btn btn-primary" onClick={this.updateExpiration}>{gettext('Update')}</button>{' '}
<button className="btn btn-secondary" onClick={this.editingExpirationToggle}>{gettext('Cancel')}</button>
</div>
}
</FormGroup>
)}

View File

@@ -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 && (
<FormGroup className="mb-0">
<dt className="text-secondary font-weight-normal">{gettext('Expiration Date:')}</dt>
<dd>{moment(sharedUploadInfo.expire_date).format('YYYY-MM-DD HH:mm:ss')}</dd>
{!this.state.isEditingExpiration &&
<dd style={{width:'250px'}} onMouseEnter={this.handleMouseOverExpirationEditIcon} onMouseLeave={this.handleMouseOutExpirationEditIcon}>
{moment(sharedUploadInfo.expire_date).format('YYYY-MM-DD HH:mm:ss')}
{this.state.isExpirationEditIconShow && (
<a href="#"
role="button"
aria-label={gettext('Edit')}
title={gettext('Edit')}
className="fa fa-pencil-alt attr-action-icon"
onClick={this.editExpirationToggle}>
</a>
)}
</dd>
}
{this.state.isEditingExpiration &&
<div className="ml-4">
<FormGroup check>
<Label check>
<Input type="radio" name="set-exp" value="by-days" checked={this.state.setExp == 'by-days'} onChange={this.setExp} className="mr-1" />
<span>{gettext('Expiration days')}</span>
</Label>
{this.state.setExp == 'by-days' && (
<Fragment>
<InputGroup style={{width: inputWidth}}>
<Input type="text" value={this.state.expireDays} onChange={this.onExpireDaysChanged} />
<InputGroupAddon addonType="append">
<InputGroupText>{gettext('days')}</InputGroupText>
</InputGroupAddon>
</InputGroup>
{!this.state.isExpireDaysNoLimit && (
<FormText color="muted">{this.expirationLimitTip}</FormText>
)}
</Fragment>
)}
</FormGroup>
<FormGroup check>
<Label check>
<Input type="radio" name="set-exp" value="by-date" checked={this.state.setExp == 'by-date'} onChange={this.setExp} className="mr-1" />
<span>{gettext('Expiration time')}</span>
</Label>
{this.state.setExp == 'by-date' && (
<DateTimePicker
inputWidth={inputWidth}
disabledDate={this.disabledDate}
value={this.state.expDate}
onChange={this.onExpDateChanged}
/>
)}
</FormGroup>
<button className="btn btn-primary" onClick={this.updateExpiration}>{gettext('Update')}</button>{' '}
<button className="btn btn-secondary" onClick={this.editExpirationToggle}>{gettext('Cancel')}</button>
</div>
}
</FormGroup>
)}
</Form>

View File

@@ -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)

View File

@@ -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.