diff --git a/frontend/src/css/sdoc-file-history.css b/frontend/src/css/sdoc-file-history.css index c7c4d75093..d9838bb682 100644 --- a/frontend/src/css/sdoc-file-history.css +++ b/frontend/src/css/sdoc-file-history.css @@ -85,6 +85,7 @@ width: 100%; padding-top: 0; padding-bottom: 0; + margin-left: 0 !important; } .sdoc-file-history .sdoc-file-history-content .article { diff --git a/frontend/src/view-file-sdoc.js b/frontend/src/view-file-sdoc.js index 36b6af2836..2394e00b6f 100644 --- a/frontend/src/view-file-sdoc.js +++ b/frontend/src/view-file-sdoc.js @@ -11,7 +11,8 @@ const { username, name } = window.app.userInfo; const { repoID, repoName, parentDir, filePerm, docPath, docName, docUuid, seadocAccessToken, seadocServerUrl, assetsUrl, - isSdocRevision, isPublished, originFilename, revisionCreatedAt, + isSdocRevision, isPublished, originFilename, revisionCreatedAt, originFileVersion, + originFilePath, originDocUuid, } = window.app.pageOptions; window.seafile = { @@ -37,6 +38,9 @@ window.seafile = { isPublished, revisionURL: Utils.generateRevisionURL(siteRoot, repoID, docPath), originFilename, + originFileVersion, + originFilePath, + originDocUuid, revisionCreatedAt, lang, }; diff --git a/seahub/seadoc/apis.py b/seahub/seadoc/apis.py index 2149cb57e1..78d8bd8e35 100644 --- a/seahub/seadoc/apis.py +++ b/seahub/seadoc/apis.py @@ -223,8 +223,7 @@ class SeadocDownloadLink(APIView): return Response({'download_link': download_link}) -class SeadocRevisionDownloadLinks(APIView): - +class SeadocOriginFileContent(APIView): authentication_classes = () throttle_classes = (UserRateThrottle,) @@ -244,12 +243,6 @@ class SeadocRevisionDownloadLinks(APIView): if filetype != SEADOC: error_msg = 'seadoc file type %s invalid.' % filetype return api_error(status.HTTP_400_BAD_REQUEST, error_msg) - - - file_download_link = get_seadoc_download_link(uuid_map) - if not file_download_link: - error_msg = 'seadoc file %s not found.' % uuid_map.filename - return api_error(status.HTTP_404_NOT_FOUND, error_msg) revision_info = is_seadoc_revision(file_uuid) origin_doc_uuid = revision_info.get('origin_doc_uuid', '') @@ -264,14 +257,14 @@ class SeadocRevisionDownloadLinks(APIView): error_msg = 'seadoc origin uuid %s not found.' % origin_doc_uuid return api_error(status.HTTP_404_NOT_FOUND, error_msg) - origin_file_download_link = get_seadoc_download_link(origin_uuid_map) + origin_file_download_link = get_seadoc_download_link(origin_uuid_map, True) if not origin_file_download_link: error_msg = 'seadoc origin file %s not found.' % origin_uuid_map.filename return api_error(status.HTTP_404_NOT_FOUND, error_msg) + resp = requests.get(origin_file_download_link) return Response({ - 'file_download_link': file_download_link, - 'origin_file_download_link': origin_file_download_link + 'content': resp.content }) @@ -1291,6 +1284,139 @@ class SeadocRevisions(APIView): }) +class SeadocRevisionView(APIView): + # sdoc editor use jwt token + authentication_classes = (SdocJWTTokenAuthentication, TokenAuthentication, SessionAuthentication) + permission_classes = (IsAuthenticated,) + throttle_classes = (UserRateThrottle, ) + + def delete(self, request, file_uuid): + if not file_uuid: + error_msg = 'file_uuid %s not found.' % file_uuid + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + uuid_map = FileUUIDMap.objects.get_fileuuidmap_by_uuid(file_uuid) + + if not uuid_map: + error_msg = 'file %s uuid_map not found.' % file_uuid + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + revision = SeadocRevision.objects.get_by_doc_uuid(file_uuid) + if not revision: + error_msg = 'Revision %s not found.' % file_uuid + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + if revision.is_published: + error_msg = 'Revision %s is already published.' % file_uuid + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + repo_id = revision.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) + + # permission check + permission = check_folder_permission(request, repo_id, '/') + if not permission: + error_msg = 'Permission denied.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + if permission != PERMISSION_READ_WRITE: + error_msg = 'Permission denied.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + # get revision file info + revision_file_uuid = uuid_map + revision_parent_path = revision_file_uuid.parent_path + revision_filename = revision_file_uuid.filename + username = request.user.username + + revision_image_parent_path = '/images/sdoc/' + str(revision_file_uuid.uuid) + '/' + dir_id = seafile_api.get_dir_id_by_path(repo_id, revision_image_parent_path) + if dir_id: + seafile_api.del_file( + repo_id, '/images/sdoc/', json.dumps([str(revision_file_uuid.uuid)]), username) + + seafile_api.del_file( + repo_id, revision_parent_path, revision_filename, username) + + SeadocRevision.objects.delete_by_doc_uuid(file_uuid) + + sdoc_server_api = SdocServerAPI(file_uuid, revision_filename, username) + sdoc_server_api.remove_doc() + + return Response({ + 'success': True + }) + + # modify by rebase op + def put(self, request, file_uuid): + + if not file_uuid: + error_msg = 'file_uuid %s not found.' % file_uuid + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + uuid_map = FileUUIDMap.objects.get_fileuuidmap_by_uuid(file_uuid) + + if not uuid_map: + error_msg = 'file %s uuid_map not found.' % file_uuid + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + revision = SeadocRevision.objects.get_by_doc_uuid(file_uuid) + if not revision: + error_msg = 'Revision %s not found.' % file_uuid + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + if revision.is_published: + error_msg = 'Revision %s is already published.' % file_uuid + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + origin_doc_uuid = revision.origin_doc_uuid + origin_uuid_map = FileUUIDMap.objects.get_fileuuidmap_by_uuid(origin_doc_uuid) + if not origin_uuid_map: + error_msg = 'origin file %s uuid_map not found.' % file_uuid + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + file_path = posixpath.join(uuid_map.parent_path, uuid_map.filename) + file_id = seafile_api.get_file_id_by_path(uuid_map.repo_id, file_path) + if not file_id: # save file anyway + seafile_api.post_empty_file( + uuid_map.repo_id, uuid_map.parent_path, uuid_map.filename, '') + + username = request.user.username + upload_link = get_seadoc_upload_link(uuid_map, username) + if not upload_link: + error_msg = 'seadoc file %s not found.' % uuid_map.filename + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + try: + + # rewrite file + file = request.FILES.get('file', None) + files = { + 'file': file, + 'file_name': uuid_map.filename, + 'target_file': file_path, + } + requests.post(upload_link, files=files) + + # update origin file version + origin_doc_path = revision.origin_doc_path + newest_file_version = seafile_api.get_file_id_by_path(origin_uuid_map.repo_id, origin_doc_path) + SeadocRevision.objects.update_origin_file_version(file_uuid, newest_file_version) + + # server content update + sdoc_server_api = SdocServerAPI(file_uuid, str(uuid_map.filename), username) + sdoc_server_api.replace_doc() + + return Response({ + 'origin_file_version': newest_file_version, + }) + except Exception as e: + logger.error(e) + error_msg = 'Internal Server Error' + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + + class SeadocPublishRevision(APIView): authentication_classes = (SdocJWTTokenAuthentication, TokenAuthentication, SessionAuthentication) permission_classes = (IsAuthenticated, ) @@ -1353,8 +1479,17 @@ class SeadocPublishRevision(APIView): revision_parent_path = revision_file_uuid.parent_path revision_filename = revision_file_uuid.filename - # move revision file + # username username = request.user.username + try: + sdoc_server_api = SdocServerAPI(file_uuid, revision_filename, username) + res = sdoc_server_api.save_doc() + except Exception as e: + logger.error(e) + error_msg = 'Internal Server Error' + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + + # move revision file seafile_api.move_file( repo_id, revision_parent_path, json.dumps([revision_filename]), @@ -1367,12 +1502,6 @@ class SeadocPublishRevision(APIView): dst_file_id = seafile_api.get_file_id_by_path(repo_id, origin_file_path) SeadocRevision.objects.publish(file_uuid, username, dst_file_id) - # refresh docs - doc_uuids = [revision.origin_doc_uuid, revision.doc_uuid] - sdoc_server_api = SdocServerAPI( - revision.origin_doc_uuid, origin_file_filename, username) - sdoc_server_api.internal_refresh_docs(doc_uuids) - # move image files revision_image_parent_path = '/images/sdoc/' + str(revision_file_uuid.uuid) + '/' dir_id = seafile_api.get_dir_id_by_path(repo_id, revision_image_parent_path) @@ -1393,6 +1522,13 @@ class SeadocPublishRevision(APIView): seafile_api.del_file( repo_id, '/images/sdoc/', json.dumps([str(revision_file_uuid.uuid)]), username) + try: + res = sdoc_server_api.publish_doc(str(origin_file_uuid.uuid), origin_file_filename) + except Exception as e: + logger.error(e) + error_msg = 'Internal Server Error' + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + revision = SeadocRevision.objects.get_by_doc_uuid(file_uuid) return Response(revision.to_dict()) @@ -1564,3 +1700,58 @@ class SeadocDirView(APIView): file_list.sort(key=lambda x: x['name'].lower()) dentrys = dir_list + file_list return Response(dentrys) + + +class SdocRevisionBaseVersionContent(APIView): + authentication_classes = (SdocJWTTokenAuthentication, TokenAuthentication, SessionAuthentication) + throttle_classes = (UserRateThrottle,) + + def get(self, request, file_uuid): + if not file_uuid: + error_msg = 'file_uuid %s not found.' % file_uuid + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + uuid_map = FileUUIDMap.objects.get_fileuuidmap_by_uuid(file_uuid) + if not uuid_map: + error_msg = 'File %s uuid_map not found.' % file_uuid + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + revision = SeadocRevision.objects.get_by_doc_uuid(file_uuid) + if not revision: + error_msg = 'Revision %s not found.' % file_uuid + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + if revision.is_published: + error_msg = 'Revision %s is already published.' % file_uuid + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + origin_doc_path = revision.origin_doc_path + if not origin_doc_path: + return api_error(status.HTTP_400_BAD_REQUEST, 'Origin file path is invalid.') + + origin_file_version = revision.origin_file_version + if not origin_file_version: + return api_error(status.HTTP_400_BAD_REQUEST, 'Origin file version is missing.') + + origin_doc_uuid = revision.origin_doc_uuid + origin_doc_uuid_map = FileUUIDMap.objects.get_fileuuidmap_by_uuid(origin_doc_uuid) + if not origin_doc_uuid_map: + error_msg = 'Origin file uuid %s not found.' % file_uuid + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + username = request.user.username + token = seafile_api.get_fileserver_access_token(origin_doc_uuid_map.repo_id, + origin_file_version, 'download', username) + + if not token: + error_msg = 'Origin file %s not found.' % origin_doc_uuid + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + origin_file_name = os.path.basename(origin_doc_path) + download_url = gen_inner_file_get_url(token, origin_file_name) + + resp = requests.get(download_url) + return Response({ + 'content': resp.content + }) + diff --git a/seahub/seadoc/models.py b/seahub/seadoc/models.py index bc02d464ad..dcf8b1e064 100644 --- a/seahub/seadoc/models.py +++ b/seahub/seadoc/models.py @@ -128,6 +128,12 @@ class SeadocRevisionManager(models.Manager): publish_file_version=publish_file_version, is_published=True, ) + + def delete_by_doc_uuid(self, doc_uuid): + self.filter(doc_uuid=doc_uuid).delete() + + def update_origin_file_version(self, doc_uuid, origin_file_version): + return self.filter(doc_uuid=doc_uuid).update(origin_file_version=origin_file_version) class SeadocRevision(models.Model): diff --git a/seahub/seadoc/sdoc_server_api.py b/seahub/seadoc/sdoc_server_api.py index 199d34be71..d44fda78fb 100644 --- a/seahub/seadoc/sdoc_server_api.py +++ b/seahub/seadoc/sdoc_server_api.py @@ -37,3 +37,31 @@ class SdocServerAPI(object): data = {"doc_uuids" : doc_uuids} response = requests.post(url, json=data, headers=self.headers) return parse_response(response) + + def remove_doc(self): + url = self.sdoc_server_url + '/api/v1/docs/' + self.doc_uuid + '/?from=seahub' + response = requests.delete(url, headers=self.headers) + return parse_response(response) + + def save_doc(self): + url = self.sdoc_server_url + '/api/v1/docs/' + self.doc_uuid + '/save/?from=seahub' + response = requests.post(url, json={}, headers=self.headers) + return parse_response(response) + + def publish_doc(self, origin_doc_uuid, origin_doc_name): + url = self.sdoc_server_url + '/api/v1/docs/' + self.doc_uuid + '/publish/?from=seahub' + data = { + 'origin_doc_uuid': origin_doc_uuid, + 'origin_doc_name': origin_doc_name, + } + response = requests.post(url, json=data, headers=self.headers) + return parse_response(response) + + def replace_doc(self): + url = self.sdoc_server_url + '/api/v1/docs/' + self.doc_uuid + '/replace/?from=seahub' + data = { + 'doc_name': self.filename + } + response = requests.post(url, json=data, headers=self.headers) + return parse_response(response) + diff --git a/seahub/seadoc/urls.py b/seahub/seadoc/urls.py index bf1b61de0b..e8787ee667 100644 --- a/seahub/seadoc/urls.py +++ b/seahub/seadoc/urls.py @@ -1,8 +1,8 @@ from django.urls import re_path -from .apis import SeadocAccessToken, SeadocUploadLink, SeadocDownloadLink, SeadocRevisionDownloadLinks, SeadocUploadFile, \ +from .apis import SeadocAccessToken, SeadocUploadLink, SeadocDownloadLink, SeadocOriginFileContent, SeadocUploadFile, \ SeadocUploadImage, SeadocDownloadImage, SeadocAsyncCopyImages, SeadocCopyHistoryFile, SeadocHistory, SeadocDrafts, SeadocMaskAsDraft, \ SeadocCommentsView, SeadocCommentView, SeadocStartRevise, SeadocPublishRevision, SeadocRevisionsCount, SeadocRevisions, \ - SeadocCommentRepliesView, SeadocCommentReplyView, SeadocFileView, SeadocFileUUIDView, SeadocDirView + SeadocCommentRepliesView, SeadocCommentReplyView, SeadocFileView, SeadocFileUUIDView, SeadocDirView, SdocRevisionBaseVersionContent, SeadocRevisionView # api/v2.1/seadoc/ @@ -11,7 +11,6 @@ urlpatterns = [ re_path(r'^upload-file/(?P[-0-9a-f]{36})/$', SeadocUploadFile.as_view(), name='seadoc_upload_file'), re_path(r'^upload-link/(?P[-0-9a-f]{36})/$', SeadocUploadLink.as_view(), name='seadoc_upload_link'), re_path(r'^download-link/(?P[-0-9a-f]{36})/$', SeadocDownloadLink.as_view(), name='seadoc_download_link'), - re_path(r'^revision/download-links/(?P[-0-9a-f]{36})/$', SeadocRevisionDownloadLinks.as_view(), name='seadoc_revision_download_links'), re_path(r'^upload-image/(?P[-0-9a-f]{36})/$', SeadocUploadImage.as_view(), name='seadoc_upload_image'), re_path(r'^download-image/(?P[-0-9a-f]{36})/(?P.*)$', SeadocDownloadImage.as_view(), name='seadoc_download_image'), re_path(r'^async-copy-images/(?P[-0-9a-f]{36})/$', SeadocAsyncCopyImages.as_view(), name='seadoc_async_copy_images'), @@ -27,6 +26,9 @@ urlpatterns = [ re_path(r'^publish-revision/(?P[-0-9a-f]{36})/$', SeadocPublishRevision.as_view(), name='seadoc_publish_revision'), re_path(r'^revisions-count/(?P[-0-9a-f]{36})/$', SeadocRevisionsCount.as_view(), name='seadoc_revisions_count'), re_path(r'^revisions/(?P[-0-9a-f]{36})/$', SeadocRevisions.as_view(), name='seadoc_revisions'), + re_path(r'^revision/(?P[-0-9a-f]{36})/$', SeadocRevisionView.as_view(), name='seadoc_revision'), + re_path(r'^revision/base-version-content/(?P[-0-9a-f]{36})/$', SdocRevisionBaseVersionContent.as_view(), name='sdoc_revision_base_version_content'), + re_path(r'^revision/origin-file-content/(?P[-0-9a-f]{36})/$', SeadocOriginFileContent.as_view(), name='sdoc_revision_origin_file_content'), re_path(r'^file/(?P[-0-9a-f]{36})/$', SeadocFileView.as_view(), name='seadoc_file_view'), re_path(r'^file-uuid/(?P[-0-9a-f]{36})/$', SeadocFileUUIDView.as_view(), name='seadoc_file_uuid_view'), re_path(r'^dir/(?P[-0-9a-f]{36})/$', SeadocDirView.as_view(), name='seadoc_dir_view'), diff --git a/seahub/seadoc/utils.py b/seahub/seadoc/utils.py index 58d863f231..e4ced978fc 100644 --- a/seahub/seadoc/utils.py +++ b/seahub/seadoc/utils.py @@ -10,7 +10,7 @@ from seaserv import seafile_api from seahub.tags.models import FileUUIDMap from seahub.settings import SEADOC_PRIVATE_KEY -from seahub.utils import normalize_file_path, gen_file_get_url, gen_file_upload_url +from seahub.utils import normalize_file_path, gen_file_get_url, gen_file_upload_url, gen_inner_file_get_url from seahub.views import check_folder_permission from seahub.base.templatetags.seahub_tags import email2nickname from seahub.avatar.templatetags.avatar_tags import api_avatar_url @@ -113,7 +113,7 @@ def get_seadoc_upload_link(uuid_map, last_modify_user=''): return upload_link -def get_seadoc_download_link(uuid_map): +def get_seadoc_download_link(uuid_map, is_inner=False): repo_id = uuid_map.repo_id parent_path = uuid_map.parent_path filename = uuid_map.filename @@ -127,7 +127,11 @@ def get_seadoc_download_link(uuid_map): if not token: return None - download_link = gen_file_get_url(token, filename) + if is_inner: + download_link = gen_inner_file_get_url(token, filename) + else: + download_link = gen_file_get_url(token, filename) + return download_link