diff --git a/frontend/package.json b/frontend/package.json index 0bc334061a..1b6fb1b8a5 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -6,7 +6,7 @@ "@reach/router": "^1.2.0", "@seafile/react-image-lightbox": "0.0.1", "@seafile/resumablejs": "^1.1.15", - "@seafile/seafile-calendar": "0.0.8", + "@seafile/seafile-calendar": "0.0.12", "@seafile/seafile-editor": "^0.2.84", "MD5": "^1.3.0", "autoprefixer": "7.1.6", diff --git a/frontend/src/components/date-and-time-picker.js b/frontend/src/components/date-and-time-picker.js new file mode 100644 index 0000000000..d9511735d6 --- /dev/null +++ b/frontend/src/components/date-and-time-picker.js @@ -0,0 +1,78 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import moment from 'moment'; +import Calendar from '@seafile/seafile-calendar'; +import DatePicker from '@seafile/seafile-calendar/lib/Picker'; +import { translateCalendar } from '../utils/date-format-utils'; + +import '@seafile/seafile-calendar/assets/index.css'; +import '../css/date-and-time-picker.css'; + +const propsTypes = { + disabledDate: PropTypes.func.isRequired, + value: PropTypes.object, + onChange: PropTypes.func.isRequired +}; + +const FORMAT = 'YYYY-MM-DD HH:mm'; + +class Picker extends React.Component { + + constructor(props) { + super(props); + this.defaultCalendarValue = null; + this.calendarContainerRef = React.createRef(); + } + + componentDidMount() { + let lang = window.app.config.lang; + this.defaultCalendarValue = moment().locale(lang).clone(); + } + + getCalendarContainer = () => { + return this.calendarContainerRef.current; + } + + render() { + const props = this.props; + const calendar = (); + return ( + + { + ({value}) => { + return ( +
+ +
+
+ ); + } + } + + ); + } +} + +Picker.propsTypes = propsTypes; + +export default Picker; diff --git a/frontend/src/components/dialog/generate-share-link.js b/frontend/src/components/dialog/generate-share-link.js index 08cf6f4d94..9799103c0c 100644 --- a/frontend/src/components/dialog/generate-share-link.js +++ b/frontend/src/components/dialog/generate-share-link.js @@ -2,7 +2,7 @@ import React, { Fragment } 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, Alert } from 'reactstrap'; +import { Button, Form, FormGroup, Label, Input, InputGroup, InputGroupAddon, InputGroupText, Alert, FormText } from 'reactstrap'; import { isPro, gettext, shareLinkExpireDaysMin, shareLinkExpireDaysMax, shareLinkExpireDaysDefault, shareLinkPasswordMinLength, canSendShareLinkEmail } from '../../utils/constants'; import ShareLinkPermissionEditor from '../../components/select-editor/share-link-permission-editor'; import { seafileAPI } from '../../utils/seafile-api'; @@ -11,6 +11,7 @@ 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'; const propTypes = { itemPath: PropTypes.string.isRequired, @@ -18,23 +19,41 @@ const propTypes = { closeShareDialog: PropTypes.func.isRequired, }; +const inputWidth = Utils.isDesktop() ? 250 : 210; + class GenerateShareLink extends React.Component { constructor(props) { super(props); - this.isExpireDaysNoLimit = (parseInt(shareLinkExpireDaysMin) === 0 && parseInt(shareLinkExpireDaysMax) === 0 && shareLinkExpireDaysDefault == 0); + 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: false, isPasswordVisible: false, isExpireChecked: !this.isExpireDaysNoLimit, + setExp: 'by-days', + expireDays: this.defaultExpireDays, + expDate: null, password: '', passwdnew: '', - expireDays: this.defaultExpireDays, errorInfo: '', sharedLinkInfo: null, isNoticeMessageShow: false, @@ -93,6 +112,39 @@ class GenerateShareLink extends React.Component { } } + 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(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 + }); + } + onPasswordInputChecked = () => { this.setState({ isShowPasswordInput: !this.state.isShowPasswordInput, @@ -135,14 +187,21 @@ class GenerateShareLink extends React.Component { if (isValid) { this.setState({errorInfo: ''}); let { itemPath, repoID } = this.props; - let { password, isExpireChecked, expireDays } = this.state; + let { password, isExpireChecked, setExp, expireDays, expDate } = this.state; let permissions; if (isPro) { const permissionDetails = Utils.getShareLinkPermissionObject(this.state.currentPermission).permissionDetails; permissions = JSON.stringify(permissionDetails); } - const expireDaysSent = isExpireChecked ? expireDays : ''; - seafileAPI.createShareLink(repoID, itemPath, password, expireDaysSent, permissions).then((res) => { + let expirationTime = ''; + if (isExpireChecked) { + if (setExp == 'by-days') { + expirationTime = moment().add(parseInt(expireDays), 'days').format(); + } else { + expirationTime = expDate.format(); + } + } + seafileAPI.createShareLink(repoID, itemPath, password, expirationTime, permissions).then((res) => { let sharedLinkInfo = new ShareLink(res.data); this.setState({sharedLinkInfo: sharedLinkInfo}); }).catch((error) => { @@ -195,7 +254,8 @@ class GenerateShareLink extends React.Component { } validateParamsInput = () => { - let { isShowPasswordInput , password, passwdnew, isExpireChecked, expireDays } = this.state; + let { isShowPasswordInput, password, passwdnew, isExpireChecked, setExp, expireDays, expDate } = this.state; + // validate password if (isShowPasswordInput) { if (password.length === 0) { @@ -212,22 +272,17 @@ class GenerateShareLink extends React.Component { } } - // validate days - // no limit - let reg = /^\d+$/; - if (this.isExpireDaysNoLimit) { - if (isExpireChecked) { - if (!expireDays) { - this.setState({errorInfo: 'Please enter days'}); + if (isExpireChecked) { + if (setExp == 'by-date') { + if (!expDate) { + this.setState({errorInfo: 'Please select an expiration time'}); return false; } - if (!reg.test(expireDays)) { - this.setState({errorInfo: 'Please enter a non-negative integer'}); - return false; - } - this.setState({expireDays: parseInt(expireDays)}); + return true; } - } else { + + // by days + let reg = /^\d+$/; if (!expireDays) { this.setState({errorInfo: 'Please enter days'}); return false; @@ -238,10 +293,10 @@ class GenerateShareLink extends React.Component { } expireDays = parseInt(expireDays); - let minDays = parseInt(shareLinkExpireDaysMin); - let maxDays = parseInt(shareLinkExpireDaysMax); + let minDays = shareLinkExpireDaysMin; + let maxDays = shareLinkExpireDaysMax; - if (minDays !== 0 && maxDays !== maxDays) { + if (minDays !== 0 && maxDays == 0) { if (expireDays < minDays) { this.setState({errorInfo: 'Please enter valid days'}); return false; @@ -261,6 +316,7 @@ class GenerateShareLink extends React.Component { return false; } } + this.setState({expireDays: expireDays}); } @@ -385,80 +441,92 @@ class GenerateShareLink extends React.Component {
- - {this.state.isShowPasswordInput && - - {' '}{passwordLengthTip} - - - - - - - - - - - } - {this.isExpireDaysNoLimit && ( - - - + {this.state.isShowPasswordInput && +
+ + + {passwordLengthTip} + + + + + + + - {this.state.isExpireChecked && + + + + +
+ } +
+ + + {this.state.isExpireChecked && +
+ {this.state.setExp == 'by-days' && ( + + + + + {gettext('days')} + + + {!this.state.isExpireDaysNoLimit && ( + {this.expirationLimitTip} + )} + + )} - } - - )} - {!this.isExpireDaysNoLimit && ( - - - - - - - - - )} + +
+ } +
{isPro && ( - - - - + + {this.state.permissionOptions.map((item, index) => { return ( - - + )} {this.state.errorInfo && {gettext(this.state.errorInfo)}} diff --git a/frontend/src/components/dialog/generate-upload-link.js b/frontend/src/components/dialog/generate-upload-link.js index 2028236465..adcf9b0472 100644 --- a/frontend/src/components/dialog/generate-upload-link.js +++ b/frontend/src/components/dialog/generate-upload-link.js @@ -2,13 +2,14 @@ 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, Alert } from 'reactstrap'; +import { Button, Form, FormGroup, Label, Input, InputGroup, InputGroupAddon, InputGroupText, Alert } from 'reactstrap'; import { gettext, shareLinkPasswordMinLength, canSendShareLinkEmail } 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'; const propTypes = { itemPath: PropTypes.string.isRequired, @@ -16,6 +17,8 @@ const propTypes = { closeShareDialog: PropTypes.func.isRequired, }; +const inputWidth = Utils.isDesktop() ? 250 : 210; + class GenerateUploadLink extends React.Component { constructor(props) { super(props); @@ -27,7 +30,9 @@ class GenerateUploadLink extends React.Component { sharedUploadInfo: null, isSendLinkShown: false, isExpireChecked: false, - expireDays: 0, + setExp: 'by-days', + expireDays: '', + expDate: null }; } @@ -88,13 +93,23 @@ class GenerateUploadLink extends React.Component { } generateUploadLink = () => { - let path = this.props.itemPath; - let repoID = this.props.repoID; - let { password, expireDays } = this.state; - let isValid = this.validateParamsInput(); if (isValid) { - seafileAPI.createUploadLink(repoID, path, password, expireDays).then((res) => { + this.setState({errorInfo: ''}); + + let { itemPath, repoID } = this.props; + let { password, isExpireChecked, setExp, expireDays, expDate } = this.state; + + let expirationTime = ''; + if (isExpireChecked) { + if (setExp == 'by-days') { + expirationTime = moment().add(parseInt(expireDays), 'days').format(); + } else { + expirationTime = expDate.format(); + } + } + + seafileAPI.createUploadLink(repoID, itemPath, password, expirationTime).then((res) => { let sharedUploadInfo = new UploadLink(res.data); this.setState({sharedUploadInfo: sharedUploadInfo}); }).catch(error => { @@ -105,7 +120,7 @@ class GenerateUploadLink extends React.Component { } validateParamsInput = () => { - let { showPasswordInput , password, passwordnew, isExpireChecked, expireDays } = this.state; + let { showPasswordInput, password, passwordnew, isExpireChecked, setExp, expireDays, expDate } = this.state; // check password params if (showPasswordInput) { @@ -123,9 +138,16 @@ class GenerateUploadLink extends React.Component { } } - // check expire day params - let reg = /^\d+$/; if (isExpireChecked) { + if (setExp == 'by-date') { + if (!expDate) { + this.setState({errorInfo: 'Please select an expiration time'}); + return false; + } + return true; + } + + let reg = /^\d+$/; if (!expireDays) { this.setState({errorInfo: gettext('Please enter days')}); return false; @@ -143,6 +165,27 @@ 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; + } + + return current.isBefore(moment(), 'day'); + } + + onExpDateChanged = (value) => { + this.setState({ + expDate: value + }); + } + onExpireDaysChanged = (e) => { let day = e.target.value.trim(); this.setState({expireDays: day}); @@ -218,36 +261,67 @@ class GenerateUploadLink extends React.Component { + {this.state.showPasswordInput && +
+ + + {passwordLengthTip} + + + + + + + + + + + + +
+ }
- {this.state.showPasswordInput && - - {/* todo translate */} - {' '}{passwordLengthTip} - - - - - - - - - - - } + {this.state.isExpireChecked && +
+ + + {this.state.setExp == 'by-days' && ( + + + + {gettext('days')} + + + )} + + + + {this.state.setExp == 'by-date' && ( + + )} + +
+ }
- {this.state.isExpireChecked && - - - - } {this.state.errorInfo && {this.state.errorInfo}} diff --git a/frontend/src/css/date-and-time-picker.css b/frontend/src/css/date-and-time-picker.css new file mode 100644 index 0000000000..64b1f3b669 --- /dev/null +++ b/frontend/src/css/date-and-time-picker.css @@ -0,0 +1,13 @@ + /* to overwrite styles from seahub_react.css */ +.rc-calendar-table { + table-layout: auto; +} +.rc-calendar-table tbody tr { + height: auto; +} + +/* overwrite some styles */ +/* for 'markdown file view -> share -> picker' */ +.rc-calendar-input:focus { + border-color: transparent; +} diff --git a/frontend/src/pages/sys-admin/index.js b/frontend/src/pages/sys-admin/index.js index 36990b4dc4..9226aa2ad6 100644 --- a/frontend/src/pages/sys-admin/index.js +++ b/frontend/src/pages/sys-admin/index.js @@ -69,8 +69,8 @@ import FileScanRecords from './file-scan-records'; import VirusScanRecords from './virus-scan-records'; import WorkWeixinDepartments from './work-weixin-departments'; import DingtalkDepartments from './dingtalk-departments'; - import Invitations from './invitations/invitations'; + import StatisticFile from './statistic/statistic-file'; import StatisticStorage from './statistic/statistic-storage'; import StatisticTraffic from './statistic/statistic-traffic'; diff --git a/seahub/api2/endpoints/share_links.py b/seahub/api2/endpoints/share_links.py index c9004db0b0..843aed8c20 100644 --- a/seahub/api2/endpoints/share_links.py +++ b/seahub/api2/endpoints/share_links.py @@ -2,9 +2,11 @@ import os import stat import json +import pytz import logging import posixpath from constance import config +from datetime import datetime from dateutil.relativedelta import relativedelta from rest_framework.authentication import SessionAuthentication @@ -244,33 +246,71 @@ class ShareLinks(APIView): error_msg = _('Password is too short.') return api_error(status.HTTP_400_BAD_REQUEST, error_msg) - try: - expire_days = int(request.data.get('expire_days', 0)) - except ValueError: - error_msg = 'expire_days invalid.' + 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 <= 0: - if SHARE_LINK_EXPIRE_DAYS_DEFAULT > 0: - expire_days = SHARE_LINK_EXPIRE_DAYS_DEFAULT - - 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 + 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 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 + if expire_days <= 0: + error_msg = 'expire_days invalid.' return api_error(status.HTTP_400_BAD_REQUEST, error_msg) - if expire_days <= 0: - expire_date = None - else: + 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) + elif expiration_time: + + try: + expire_date = datetime.fromisoformat(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(pytz.UTC).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) + + else: + if SHARE_LINK_EXPIRE_DAYS_DEFAULT > 0: + expire_date = timezone.now() + relativedelta(days=SHARE_LINK_EXPIRE_DAYS_DEFAULT) + try: perm = check_permissions_arg(request) except Exception: diff --git a/seahub/api2/endpoints/upload_links.py b/seahub/api2/endpoints/upload_links.py index db01771d5c..b571eaac6d 100644 --- a/seahub/api2/endpoints/upload_links.py +++ b/seahub/api2/endpoints/upload_links.py @@ -1,8 +1,10 @@ # Copyright (c) 2012-2016 Seafile Ltd. import os import json +import pytz import logging from constance import config +from datetime import datetime from dateutil.relativedelta import relativedelta from rest_framework.authentication import SessionAuthentication @@ -153,12 +155,37 @@ class UploadLinks(APIView): error_msg = _('Password is too short') return api_error(status.HTTP_400_BAD_REQUEST, error_msg) - try: - expire_days = int(request.data.get('expire_days', 0)) - except ValueError: - error_msg = 'expire_days invalid.' + 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) + + expire_date = timezone.now() + relativedelta(days=expire_days) + + elif expiration_time: + + try: + expire_date = datetime.fromisoformat(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(pytz.UTC).replace(tzinfo=None) + # resource check repo = seafile_api.get_repo(repo_id) if not repo: @@ -180,11 +207,6 @@ class UploadLinks(APIView): error_msg = 'Permission denied.' return api_error(status.HTTP_403_FORBIDDEN, error_msg) - if expire_days <= 0: - expire_date = None - else: - expire_date = timezone.now() + relativedelta(days=expire_days) - username = request.user.username uls = UploadLinkShare.objects.get_upload_link_by_path(username, repo_id, path) if not uls: diff --git a/seahub/templates/base_for_react.html b/seahub/templates/base_for_react.html index d6f22b674f..6cc5872acf 100644 --- a/seahub/templates/base_for_react.html +++ b/seahub/templates/base_for_react.html @@ -83,9 +83,6 @@ })(), enableRepoSnapshotLabel: {% if enable_repo_snapshot_label %} true {% else %} false {% endif %}, shareLinkPasswordMinLength: {{ share_link_password_min_length }}, - shareLinkExpireDaysDefault: {% if share_link_expire_days_default %} {{ share_link_expire_days_default }} {% else %} 0 {% endif %}, - shareLinkExpireDaysMin: "{{ share_link_expire_days_min }}", - shareLinkExpireDaysMax: "{{ share_link_expire_days_max }}", sideNavFooterCustomHtml: "{{ side_nav_footer_custom_html|safe|escapejs }}", maxFileName: "{{ max_file_name }}", canPublishRepo: {% if user.permissions.can_publish_repo %} true {% else %} false {% endif %}, diff --git a/seahub/templates/markdown_file_view_react.html b/seahub/templates/markdown_file_view_react.html index 59aa99262e..74f8f86e87 100644 --- a/seahub/templates/markdown_file_view_react.html +++ b/seahub/templates/markdown_file_view_react.html @@ -28,6 +28,7 @@ serviceUrl: '{{ serviceUrl }}', isPro: '{{ is_pro }}', isDocs: '{{ is_docs }}', + lang: '{{ LANGUAGE_CODE }}', seafileCollabServer: '{{ seafile_collab_server}}', }, pageOptions: { @@ -44,10 +45,10 @@ hasDraft: '{{ has_draft }}' === 'True', draftFilePath: '{{ draft_file_path }}', draftOriginFilePath: '{{ draft_origin_file_path }}', - shareLinkPasswordMinLength: '{{ share_link_password_min_length }}', - shareLinkExpireDaysDefault: '{{ share_link_expire_days_default }}', - shareLinkExpireDaysMin: '{{ share_link_expire_days_min }}', - shareLinkExpireDaysMax: '{{ share_link_expire_days_max }}', + shareLinkPasswordMinLength: {{ share_link_password_min_length }}, + shareLinkExpireDaysDefault: {{ share_link_expire_days_default }}, + shareLinkExpireDaysMin: {{ share_link_expire_days_min }}, + shareLinkExpireDaysMax: {{ share_link_expire_days_max }}, canGenerateShareLink: {% if user.permissions.can_generate_share_link %} true {% else %} false {% endif %}, canSendShareLinkEmail: {% if user.permissions.can_send_share_link_mail %} true {% else %} false {% endif %}, isLocked: {% if file_locked %}true{% else %}false{% endif %}, diff --git a/seahub/templates/react_app.html b/seahub/templates/react_app.html index ad177f82bd..ef2ee43aa5 100644 --- a/seahub/templates/react_app.html +++ b/seahub/templates/react_app.html @@ -6,5 +6,12 @@ {% endblock %} {% block extra_script %} + {% render_bundle 'app' 'js' %} {% endblock %} diff --git a/seahub/views/__init__.py b/seahub/views/__init__.py index 3fd6dbf2af..279a45a4f4 100644 --- a/seahub/views/__init__.py +++ b/seahub/views/__init__.py @@ -1150,7 +1150,7 @@ def react_fake_view(request, **kwargs): 'enable_repo_snapshot_label': settings.ENABLE_REPO_SNAPSHOT_LABEL, 'resumable_upload_file_block_size': settings.RESUMABLE_UPLOAD_FILE_BLOCK_SIZE, 'max_number_of_files_for_fileupload': settings.MAX_NUMBER_OF_FILES_FOR_FILEUPLOAD, - 'share_link_expire_days_default': settings.SHARE_LINK_EXPIRE_DAYS_DEFAULT, + 'share_link_expire_days_default': SHARE_LINK_EXPIRE_DAYS_DEFAULT, 'share_link_expire_days_min': SHARE_LINK_EXPIRE_DAYS_MIN, 'share_link_expire_days_max': SHARE_LINK_EXPIRE_DAYS_MAX, 'enable_encrypted_library': config.ENABLE_ENCRYPTED_LIBRARY,