diff --git a/frontend/src/pages/data-grid/app-main.js b/frontend/src/pages/data-grid/app-main.js
index 18c02a54e4..aa2aedd31e 100644
--- a/frontend/src/pages/data-grid/app-main.js
+++ b/frontend/src/pages/data-grid/app-main.js
@@ -9,7 +9,7 @@ import GridHeaderContextMenu from './grid-header-contextmenu';
import GridContentContextMenu from './grid-content-contextmenu';
import DTableStore from './store/dtable-store';
-const { repoID, filePath } = window.app.pageOptions;
+const { workspaceID, fileName } = window.app.pageOptions;
const DEFAULT_DATA = {
columns: [
@@ -38,7 +38,7 @@ class AppMain extends React.Component {
componentDidMount() {
- seafileAPI.getFileDownloadLink(repoID, filePath).then(res => {
+ seafileAPI.getTableDownloadLink(workspaceID, fileName).then(res => {
let url = res.data;
seafileAPI.getFileContent(url).then(res => {
let data = res.data ? res.data : JSON.stringify(DEFAULT_DATA);
diff --git a/frontend/src/pages/dtable/dtable.js b/frontend/src/pages/dtable/dtable.js
index 0241fe4969..10335d07af 100644
--- a/frontend/src/pages/dtable/dtable.js
+++ b/frontend/src/pages/dtable/dtable.js
@@ -3,7 +3,6 @@ import PropTypes from 'prop-types';
import { Button, Dropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap';
import moment from 'moment';
import { seafileAPI } from '../../utils/seafile-api';
-import { Utils } from '../../utils/utils';
import { gettext, siteRoot } from '../../utils/constants';
import Loading from '../../components/loading';
import CreateWorkspaceDialog from '../../components/dialog/create-workspace-dialog';
@@ -17,7 +16,7 @@ moment.locale(window.app.config.lang);
const tablePropTypes = {
table: PropTypes.object.isRequired,
- repoID: PropTypes.string.isRequired,
+ workspaceID: PropTypes.string.isRequired,
renameTable: PropTypes.func.isRequired,
deleteTable: PropTypes.func.isRequired,
};
@@ -74,7 +73,7 @@ class Table extends Component {
render() {
let table = this.props.table;
- let tableHref = siteRoot + 'lib/' + this.props.repoID + '/file' + Utils.encodePath(Utils.joinPath('/', table.name));
+ let tableHref = siteRoot + 'workspace/' + this.props.workspaceID + '/dtable/' + table.name + '/';
return (
@@ -269,7 +268,7 @@ class Workspace extends Component {
diff --git a/frontend/src/utils/constants.js b/frontend/src/utils/constants.js
index f47dc922a8..709db1e78c 100644
--- a/frontend/src/utils/constants.js
+++ b/frontend/src/utils/constants.js
@@ -54,6 +54,9 @@ export const canLockUnlockFile = window.app.pageOptions.canLockUnlockFile;
export const curNoteMsg = window.app.pageOptions.curNoteMsg;
export const curNoteID = window.app.pageOptions.curNoteID;
+// ctable
+export const workspaceID = window.app.pageOptions.workspaceID;
+
// wiki
export const slug = window.wiki ? window.wiki.config.slug : '';
export const repoID = window.wiki ? window.wiki.config.repoId : '';
diff --git a/seahub/api2/endpoints/dtable.py b/seahub/api2/endpoints/dtable.py
index 909c0c66b3..5eeb2607f0 100644
--- a/seahub/api2/endpoints/dtable.py
+++ b/seahub/api2/endpoints/dtable.py
@@ -7,17 +7,26 @@ from rest_framework.authentication import SessionAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework import status
from rest_framework.response import Response
+from django.http import HttpResponse, Http404
+from django.shortcuts import render
+from django.utils.translation import ugettext as _
from pysearpc import SearpcError
-from seaserv import seafile_api, edit_repo
+from seaserv import seafile_api, edit_repo, is_repo_owner
+
from seahub.api2.authentication import TokenAuthentication
from seahub.api2.throttling import UserRateThrottle
from seahub.api2.utils import api_error
+from seahub.api2.views import get_repo_file
from seahub.dtable.models import WorkSpaces
from seahub.base.templatetags.seahub_tags import email2nickname
from seahub.utils.timeutils import timestamp_to_isoformat_timestr
-from seahub.utils import is_valid_dirent_name, is_org_context, normalize_file_path, check_filename_with_rename
-from seahub.settings import MAX_UPLOAD_FILE_NAME_LEN
+from seahub.utils import is_valid_dirent_name, is_org_context, normalize_file_path, \
+ check_filename_with_rename, render_error, render_permission_error, CTABLE
+from seahub.views.file import send_file_access_msg
+from seahub.auth.decorators import login_required
+from seahub.settings import MAX_UPLOAD_FILE_NAME_LEN, SHARE_LINK_EXPIRE_DAYS_MIN, \
+ SHARE_LINK_EXPIRE_DAYS_MAX, SHARE_LINK_EXPIRE_DAYS_DEFAULT
logger = logging.getLogger(__name__)
@@ -233,6 +242,52 @@ class DTableView(APIView):
permission_classes = (IsAuthenticated, )
throttle_classes = (UserRateThrottle, )
+ def get(self, request, workspace_id):
+ """view table file, get table download link
+ """
+ # argument check
+ table_name = request.GET.get('name', None)
+ if not table_name:
+ error_msg = 'name invalid.'
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ reuse = request.GET.get('reuse', '0')
+ if reuse not in ('1', '0'):
+ error_msg = 'reuse 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 %s not found.' % workspace_id
+ return api_error(status.HTTP_404_NOT_FOUND, error_msg)
+
+ repo_id = workspace.repo_id
+ repo = seafile_api.get_repo(repo_id)
+ if not repo:
+ error_msg = 'Library %s not found.' % repo_id
+ return api_error(status.HTTP_404_NOT_FOUND, error_msg)
+
+ table_path = normalize_file_path(table_name)
+ table_file_id = seafile_api.get_file_id_by_path(repo_id, table_path)
+ if not table_file_id:
+ error_msg = 'Library %s not found.' % repo_id
+ return api_error(status.HTTP_404_NOT_FOUND, error_msg)
+
+ # permission check
+ username = request.user.username
+ owner = workspace.owner
+ if username != owner:
+ error_msg = 'Permission denied.'
+ return api_error(status.HTTP_403_FORBIDDEN, error_msg)
+
+ # send stats message
+ send_file_access_msg(request, repo, table_path, 'api')
+
+ op = request.GET.get('op', 'download')
+ use_onetime = False if reuse == '1' else True
+ return get_repo_file(request, repo_id, table_file_id, table_name, op, use_onetime)
+
def post(self, request, workspace_id):
"""create a table file
"""
@@ -409,3 +464,41 @@ class DTableView(APIView):
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
return Response({'success': True}, status=status.HTTP_200_OK)
+
+
+@login_required
+def dtable_file_view(request, workspace_id, name):
+
+ # resource check
+ 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
+
+ table_path = normalize_file_path(name)
+ table_file_id = seafile_api.get_file_id_by_path(repo_id, table_path)
+ if not table_file_id:
+ return render_error(request, _(u'Table does not exist'))
+
+ # permission check
+ username = request.user.username
+ owner = workspace.owner
+ if username != owner:
+ return render_permission_error(request, _(u'Unable to view file'))
+
+ return_dict = {
+ 'repo': repo,
+ 'workspace_id': workspace_id,
+ 'path': table_path,
+ 'filename': name,
+ 'filetype': CTABLE,
+ '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,
+ }
+
+ return render(request, 'ctable_file_view_react.html', return_dict)
diff --git a/seahub/templates/file_view_react.html b/seahub/templates/file_view_react.html
index 2a44ec8281..219129a200 100644
--- a/seahub/templates/file_view_react.html
+++ b/seahub/templates/file_view_react.html
@@ -41,6 +41,10 @@ window.app.pageOptions = {
enableWatermark: {% if enable_watermark %}true{% else %}false{% endif %},
// for {{filetype}} file
+ {% if filetype == 'ctable' %}
+ workspaceID: '{{ workspace_id }}',
+ {% endif %}
+
{% block extra_data %}
{% endblock %}
};
diff --git a/seahub/urls.py b/seahub/urls.py
index 018cec930a..62a44fa6d4 100644
--- a/seahub/urls.py
+++ b/seahub/urls.py
@@ -84,7 +84,7 @@ from seahub.api2.endpoints.related_files import RelatedFilesView, RelatedFileVie
from seahub.api2.endpoints.webdav_secret import WebdavSecretView
from seahub.api2.endpoints.starred_items import StarredItems
from seahub.api2.endpoints.markdown_lint import MarkdownLintView
-from seahub.api2.endpoints.dtable import WorkSpacesView, WorkSpaceView, DTableView
+from seahub.api2.endpoints.dtable import WorkSpacesView, WorkSpaceView, DTableView, dtable_file_view
# Admin
from seahub.api2.endpoints.admin.revision_tag import AdminTaggedItemsView
@@ -348,6 +348,7 @@ urlpatterns = [
url(r'^api/v2.1/workspaces/$', WorkSpacesView.as_view(), name='api-v2.1-workspaces'),
url(r'^api/v2.1/workspace/(?P\d+)/$', WorkSpaceView.as_view(), name='api-v2.1-workspace'),
url(r'^api/v2.1/workspace/(?P\d+)/dtable/$', DTableView.as_view(), name='api-v2.1-workspace-table'),
+ url(r'^workspace/(?P\d+)/dtable/(?P.*)/$', dtable_file_view, name='dtable-file-view'),
# Deprecated
url(r'^api/v2.1/repos/(?P[-0-9a-f]{36})/tags/$', FileTagsView.as_view(), name="api-v2.1-filetags-view"),
diff --git a/seahub/views/file.py b/seahub/views/file.py
index 2bbfb4ffac..3328da3590 100644
--- a/seahub/views/file.py
+++ b/seahub/views/file.py
@@ -734,8 +734,6 @@ def view_lib_file(request, repo_id, path):
return render(request, template, return_dict)
- elif filetype == CTABLE:
- return render(request, template, return_dict)
elif filetype == CDOC:
return render(request, template, return_dict)
elif filetype == IMAGE: