diff --git a/frontend/src/components/dialog/generate-dtable-share-link.js b/frontend/src/components/dialog/generate-dtable-share-link.js
new file mode 100644
index 0000000000..d5b27f5191
--- /dev/null
+++ b/frontend/src/components/dialog/generate-dtable-share-link.js
@@ -0,0 +1,378 @@
+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 { gettext, shareLinkExpireDaysMin, shareLinkExpireDaysMax, shareLinkExpireDaysDefault, shareLinkPasswordMinLength, canSendShareLinkEmail } from '../../utils/constants';
+import { seafileAPI } from '../../utils/seafile-api';
+import { Utils } from '../../utils/utils';
+import ShareLink from '../../models/share-link';
+import toaster from '../toast';
+import Loading from '../loading';
+import DTableShareLink from '../../models/dtable-share-link';
+
+const propTypes = {
+ workspaceID: PropTypes.number.isRequired,
+ name: PropTypes.string.isRequired,
+ closeShareDialog: PropTypes.func.isRequired,
+};
+
+class GenerateDTableShareLink extends React.Component {
+
+ constructor(props) {
+ super(props);
+
+ this.isExpireDaysNoLimit = (parseInt(shareLinkExpireDaysMin) === 0 && parseInt(shareLinkExpireDaysMax) === 0 && shareLinkExpireDaysDefault == 0);
+ this.defaultExpireDays = this.isExpireDaysNoLimit ? '' : shareLinkExpireDaysDefault;
+
+ this.isExpireDaysNoLimit = true;
+ this.defaultExpireDays = '';
+
+ this.permissionOptions = ['read-only', 'read-write'];
+
+ this.state = {
+ isValidate: false,
+ isShowPasswordInput: false,
+ isPasswordVisible: false,
+ isExpireChecked: !this.isExpireDaysNoLimit,
+ password: '',
+ passwdnew: '',
+ expireDays: this.defaultExpireDays,
+ errorInfo: '',
+ sharedLinkInfo: null,
+ isNoticeMessageShow: false,
+ isLoading: true,
+ currentPermission: this.permissionOptions[0],
+ isSendLinkShown: false,
+ };
+ }
+
+ componentDidMount() {
+ let workspaceID = this.props.workspaceID;
+ let name = this.props.name;
+ seafileAPI.getDTableShareLink(workspaceID, name).then((res) => {
+ window.res = res;
+ if (res.data.dtable_share_links.length !== 0) {
+ let sharedLinkInfo = new ShareLink(res.data.dtable_share_links[0]);
+ this.setState({
+ isLoading: false,
+ sharedLinkInfo: sharedLinkInfo
+ });
+ } else {
+ this.setState({isLoading: false});
+ }
+ }).catch(error => {
+ let errMessage = Utils.getErrorMsg(error);
+ toaster.danger(errMessage);
+ });
+ }
+
+ onPasswordInputChecked = () => {
+ this.setState({
+ isShowPasswordInput: !this.state.isShowPasswordInput,
+ password: '',
+ passwdnew: '',
+ errorInfo: ''
+ });
+ }
+
+ togglePasswordVisible = () => {
+ this.setState({
+ isPasswordVisible: !this.state.isPasswordVisible
+ });
+ }
+
+ generatePassword = () => {
+ let val = Utils.generatePassword(shareLinkPasswordMinLength);
+ this.setState({
+ password: val,
+ passwdnew: val
+ });
+ }
+
+ inputPassword = (e) => {
+ let passwd = e.target.value.trim();
+ this.setState({password: passwd});
+ }
+
+ inputPasswordNew = (e) => {
+ let passwd = e.target.value.trim();
+ this.setState({passwdnew: passwd});
+ }
+
+ generateDTableShareLink = () => {
+ let isValid = this.validateParamsInput();
+ if (isValid) {
+ this.setState({errorInfo: ''});
+ let { workspaceID, name } = this.props;
+ let { password, isExpireChecked, expireDays } = this.state;
+ let permission = Utils.getDTableShareLinkPermissionObject(this.state.currentPermission).permission;
+ const expireDaysSent = isExpireChecked ? expireDays : '';
+ seafileAPI.createDTableShareLink(workspaceID, name, password, expireDaysSent, permission).then((res) => {
+ let sharedLinkInfo = new DTableShareLink(res.data);
+ this.setState({sharedLinkInfo: sharedLinkInfo});
+ }).catch((error) => {
+ let errMessage = Utils.getErrorMsg(error);
+ toaster.danger(errMessage);
+ });
+ }
+ }
+
+ onCopySharedLink = () => {
+ let sharedLink = this.state.sharedLinkInfo.link;
+ copy(sharedLink);
+ toaster.success(gettext('Share link is copied to the clipboard.'));
+ this.props.closeShareDialog();
+ }
+
+ deleteShareLink = () => {
+ let sharedLinkInfo = this.state.sharedLinkInfo;
+ seafileAPI.deleteDTableShareLink(sharedLinkInfo.token).then(() => {
+ this.setState({
+ password: '',
+ passwordnew: '',
+ isShowPasswordInput: false,
+ expireDays: this.defaultExpireDays,
+ isExpireChecked: !this.isExpireDaysNoLimit,
+ errorInfo: '',
+ sharedLinkInfo: null,
+ isNoticeMessageShow: false,
+ });
+ }).catch((error) => {
+ let errMessage = Utils.getErrorMsg(error);
+ toaster.danger(errMessage);
+ });
+ }
+
+ onExpireChecked = (e) => {
+ this.setState({isExpireChecked: e.target.checked});
+ }
+
+ onExpireDaysChanged = (e) => {
+ let day = e.target.value.trim();
+ this.setState({expireDays: day});
+ }
+
+ setPermission = (e) => {
+ this.setState({currentPermission: e.target.value});
+ }
+
+ validateParamsInput = () => {
+ let { isShowPasswordInput , password, passwdnew, isExpireChecked, expireDays } = this.state;
+ // validate password
+ if (isShowPasswordInput) {
+ if (password.length === 0) {
+ this.setState({errorInfo: 'Please enter password'});
+ return false;
+ }
+ if (password.length < shareLinkPasswordMinLength) {
+ this.setState({errorInfo: 'Password is too short'});
+ return false;
+ }
+ if (password !== passwdnew) {
+ this.setState({errorInfo: 'Passwords don\'t match'});
+ return false;
+ }
+ }
+
+ // validate days
+ // no limit
+ let reg = /^\d+$/;
+ if (this.isExpireDaysNoLimit) {
+ if (isExpireChecked) {
+ if (!expireDays) {
+ this.setState({errorInfo: 'Please enter days'});
+ return false;
+ }
+ if (!reg.test(expireDays)) {
+ this.setState({errorInfo: 'Please enter a non-negative integer'});
+ return false;
+ }
+ this.setState({expireDays: parseInt(expireDays)});
+ }
+ } else {
+ if (!expireDays) {
+ this.setState({errorInfo: 'Please enter days'});
+ return false;
+ }
+ if (!reg.test(expireDays)) {
+ this.setState({errorInfo: 'Please enter a non-negative integer'});
+ return false;
+ }
+
+ expireDays = parseInt(expireDays);
+ let minDays = parseInt(shareLinkExpireDaysMin);
+ let maxDays = parseInt(shareLinkExpireDaysMax);
+
+ if (minDays !== 0 && maxDays !== maxDays) {
+ if (expireDays < minDays) {
+ this.setState({errorInfo: 'Please enter valid days'});
+ return false;
+ }
+ }
+
+ if (minDays === 0 && maxDays !== 0 ) {
+ if (expireDays > maxDays) {
+ this.setState({errorInfo: 'Please enter valid days'});
+ return false;
+ }
+ }
+
+ if (minDays !== 0 && maxDays !== 0) {
+ if (expireDays < minDays || expireDays > maxDays) {
+ this.setState({errorInfo: 'Please enter valid days'});
+ return false;
+ }
+ }
+ this.setState({expireDays: expireDays});
+ }
+
+ return true;
+ }
+
+ onNoticeMessageToggle = () => {
+ this.setState({isNoticeMessageShow: !this.state.isNoticeMessageShow});
+ }
+
+ toggleSendLink = () => {
+ this.setState({ isSendLinkShown: !this.state.isSendLinkShown });
+ }
+
+ render() {
+
+ if (this.state.isLoading) {
+ return ;
+ }
+
+ let passwordLengthTip = gettext('(at least {passwordLength} characters)');
+ passwordLengthTip = passwordLengthTip.replace('{passwordLength}', shareLinkPasswordMinLength);
+
+ if (this.state.sharedLinkInfo) {
+ let sharedLinkInfo = this.state.sharedLinkInfo;
+ return (
+
+
+ {(canSendShareLinkEmail && !this.state.isSendLinkShown && !this.state.isNoticeMessageShow) &&
+
+ }
+ {(!this.state.isSendLinkShown && !this.state.isNoticeMessageShow) &&
+
+ }
+ {this.state.isNoticeMessageShow &&
+
+
{gettext('Are you sure you want to delete the share link?')}
+
{gettext('If the share link is deleted, no one will be able to access it any more.')}
+
{' '}
+
+
+ }
+
+ );
+ } else {
+ return (
+
+ );
+ }
+ }
+}
+
+GenerateDTableShareLink.propTypes = propTypes;
+
+export default GenerateDTableShareLink;
diff --git a/frontend/src/components/dialog/share-table-dialog.js b/frontend/src/components/dialog/share-table-dialog.js
index aec8496c80..6b7d9e446a 100644
--- a/frontend/src/components/dialog/share-table-dialog.js
+++ b/frontend/src/components/dialog/share-table-dialog.js
@@ -5,6 +5,7 @@ import {Modal, ModalHeader, ModalBody, Nav, NavItem, NavLink, TabContent, TabPan
import ShareTableToUser from './share-table-to-user';
import '../../css/share-link-dialog.css';
+import GenerateDTableShareLink from './generate-dtable-share-link';
const propTypes = {
currentTable: PropTypes.object.isRequired,
@@ -40,6 +41,13 @@ class ShareTableDialog extends React.Component {
>{gettext('Share to user')}
+
+ {gettext('Share link')}
+
+
@@ -51,6 +59,13 @@ class ShareTableDialog extends React.Component {
currentTable={this.props.currentTable}
/>
+ {activeTab === 'shareLink' &&
+
+ }
diff --git a/frontend/src/models/dtable-share-link.js b/frontend/src/models/dtable-share-link.js
new file mode 100644
index 0000000000..a0367549ce
--- /dev/null
+++ b/frontend/src/models/dtable-share-link.js
@@ -0,0 +1,16 @@
+class DTableShareLink {
+
+ constructor(object) {
+ this.workspaceID = object.workspace_id;
+ this.permissions = object.permission;
+ this.username = object.username;
+ this.is_expired = object.is_expired;
+ this.expire_date = object.expire_date;
+ this.token = object.token;
+ this.link = object.link;
+ this.ctime = object.ctime;
+ }
+
+}
+
+export default DTableShareLink;
diff --git a/frontend/src/utils/utils.js b/frontend/src/utils/utils.js
index 72098f0e9b..5c6553f56e 100644
--- a/frontend/src/utils/utils.js
+++ b/frontend/src/utils/utils.js
@@ -575,6 +575,23 @@ export const Utils = {
}
},
+ getDTableShareLinkPermissionObject: function(permission) {
+ switch (permission) {
+ case 'read-only':
+ return {
+ value: permission,
+ text: 'read-only',
+ permission: 'r'
+ };
+ case 'read-write':
+ return {
+ value: permission,
+ text: 'read-write',
+ permission: 'rw'
+ };
+ }
+ },
+
formatSize: function(options) {
/*
* param: {bytes, precision}
diff --git a/seahub/api2/endpoints/dtable_share_links.py b/seahub/api2/endpoints/dtable_share_links.py
index c518926412..961034bfd4 100644
--- a/seahub/api2/endpoints/dtable_share_links.py
+++ b/seahub/api2/endpoints/dtable_share_links.py
@@ -14,6 +14,7 @@ from seahub.api2.endpoints.dtable import WRITE_PERMISSION_TUPLE
from seahub.api2.permissions import CanGenerateShareLink
from seahub.api2.throttling import UserRateThrottle
from seahub.api2.utils import api_error
+from seahub.constants import PERMISSION_READ
from seahub.dtable.models import DTables, DTableShareLinks, Workspaces
from seahub.settings import SHARE_LINK_EXPIRE_DAYS_MAX, \
SHARE_LINK_EXPIRE_DAYS_MIN, SHARE_LINK_EXPIRE_DAYS_DEFAULT, SHARE_LINK_PASSWORD_MIN_LENGTH
@@ -33,6 +34,8 @@ def get_share_dtable_link_info(sdl, dtable):
'dtable': dtable.name,
'dtable_id': dtable.id,
'workspace_id': dtable.workspace_id,
+ 'expire_date': sdl.expire_date,
+ 'ctime': sdl.ctime,
}
return data
@@ -44,6 +47,43 @@ class DTableShareLinksView(APIView):
permission_classes = (IsAuthenticated, CanGenerateShareLink)
throttle_classes = (UserRateThrottle,)
+ def get(self, request):
+ """
+ get dtable all share links of such user
+ :param request:
+ :return:
+ """
+ username = request.user.username
+ workspace_id = request.GET.get('workspace_id')
+ if not workspace_id:
+ error_msg = _('workspace_id invalid.')
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+ table_name = request.GET.get('table_name')
+ if not table_name:
+ error_msg = _('table_name invalid.')
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ # resource check
+ workspace = Workspaces.objects.get_workspace_by_id(workspace_id)
+ if not workspace:
+ error_msg = _('Workspace %(workspace)s not found' % {'workspace': workspace_id})
+ return api_error(status.HTTP_404_NOT_FOUND, error_msg)
+ repo = seafile_api.get_repo(workspace.repo_id)
+ if not repo:
+ error_msg = _('Library %(workspace)s not found' % {'workspace': workspace_id})
+ return api_error(status.HTTP_404_NOT_FOUND, error_msg)
+ dtable = DTables.objects.get_dtable(workspace_id, table_name)
+ if not dtable:
+ error_msg = _('DTable %(table)s not found' % {'table': table_name})
+ return api_error(status.HTTP_404_NOT_FOUND, error_msg)
+
+ # get table's all links of user
+ dsls = DTableShareLinks.objects.filter(dtable=dtable, username=username)
+ results = [get_share_dtable_link_info(item, dtable) for item in dsls]
+ return Response({
+ 'dtable_share_links': results
+ })
+
def post(self, request):
# argument check
workspace_id = request.data.get('workspace_id')
@@ -62,9 +102,11 @@ class DTableShareLinksView(APIView):
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
try:
- expire_days = int(request.data.get('expire_days'))
+ expire_days = int(request.data.get('expire_days', 0))
except ValueError:
- return api_error(status.HTTP_400_BAD_REQUEST, 'expire_days invalid')
+ return api_error(status.HTTP_400_BAD_REQUEST, _('expire_days invalid'))
+ except TypeError:
+ return api_error(status.HTTP_400_BAD_REQUEST, _('expire_days invalid'))
if expire_days <= 0:
if SHARE_LINK_EXPIRE_DAYS_DEFAULT > 0:
@@ -91,6 +133,7 @@ class DTableShareLinksView(APIView):
if link_permission and link_permission not in [perm[0] for perm in DTableShareLinks.PERMISSION_CHOICES]:
error_msg = _('Permission invalid')
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+ link_permission = link_permission if link_permission else PERMISSION_READ
# resource check
workspace = Workspaces.objects.get_workspace_by_id(workspace_id)
@@ -127,3 +170,28 @@ class DTableShareLinksView(APIView):
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
data = get_share_dtable_link_info(sdl, dtable)
return Response(data)
+
+
+class DTableSharedLinkView(APIView):
+
+ authentication_classes = (TokenAuthentication, SessionAuthentication)
+ permission_classes = (IsAuthenticated, CanGenerateShareLink)
+ throttle_classes = (UserRateThrottle,)
+
+ def delete(self, request, token):
+ dsl = DTableShareLinks.objects.filter(token=token).first()
+ if not dsl:
+ return Response({'success': True})
+
+ username = request.user.username
+ if not dsl.is_owner(username):
+ error_msg = _('Permission denied.')
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ try:
+ dsl.delete()
+ except Exception as e:
+ logger.error(e)
+ error_msg = _('Internal server error')
+ return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
+ return Response({'success': True})
diff --git a/seahub/dtable/models.py b/seahub/dtable/models.py
index f7cddd3f30..16a03635b8 100644
--- a/seahub/dtable/models.py
+++ b/seahub/dtable/models.py
@@ -3,6 +3,7 @@
import uuid
import hmac
from django.contrib.auth.hashers import make_password
+from django.utils import timezone
from hashlib import sha1
import datetime
@@ -254,7 +255,7 @@ class DTableAPIToken(models.Model):
class DTableShareLinksManager(models.Manager):
def create_link(self, dtable_id, username,
- password=None, expire_date=None, permission=None):
+ password=None, expire_date=None, permission='r'):
if password:
password = make_password(password)
token = gen_token(max_length=config.SHARE_LINK_TOKEN_LENGTH)
@@ -287,6 +288,15 @@ class DTableShareLinks(models.Model):
class Meta:
db_table = 'dtable_share_links'
+ def is_owner(self, username):
+ return self.username == username
+
+ def is_expired(self):
+ if not self.expire_date:
+ return False
+ else:
+ return self.expire_date < timezone.now()
+
class DTableFormLinksManager(models.Manager):
diff --git a/seahub/dtable/urls.py b/seahub/dtable/urls.py
index 3a9aea0002..24e508a7f3 100644
--- a/seahub/dtable/urls.py
+++ b/seahub/dtable/urls.py
@@ -1,12 +1,13 @@
# -*- coding: utf-8 -*-
from django.conf.urls import url
-from .views import dtable_file_view, dtable_asset_access, dtable_asset_file_view, dtable_form_view
-
+from .views import dtable_file_view, dtable_asset_access, dtable_asset_file_view, dtable_form_view, \
+ dtable_share_link_view
urlpatterns = [
url(r'^workspace/(?P\d+)/dtable/(?P.*)/$', dtable_file_view, name='dtable_file_view'),
url(r'^workspace/(?P\d+)/asset/(?P[-0-9a-f]{36})/(?P.*)$', dtable_asset_access, name='dtable_asset_access'),
url(r'^workspace/(?P\d+)/asset-file/(?P[-0-9a-f]{36})/(?P.*)$', dtable_asset_file_view, name='dtable_asset_file_view'),
- url(r'^dtable/forms/(?P[-0-9a-f]{36})$', dtable_form_view, name='dtable_form_view')
+ url(r'^dtable/forms/(?P[-0-9a-f]{36})$', dtable_form_view, name='dtable_form_view'),
+ url(r'^dtable/links/(?P[-0-9a-f]+)/$', dtable_share_link_view, name='dtable_share_link_view'),
]
diff --git a/seahub/dtable/utils.py b/seahub/dtable/utils.py
index f3bba999ca..f970c001e6 100644
--- a/seahub/dtable/utils.py
+++ b/seahub/dtable/utils.py
@@ -81,4 +81,4 @@ def gen_share_dtable_link(token):
service_url = get_service_url()
assert service_url is not None
service_url = service_url.rstrip('/')
- return '%s/dtable/link/%s' % (service_url, token)
+ return '%s/dtable/links/%s' % (service_url, token)
diff --git a/seahub/dtable/views.py b/seahub/dtable/views.py
index 42b2d78d2b..4df8f9dbfa 100644
--- a/seahub/dtable/views.py
+++ b/seahub/dtable/views.py
@@ -10,7 +10,7 @@ from django.shortcuts import render
from django.utils.translation import ugettext as _
from seaserv import seafile_api
-from seahub.dtable.models import Workspaces, DTables, DTableFormLinks
+from seahub.dtable.models import Workspaces, DTables, DTableFormLinks, DTableShareLinks
from seahub.utils import normalize_file_path, render_error, render_permission_error, \
gen_file_get_url, get_file_type_and_ext, gen_inner_file_get_url
from seahub.auth.decorators import login_required
@@ -236,3 +236,45 @@ def dtable_form_view(request, token):
}
return render(request, 'dtable_form_view_react.html', return_dict)
+
+
+def dtable_share_link_view(request, token):
+ dsl = DTableShareLinks.objects.filter(token=token).first()
+ if not dsl:
+ return render_error(request, _('Share link does not exist'))
+ if dsl.is_expired():
+ return render_error(request, _('Share link has expired'))
+
+ # resource check
+ workspace_id = dsl.dtable.workspace.id
+ workspace = Workspaces.objects.get_workspace_by_id(workspace_id)
+ if not workspace:
+ raise Http404
+
+ repo_id = workspace.repo_id
+ repo = seafile_api.get_repo(repo_id)
+ if not repo:
+ raise Http404
+
+ name = dsl.dtable.name
+ dtable = DTables.objects.get_dtable(workspace, name)
+ if not dtable:
+ return render_error(request, _('DTable does not exist'))
+
+ table_file_name = name + FILE_TYPE
+ table_path = normalize_file_path(table_file_name)
+
+ return_dict = {
+ 'repo': repo,
+ 'filename': name,
+ 'path': table_path,
+ 'filetype': 'dtable',
+ 'workspace_id': workspace_id,
+ 'dtable_uuid': dtable.uuid.hex,
+ 'media_url': MEDIA_URL,
+ 'dtable_server': DTABLE_SERVER_URL,
+ 'dtable_socket': SEAFILE_COLLAB_SERVER,
+ 'permission': dsl.permission
+ }
+
+ return render(request, 'dtable_share_link_view_react.html', return_dict)
diff --git a/seahub/templates/dtable_share_link_view_react.html b/seahub/templates/dtable_share_link_view_react.html
new file mode 100644
index 0000000000..a440d2013f
--- /dev/null
+++ b/seahub/templates/dtable_share_link_view_react.html
@@ -0,0 +1,54 @@
+{% load seahub_tags i18n staticfiles %}
+{% load render_bundle from webpack_loader %}
+
+
+
+
+{{ filename }}
+
+
+
+
+
+
+
+
+
+{% render_bundle 'viewDataGrid' 'css' %}
+{% if branding_css != '' %}{% endif %}
+{% if enable_branding_css %}{% endif %}
+
+
+
+
+
+
+
+
+{% render_bundle 'commons' %}
+{% render_bundle 'viewDataGrid' 'js' %}
+
+
+
diff --git a/seahub/templates/react_dtable.html b/seahub/templates/react_dtable.html
index 677c146db2..7ae4cce4cf 100644
--- a/seahub/templates/react_dtable.html
+++ b/seahub/templates/react_dtable.html
@@ -43,7 +43,10 @@
mediaUrl: '{{ media_url }}',
dtableServer: '{{ dtable_server }}',
dtableSocket: '{{ dtable_socket }}',
- lang: '{{ LANGUAGE_CODE }}'
+ lang: '{{ LANGUAGE_CODE }}',
+ shareLinkExpireDaysMin: '{{ share_link_expire_days_min }}',
+ shareLinkExpireDaysMax: '{{ share_link_expire_days_max }}',
+ shareLinkExpireDaysDefault: '{{ share_link_expire_days_default }}',
}
};
diff --git a/seahub/urls.py b/seahub/urls.py
index 237a90eb81..d0067a7b2c 100644
--- a/seahub/urls.py
+++ b/seahub/urls.py
@@ -4,7 +4,6 @@ from django.conf.urls import url, include
# from django.views.generic.simple import direct_to_template
from django.views.generic import TemplateView
-from seahub.api2.endpoints.dtable_share_links import DTableShareLinksView
from seahub.views import *
from seahub.views.sysadmin import *
from seahub.views.ajax import *
@@ -98,6 +97,7 @@ from seahub.api2.endpoints.dtable_forms import DTableFormLinksView, DTableFormLi
from seahub.api2.endpoints.dtable_share import SharedDTablesView, DTableShareView
from seahub.api2.endpoints.dtable_related_users import DTableRelatedUsersView
from seahub.api2.endpoints.recent_added_files import RecentAddedFilesView
+from seahub.api2.endpoints.dtable_share_links import DTableShareLinksView, DTableSharedLinkView
# Admin
@@ -387,6 +387,7 @@ urlpatterns = [
url(r'^api/v2.1/workspace/(?P\d+)/dtable-asset-upload-link/$', DTableAssetUploadLinkView.as_view(), name='api-v2.1-workspace-dtable-asset-upload-link'),
url(r'^api/v2.1/dtables/shared/$', SharedDTablesView.as_view(), name='api-v2.1-dtables-share'),
url(r'^api/v2.1/dtables/share-links/$', DTableShareLinksView.as_view(), name='api-v2.1-dtables-share-links'),
+ url(r'^api/v2.1/dtables/share-links/(?P[0-9a-f]+)/$', DTableSharedLinkView.as_view(), name='api-v2.1-dtables-share-link'),
url(r'^api/v2.1/workspace/(?P\d+)/dtable/(?P.*)/share/$', DTableShareView.as_view(), name='api-v2.1-dtable-share'),
url(r'^api/v2.1/workspace/(?P\d+)/dtable/(?P.*)/related-users/$', DTableRelatedUsersView.as_view(), name='api-v2.1-dtable-related-users'),
url(r'^api/v2.1/workspace/(?P\d+)/dtable/(?P.*)/access-token/$', DTableAccessTokenView.as_view(), name='api-v2.1-dtable-access-token'),
diff --git a/seahub/views/__init__.py b/seahub/views/__init__.py
index 25b347fc48..598265dce0 100644
--- a/seahub/views/__init__.py
+++ b/seahub/views/__init__.py
@@ -1272,4 +1272,8 @@ def react_fake_view(request, **kwargs):
@login_required
def dtable_fake_view(request, **kwargs):
- return render(request, 'react_dtable.html')
+ return render(request, 'react_dtable.html', {
+ 'share_link_expire_days_default': settings.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,
+ })