diff --git a/frontend/src/components/dialog/generate-share-link.js b/frontend/src/components/dialog/generate-share-link.js index 28d96fc88d..c01337f83b 100644 --- a/frontend/src/components/dialog/generate-share-link.js +++ b/frontend/src/components/dialog/generate-share-link.js @@ -1,8 +1,8 @@ -import React, { Fragment } from 'react'; +import React from 'react'; import PropTypes from 'prop-types'; import moment from 'moment'; import copy from 'copy-to-clipboard'; -import { Button, Form, FormGroup, Label, Input, InputGroup, InputGroupAddon, InputGroupText, Alert, FormText } from 'reactstrap'; +import { Button, Form, FormGroup, Label, Input, InputGroup, InputGroupAddon, Alert } from 'reactstrap'; import { isPro, gettext, shareLinkExpireDaysMin, shareLinkExpireDaysMax, shareLinkExpireDaysDefault, shareLinkForceUsePassword, shareLinkPasswordMinLength, shareLinkPasswordStrengthLevel, canSendShareLinkEmail } from '../../utils/constants'; import ShareLinkPermissionEditor from '../../components/select-editor/share-link-permission-editor'; import { seafileAPI } from '../../utils/seafile-api'; @@ -11,8 +11,8 @@ import ShareLink from '../../models/share-link'; import toaster from '../toast'; import Loading from '../loading'; import SendLink from '../send-link'; -import DateTimePicker from '../date-and-time-picker'; import SharedLink from '../shared-link'; +import SetLinkExpiration from '../set-link-expiration'; const propTypes = { itemPath: PropTypes.string.isRequired, @@ -31,27 +31,15 @@ class GenerateShareLink extends React.Component { this.isExpireDaysNoLimit = (shareLinkExpireDaysMin === 0 && shareLinkExpireDaysMax === 0 && shareLinkExpireDaysDefault == 0); this.defaultExpireDays = this.isExpireDaysNoLimit ? '' : shareLinkExpireDaysDefault; - let expirationLimitTip = ''; - if (shareLinkExpireDaysMin !== 0 && shareLinkExpireDaysMax !== 0) { - expirationLimitTip = gettext('{minDays_placeholder} - {maxDays_placeholder} days') - .replace('{minDays_placeholder}', shareLinkExpireDaysMin) - .replace('{maxDays_placeholder}', shareLinkExpireDaysMax); - } else if (shareLinkExpireDaysMin !== 0 && shareLinkExpireDaysMax === 0) { - expirationLimitTip = gettext('Greater than or equal to {minDays_placeholder} days') - .replace('{minDays_placeholder}', shareLinkExpireDaysMin); - } else if (shareLinkExpireDaysMin === 0 && shareLinkExpireDaysMax !== 0) { - expirationLimitTip = gettext('Less than or equal to {maxDays_placeholder} days') - .replace('{maxDays_placeholder}', shareLinkExpireDaysMax); - } - this.expirationLimitTip = expirationLimitTip; - this.state = { isOpIconShown: false, isValidate: false, isShowPasswordInput: shareLinkForceUsePassword ? true : false, isPasswordVisible: false, isExpireChecked: !this.isExpireDaysNoLimit, - setExp: 'by-days', + isExpirationEditIconShow: false, + isEditingExpiration: false, + expType: 'by-days', expireDays: this.defaultExpireDays, expDate: null, password: '', @@ -116,33 +104,12 @@ class GenerateShareLink extends React.Component { } } - setExp = (e) => { + setExpType = (e) => { this.setState({ - setExp: e.target.value + expType: e.target.value }); } - disabledDate = (current) => { - if (!current) { - // allow empty select - return false; - } - - if (this.isExpireDaysNoLimit) { - return current.isBefore(moment(), 'day'); - } - - const startDay = moment().add(shareLinkExpireDaysMin, 'days'); - const endDay = moment().add(shareLinkExpireDaysMax, 'days'); - if (shareLinkExpireDaysMin !== 0 && shareLinkExpireDaysMax !== 0) { - return current.isBefore(startDay, 'day') || current.isAfter(endDay, 'day'); - } else if (shareLinkExpireDaysMin !== 0 && shareLinkExpireDaysMax === 0) { - return current.isBefore(startDay, 'day'); - } else if (shareLinkExpireDaysMin === 0 && shareLinkExpireDaysMax !== 0) { - return current.isBefore(moment(), 'day') || current.isAfter(endDay, 'day'); - } - } - onExpDateChanged = (value) => { this.setState({ expDate: value @@ -197,7 +164,7 @@ class GenerateShareLink extends React.Component { if (isValid) { this.setState({errorInfo: ''}); let { itemPath, repoID } = this.props; - let { password, isExpireChecked, setExp, expireDays, expDate } = this.state; + let { password, isExpireChecked, expType, expireDays, expDate } = this.state; let permissions; if (isPro) { const permissionDetails = Utils.getShareLinkPermissionObject(this.state.currentPermission).permissionDetails; @@ -205,7 +172,7 @@ class GenerateShareLink extends React.Component { } let expirationTime = ''; if (isExpireChecked) { - if (setExp == 'by-days') { + if (expType == 'by-days') { expirationTime = moment().add(parseInt(expireDays), 'days').format(); } else { expirationTime = expDate.format(); @@ -243,6 +210,7 @@ class GenerateShareLink extends React.Component { passwdnew: '', isShowPasswordInput: shareLinkForceUsePassword ? true : false, expireDays: this.defaultExpireDays, + expDate: null, isExpireChecked: !this.isExpireDaysNoLimit, errorInfo: '', sharedLinkInfo: null, @@ -264,7 +232,7 @@ class GenerateShareLink extends React.Component { } validateParamsInput = () => { - let { isShowPasswordInput, password, passwdnew, isExpireChecked, setExp, expireDays, expDate } = this.state; + let { isShowPasswordInput, password, passwdnew, isExpireChecked, expType, expireDays, expDate } = this.state; // validate password if (isShowPasswordInput) { @@ -287,7 +255,7 @@ class GenerateShareLink extends React.Component { } if (isExpireChecked) { - if (setExp == 'by-date') { + if (expType == 'by-date') { if (!expDate) { this.setState({errorInfo: gettext('Please select an expiration time')}); return false; @@ -337,6 +305,44 @@ 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 { expType, expireDays, expDate } = this.state; + + let expirationTime = ''; + if (expType == '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, + }); + }).catch((error) => { + let errMessage = Utils.getErrorMsg(error); + toaster.danger(errMessage); + }); + } + onNoticeMessageToggle = () => { this.setState({isNoticeMessageShow: !this.state.isNoticeMessageShow}); } @@ -373,7 +379,7 @@ class GenerateShareLink extends React.Component { let passwordLengthTip = gettext('(at least {passwordMinLength} characters and includes {passwordStrengthLevel} of the following: number, upper letter, lower letter and other symbols)'); passwordLengthTip = passwordLengthTip.replace('{passwordMinLength}', shareLinkPasswordMinLength) - .replace('{passwordStrengthLevel}', shareLinkPasswordStrengthLevel); + .replace('{passwordStrengthLevel}', shareLinkPasswordStrengthLevel); const { userPerm } = this.props; const { isCustomPermission } = Utils.getUserPermission(userPerm); @@ -421,7 +427,39 @@ 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 && +
+ +
+ + +
+
+ }
)} @@ -470,15 +508,15 @@ class GenerateShareLink extends React.Component {
{shareLinkForceUsePassword ? ( - + ) : ( - + )} {this.state.isShowPasswordInput &&
@@ -511,39 +549,17 @@ class GenerateShareLink extends React.Component { {this.state.isExpireChecked &&
- - - {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..f6ec12affc 100644 --- a/frontend/src/components/dialog/generate-upload-link.js +++ b/frontend/src/components/dialog/generate-upload-link.js @@ -1,16 +1,16 @@ -import React, { Fragment } from 'react'; +import React from 'react'; import PropTypes from 'prop-types'; import copy from 'copy-to-clipboard'; import moment from 'moment'; -import { Button, Form, FormGroup, Label, Input, InputGroup, InputGroupAddon, InputGroupText, Alert, FormText } from 'reactstrap'; +import { Button, Form, FormGroup, Label, Input, InputGroup, InputGroupAddon, Alert } from 'reactstrap'; import { gettext, shareLinkForceUsePassword, shareLinkPasswordMinLength, shareLinkPasswordStrengthLevel, canSendShareLinkEmail, uploadLinkExpireDaysMin, uploadLinkExpireDaysMax, uploadLinkExpireDaysDefault } from '../../utils/constants'; import { seafileAPI } from '../../utils/seafile-api'; import { Utils } from '../../utils/utils'; import UploadLink from '../../models/upload-link'; import toaster from '../toast'; import SendLink from '../send-link'; -import DateTimePicker from '../date-and-time-picker'; import SharedLink from '../shared-link'; +import SetLinkExpiration from '../set-link-expiration'; const propTypes = { itemPath: PropTypes.string.isRequired, @@ -27,20 +27,6 @@ class GenerateUploadLink extends React.Component { this.isExpireDaysNoLimit = (uploadLinkExpireDaysMin === 0 && uploadLinkExpireDaysMax === 0 && uploadLinkExpireDaysDefault == 0); this.defaultExpireDays = this.isExpireDaysNoLimit ? '' : uploadLinkExpireDaysDefault; - let expirationLimitTip = ''; - if (uploadLinkExpireDaysMin !== 0 && uploadLinkExpireDaysMax !== 0) { - expirationLimitTip = gettext('{minDays_placeholder} - {maxDays_placeholder} days') - .replace('{minDays_placeholder}', uploadLinkExpireDaysMin) - .replace('{maxDays_placeholder}', uploadLinkExpireDaysMax); - } else if (uploadLinkExpireDaysMin !== 0 && uploadLinkExpireDaysMax === 0) { - expirationLimitTip = gettext('Greater than or equal to {minDays_placeholder} days') - .replace('{minDays_placeholder}', uploadLinkExpireDaysMin); - } else if (uploadLinkExpireDaysMin === 0 && uploadLinkExpireDaysMax !== 0) { - expirationLimitTip = gettext('Less than or equal to {maxDays_placeholder} days') - .replace('{maxDays_placeholder}', uploadLinkExpireDaysMax); - } - this.expirationLimitTip = expirationLimitTip; - this.state = { showPasswordInput: shareLinkForceUsePassword ? true : false, passwordVisible: false, @@ -50,7 +36,9 @@ class GenerateUploadLink extends React.Component { sharedUploadInfo: null, isSendLinkShown: false, isExpireChecked: !this.isExpireDaysNoLimit, - setExp: 'by-days', + isExpirationEditIconShow: false, + isEditingExpiration: false, + expType: 'by-days', expireDays: this.defaultExpireDays, expDate: null }; @@ -124,11 +112,11 @@ class GenerateUploadLink extends React.Component { this.setState({errorInfo: ''}); let { itemPath, repoID } = this.props; - let { password, isExpireChecked, setExp, expireDays, expDate } = this.state; + let { password, isExpireChecked, expType, expireDays, expDate } = this.state; let expirationTime = ''; if (isExpireChecked) { - if (setExp == 'by-days') { + if (expType == 'by-days') { expirationTime = moment().add(parseInt(expireDays), 'days').format(); } else { expirationTime = expDate.format(); @@ -146,7 +134,7 @@ class GenerateUploadLink extends React.Component { } validateParamsInput = () => { - let { showPasswordInput, password, passwordnew, isExpireChecked, setExp, expireDays, expDate } = this.state; + let { showPasswordInput, password, passwordnew, isExpireChecked, expType, expireDays, expDate } = this.state; // check password params if (showPasswordInput) { @@ -169,7 +157,7 @@ class GenerateUploadLink extends React.Component { } if (isExpireChecked) { - if (setExp == 'by-date') { + if (expType == 'by-date') { if (!expDate) { this.setState({errorInfo: gettext('Please select an expiration time')}); return false; @@ -195,31 +183,10 @@ class GenerateUploadLink extends React.Component { this.setState({isExpireChecked: e.target.checked}); } - setExp = (e) => { - this.setState({ - setExp: e.target.value - }); - } - - disabledDate = (current) => { - if (!current) { - // allow empty select - return false; - } - - if (this.isExpireDaysNoLimit) { - return current.isBefore(moment(), 'day'); - } - - const startDay = moment().add(uploadLinkExpireDaysMin, 'days'); - const endDay = moment().add(uploadLinkExpireDaysMax, 'days'); - if (uploadLinkExpireDaysMin !== 0 && uploadLinkExpireDaysMax !== 0) { - return current.isBefore(startDay, 'day') || current.isAfter(endDay, 'day'); - } else if (uploadLinkExpireDaysMin !== 0 && uploadLinkExpireDaysMax === 0) { - return current.isBefore(startDay, 'day'); - } else if (uploadLinkExpireDaysMin === 0 && uploadLinkExpireDaysMax !== 0) { - return current.isBefore(moment(), 'day') || current.isAfter(endDay, 'day'); - } + setExpType = (e) => { + this.setState({ + expType: e.target.value + }); } onExpDateChanged = (value) => { @@ -240,12 +207,51 @@ 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 { expType, expireDays, expDate } = this.state; + + let expirationTime = ''; + if (expType == '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, + }); + }).catch((error) => { + let errMessage = Utils.getErrorMsg(error); + toaster.danger(errMessage); + }); + } + deleteUploadLink = () => { let sharedUploadInfo = this.state.sharedUploadInfo; seafileAPI.deleteUploadLink(sharedUploadInfo.token).then(() => { this.setState({ showPasswordInput: shareLinkForceUsePassword ? true : false, expireDays: this.defaultExpireDays, + expDate: null, isExpireChecked: !this.isExpireDaysNoLimit, password: '', passwordnew: '', @@ -269,7 +275,7 @@ class GenerateUploadLink extends React.Component { let passwordLengthTip = gettext('(at least {passwordMinLength} characters and includes {passwordStrengthLevel} of the following: number, upper letter, lower letter and other symbols)'); passwordLengthTip = passwordLengthTip.replace('{passwordMinLength}', shareLinkPasswordMinLength) - .replace('{passwordStrengthLevel}', shareLinkPasswordStrengthLevel); + .replace('{passwordStrengthLevel}', shareLinkPasswordStrengthLevel); if (this.state.sharedUploadInfo) { let sharedUploadInfo = this.state.sharedUploadInfo; @@ -302,7 +308,39 @@ 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 && +
+ +
+ + +
+
+ }
)} @@ -324,15 +362,15 @@ class GenerateUploadLink extends React.Component { {this.state.showPasswordInput && @@ -366,39 +404,17 @@ class GenerateUploadLink extends React.Component { {this.state.isExpireChecked &&
- - - {this.state.setExp == 'by-days' && ( - - - - - {gettext('days')} - - - {!this.state.isExpireDaysNoLimit && ( - {this.expirationLimitTip} - )} - - )} - - - - {this.state.setExp == 'by-date' && ( - - )} - +
}
diff --git a/frontend/src/components/set-link-expiration.js b/frontend/src/components/set-link-expiration.js new file mode 100644 index 0000000000..08d8d9ec33 --- /dev/null +++ b/frontend/src/components/set-link-expiration.js @@ -0,0 +1,119 @@ +import React, { Fragment } from 'react'; +import PropTypes from 'prop-types'; +import moment from 'moment'; +import { FormGroup, Label, Input, InputGroup, InputGroupAddon, InputGroupText, FormText } from 'reactstrap'; +import { gettext } from '../utils/constants'; +import { Utils } from '../utils/utils'; +import DateTimePicker from './date-and-time-picker'; + +const propTypes = { + minDays: PropTypes.number.isRequired, + maxDays: PropTypes.number.isRequired, + defaultDays: PropTypes.number.isRequired, + expType: PropTypes.string.isRequired, + setExpType: PropTypes.func.isRequired, + expireDays: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.number + ]), + onExpireDaysChanged: PropTypes.func.isRequired, + expDate: PropTypes.object, + onExpDateChanged: PropTypes.func.isRequired +}; + +const inputWidth = Utils.isDesktop() ? 250 : 210; + +class SetLinkExpiration extends React.Component { + + constructor(props) { + super(props); + + const { minDays, maxDays, defaultDays } = this.props; + this.isExpireDaysNoLimit = (minDays === 0 && maxDays === 0 && defaultDays == 0); + + let expirationLimitTip = ''; + if (minDays !== 0 && maxDays !== 0) { + expirationLimitTip = gettext('{minDays_placeholder} - {maxDays_placeholder} days') + .replace('{minDays_placeholder}', minDays) + .replace('{maxDays_placeholder}', maxDays); + } else if (minDays !== 0 && maxDays === 0) { + expirationLimitTip = gettext('Greater than or equal to {minDays_placeholder} days') + .replace('{minDays_placeholder}', minDays); + } else if (minDays === 0 && maxDays !== 0) { + expirationLimitTip = gettext('Less than or equal to {maxDays_placeholder} days') + .replace('{maxDays_placeholder}', maxDays); + } + this.expirationLimitTip = expirationLimitTip; + } + + disabledDate = (current) => { + if (!current) { + // allow empty select + return false; + } + + if (this.isExpireDaysNoLimit) { + return current.isBefore(moment(), 'day'); + } + + const { minDays, maxDays } = this.props; + const startDay = moment().add(minDays, 'days'); + const endDay = moment().add(maxDays, 'days'); + if (minDays !== 0 && maxDays !== 0) { + return current.isBefore(startDay, 'day') || current.isAfter(endDay, 'day'); + } else if (minDays !== 0 && maxDays === 0) { + return current.isBefore(startDay, 'day'); + } else if (minDays === 0 && maxDays !== 0) { + return current.isBefore(moment(), 'day') || current.isAfter(endDay, 'day'); + } + } + + render() { + const { + expType, setExpType, + expireDays, onExpireDaysChanged, + expDate, onExpDateChanged + } = this.props; + return ( + + + + {expType == 'by-days' && ( + + + + + {gettext('days')} + + + {!this.isExpireDaysNoLimit && ( + {this.expirationLimitTip} + )} + + )} + + + + {expType == 'by-date' && ( + + )} + + + ); + } +} + +SetLinkExpiration.propTypes = propTypes; + +export default SetLinkExpiration; diff --git a/seahub/api2/endpoints/share_links.py b/seahub/api2/endpoints/share_links.py index 7e84ce9d52..4dcde26a95 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 invalid.' + 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.