1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-01 23:20:51 +00:00

SeadocRevisions

This commit is contained in:
skywalker
2023-06-27 18:29:55 +08:00
committed by er-pai-r
parent 178b5b0e78
commit 0da16303f5
4 changed files with 287 additions and 3 deletions

View File

@@ -1,5 +1,6 @@
import os import os
import json import json
import uuid
import logging import logging
import requests import requests
import posixpath import posixpath
@@ -32,12 +33,13 @@ from seahub.tags.models import FileUUIDMap
from seahub.utils.error_msg import file_type_error_msg from seahub.utils.error_msg import file_type_error_msg
from seahub.utils.repo import parse_repo_perm from seahub.utils.repo import parse_repo_perm
from seahub.utils.file_revisions import get_file_revisions_within_limit from seahub.utils.file_revisions import get_file_revisions_within_limit
from seahub.seadoc.models import SeadocHistoryName, SeadocDraft from seahub.seadoc.models import SeadocHistoryName, SeadocDraft, SeadocRevision
from seahub.avatar.templatetags.avatar_tags import api_avatar_url from seahub.avatar.templatetags.avatar_tags import api_avatar_url
from seahub.base.templatetags.seahub_tags import email2nickname, \ from seahub.base.templatetags.seahub_tags import email2nickname, \
email2contact_email email2contact_email
from seahub.utils.timeutils import utc_datetime_to_isoformat_timestr, timestamp_to_isoformat_timestr from seahub.utils.timeutils import utc_datetime_to_isoformat_timestr, timestamp_to_isoformat_timestr
from seahub.base.models import FileComment from seahub.base.models import FileComment
from seahub.constants import PERMISSION_READ_WRITE
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -810,3 +812,189 @@ class SeadocCommentView(APIView):
comment = file_comment.to_dict() comment = file_comment.to_dict()
comment.update(user_to_dict(file_comment.author, request=request, avatar_size=avatar_size)) comment.update(user_to_dict(file_comment.author, request=request, avatar_size=avatar_size))
return Response(comment) return Response(comment)
class SeadocRevisions(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
def get(self, request):
"""list
"""
username = request.user.username
# argument check
owned = request.GET.get('owned')
repo_id = request.GET.get('repo_id')
if not repo_id or not owned:
error_msg = 'repo_id or owned invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
if repo_id:
# resource check
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)
revision_queryset = SeadocRevision.objects.list_by_repo_id(repo_id)
#
elif owned:
revision_queryset = SeadocRevision.objects.list_by_username(username)
revisions = [revision.to_dict() for revision in revision_queryset]
return Response({'revisions': revisions})
def post(self, request):
"""create
"""
username = request.user.username
# argument check
path = request.data.get('p', None)
if not path:
error_msg = 'p invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
repo_id = request.GET.get('repo_id')
if not repo_id:
error_msg = 'repo_id invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
path = normalize_file_path(path)
parent_dir = os.path.dirname(path)
filename = os.path.basename(path)
filetype, fileext = get_file_type_and_ext(filename)
if filetype != SEADOC:
error_msg = 'seadoc file type %s invalid.' % filetype
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
# resource check
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)
# create revision dir if does not exist
revision_dir_id = seafile_api.get_dir_id_by_path(repo_id, '/Revisions')
if revision_dir_id is None:
seafile_api.post_dir(repo_id, '/', 'Revisions', username)
#
origin_file_uuid = get_seadoc_file_uuid(repo, path)
origin_file_id = seafile_api.get_file_id_by_path(repo_id, path)
revision_file_uuid = str(uuid.uuid4())
revision_filename = revision_file_uuid + '.sdoc'
# copy file to revision dir
seafile_api.copy_file(
repo_id, parent_dir,
json.dumps([filename]),
repo_id, '/Revisions',
json.dumps([revision_filename]),
username=username, need_progress=0, synchronous=1
)
revision_uuid_map = FileUUIDMap(
uuid=revision_file_uuid,
repo_id=repo_id,
parent_path=parent_dir,
filename=revision_filename,
)
revision_uuid_map.save()
revision = SeadocRevision.objects.create(
doc_uuid=revision_file_uuid,
origin_doc_uuid=origin_file_uuid,
repo_id=repo_id,
origin_file_path=path,
username=username,
origin_file_version=origin_file_id,
)
return Response(revision.to_dict())
class SeadocPublishRevision(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated, )
throttle_classes = (UserRateThrottle, )
def post(self, request, file_uuid):
"""publish
"""
force = request.data.get('force') # used when origin file deleted
# resource check
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)
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 origin file info
origin_file_uuid = FileUUIDMap.objects.get_fileuuidmap_by_uuid(
revision.origin_doc_uuid)
if not origin_file_uuid and not force:
error_msg = 'origin sdoc %s not found.' % file_uuid
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
if origin_file_uuid:
origin_file_parent_path = origin_file_uuid.parent_path
origin_file_filename = origin_file_uuid.filename
else:
origin_file_parent_path = os.path.dirname(revision.origin_doc_path)
origin_file_filename = os.path.basename(revision.origin_doc_path)
# check if origin file's parent folder exists
if not seafile_api.get_dir_id_by_path(repo_id, origin_file_parent_path):
dst_parent_path = '/'
else:
dst_parent_path = origin_file_parent_path
# get revision file info
revision_file_uuid = FileUUIDMap.objects.get_fileuuidmap_by_uuid(file_uuid)
revision_parent_path = revision_file_uuid.parent_pat
revision_filename = revision_file_uuid.filename
# move revision file
username = request.user.username
seafile_api.move_file(repo_id, revision_parent_path,
json.dumps([revision_filename]),
repo_id, dst_parent_path,
json.dumps([origin_file_filename]),
replace=1, username=username,
need_progress=0, synchronous=1)
dst_file_id = seafile_api.get_file_id_by_path(repo_id, origin_file_filename)
revision = SeadocRevision.objects.publish(
file_uuid, username, dst_file_id)
return Response(revision.to_dict())

View File

@@ -1,6 +1,9 @@
import os
from django.db import models from django.db import models
from seahub.utils.timeutils import datetime_to_isoformat_timestr from seahub.utils.timeutils import datetime_to_isoformat_timestr
from seahub.base.templatetags.seahub_tags import email2nickname
class SeadocHistoryNameManager(models.Manager): class SeadocHistoryNameManager(models.Manager):
@@ -28,7 +31,6 @@ class SeadocHistoryName(models.Model):
def to_dict(self): def to_dict(self):
return { return {
'id': self.pk,
'doc_uuid': self.doc_uuid, 'doc_uuid': self.doc_uuid,
'obj_id': self.obj_id, 'obj_id': self.obj_id,
'name': self.name, 'name': self.name,
@@ -75,3 +77,75 @@ class SeadocDraft(models.Model):
'username': self.username, 'username': self.username,
'created_at': datetime_to_isoformat_timestr(self.created_at), 'created_at': datetime_to_isoformat_timestr(self.created_at),
} }
class SeadocRevisionManager(models.Manager):
def get_by_doc_uuid(self, doc_uuid):
return self.filter(doc_uuid=doc_uuid).first()
def list_by_doc_uuids(self, doc_uuid_list):
return self.filter(doc_uuid__in=doc_uuid_list)
def list_by_origin_doc_uuid(self, origin_doc_uuid):
return self.filter(origin_doc_uuid=origin_doc_uuid)
def list_by_username(self, username):
return self.filter(username=username)
def list_by_repo_id(self, repo_id):
return self.filter(repo_id=repo_id)
def publish(self, doc_uuid, publisher, publish_file_version):
return self.filter(doc_uuid=doc_uuid).update(
publisher=publisher,
publish_file_version=publish_file_version,
is_published=True,
)
class SeadocRevision(models.Model):
"""
"""
doc_uuid = models.CharField(max_length=36, unique=True)
origin_doc_uuid = models.CharField(max_length=36, db_index=True)
repo_id = models.CharField(max_length=36, db_index=True)
origin_doc_path = models.TextField() # used when origin file deleted
username = models.CharField(max_length=255, db_index=True)
origin_file_version = models.CharField(max_length=100)
publish_file_version = models.CharField(max_length=100, null=True)
publisher = models.CharField(max_length=255, null=True)
is_published = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True, db_index=True)
updated_at = models.DateTimeField(auto_now=True, db_index=True)
objects = SeadocRevisionManager()
class Meta:
db_table = 'sdoc_revision'
def to_dict(self):
from seahub.tags.models import FileUUIDMap
file_uuid = FileUUIDMap.objects.get_fileuuidmap_by_uuid(self.origin_doc_uuid)
if file_uuid:
origin_parent_path = file_uuid.parent_path
origin_filename = file_uuid.filename
else:
origin_parent_path = os.path.dirname(self.origin_doc_path)
origin_filename = os.path.basename(self.origin_doc_path)
return {
'username': self.username,
'nickname': email2nickname(self.username),
'repo_id': self.repo_id,
'doc_uuid': self.doc_uuid,
'origin_doc_uuid': self.origin_doc_uuid,
'origin_parent_path': origin_parent_path,
'origin_filename': origin_filename,
'origin_file_version': self.origin_file_version,
'publish_file_version': self.publish_file_version,
'publisher': self.publisher,
'publisher_nickname': email2nickname(self.publisher),
'is_published': self.is_published,
'created_at': datetime_to_isoformat_timestr(self.created_at),
'updated_at': datetime_to_isoformat_timestr(self.updated_at),
}

View File

@@ -1,7 +1,7 @@
from django.urls import re_path from django.urls import re_path
from .apis import SeadocAccessToken, SeadocUploadLink, SeadocDownloadLink, SeadocUploadFile, \ from .apis import SeadocAccessToken, SeadocUploadLink, SeadocDownloadLink, SeadocUploadFile, \
SeadocUploadImage, SeadocDownloadImage, SeadocCopyHistoryFile, SeadocHistory, SeadocDrafts, SeadocMaskAsDraft, \ SeadocUploadImage, SeadocDownloadImage, SeadocCopyHistoryFile, SeadocHistory, SeadocDrafts, SeadocMaskAsDraft, \
SeadocCommentsView, SeadocCommentView SeadocCommentsView, SeadocCommentView, SeadocRevisions, SeadocPublishRevision
urlpatterns = [ urlpatterns = [
re_path(r'^access-token/(?P<repo_id>[-0-9a-f]{36})/$', SeadocAccessToken.as_view(), name='seadoc_access_token'), re_path(r'^access-token/(?P<repo_id>[-0-9a-f]{36})/$', SeadocAccessToken.as_view(), name='seadoc_access_token'),
@@ -16,4 +16,6 @@ urlpatterns = [
re_path(r'^mask-as-draft/(?P<repo_id>[-0-9a-f]{36})/$', SeadocMaskAsDraft.as_view(), name='seadoc_mask_as_draft'), re_path(r'^mask-as-draft/(?P<repo_id>[-0-9a-f]{36})/$', SeadocMaskAsDraft.as_view(), name='seadoc_mask_as_draft'),
re_path(r'^comments/(?P<file_uuid>[-0-9a-f]{36})/$', SeadocCommentsView.as_view(), name='seadoc_comments'), re_path(r'^comments/(?P<file_uuid>[-0-9a-f]{36})/$', SeadocCommentsView.as_view(), name='seadoc_comments'),
re_path(r'^comment/(?P<file_uuid>[-0-9a-f]{36})/(?P<comment_id>\d+)/$', SeadocCommentView.as_view(), name='seadoc_comment'), re_path(r'^comment/(?P<file_uuid>[-0-9a-f]{36})/(?P<comment_id>\d+)/$', SeadocCommentView.as_view(), name='seadoc_comment'),
re_path(r'^revisions/$', SeadocRevisions.as_view(), name='seadoc_revisions'),
re_path(r'^publish-revision/$', SeadocPublishRevision.as_view(), name='seadoc_publish_revision'),
] ]

View File

@@ -1397,3 +1397,23 @@ CREATE TABLE `sdoc_draft` (
KEY `sdoc_draft_repo_id` (`repo_id`), KEY `sdoc_draft_repo_id` (`repo_id`),
KEY `sdoc_draft_username` (`username`) KEY `sdoc_draft_username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `sdoc_revision` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`repo_id` varchar(36) NOT NULL,
`doc_uuid` varchar(36) NOT NULL,
`origin_doc_uuid` varchar(36) NOT NULL,
`origin_file_version` varchar(100) NOT NULL,
`publish_file_version` varchar(100) DEFAULT NULL,
`username` varchar(255) NOT NULL,
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
`is_published` tinyint(1) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `sdoc_revise_doc_uuid` (`doc_uuid`),
KEY `sdoc_revision_repo_id` (`repo_id`),
KEY `sdoc_revision_origin_doc_uuid` (`origin_doc_uuid`),
KEY `sdoc_revision_username` (`username`),
KEY `sdoc_revision_created_at` (`created_at`),
KEY `sdoc_revision_updated_at` (`updated_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;