diff --git a/frontend/src/components/dirent-list-view/dirent-list-item.js b/frontend/src/components/dirent-list-view/dirent-list-item.js
index 0212a61271..199bc3b1ac 100644
--- a/frontend/src/components/dirent-list-view/dirent-list-item.js
+++ b/frontend/src/components/dirent-list-view/dirent-list-item.js
@@ -264,6 +264,12 @@ class DirentListItem extends React.Component {
case 'Lock':
this.onLockItem();
break;
+ case 'Mask as draft':
+ this.onMaskAsDraft();
+ break;
+ case 'Unmask as draft':
+ this.onUnmaskAsDraft();
+ break;
case 'Comment':
this.props.onDirentClick(this.props.dirent);
this.props.showDirentDetail('comments');
@@ -353,6 +359,28 @@ class DirentListItem extends React.Component {
});
}
+ onMaskAsDraft = () => {
+ let repoID = this.props.repoID;
+ let filePath = this.getDirentPath(this.props.dirent);
+ seafileAPI.sdocMaskAsDraft(repoID, filePath).then((res) => {
+ this.props.updateDirent(this.props.dirent, 'is_sdoc_draft', true);
+ }).catch(error => {
+ let errMessage = Utils.getErrorMsg(error);
+ toaster.danger(errMessage);
+ });
+ }
+
+ onUnmaskAsDraft = () => {
+ let repoID = this.props.repoID;
+ let filePath = this.getDirentPath(this.props.dirent);
+ seafileAPI.sdocUnmaskAsDraft(repoID, filePath).then((res) => {
+ this.props.updateDirent(this.props.dirent, 'is_sdoc_draft', false);
+ }).catch(error => {
+ let errMessage = Utils.getErrorMsg(error);
+ toaster.danger(errMessage);
+ });
+ }
+
onHistory = () => {
let repoID = this.props.repoID;
let filePath = this.getDirentPath(this.props.dirent);
@@ -697,6 +725,9 @@ class DirentListItem extends React.Component {
{dirent.name} :
{dirent.name}
}
+ {(Utils.isSdocFile(dirent.name) && dirent.is_sdoc_draft) &&
+ {'(draft)'}
+ }
)}
diff --git a/frontend/src/models/dirent.js b/frontend/src/models/dirent.js
index afa25203bd..f071dd4933 100644
--- a/frontend/src/models/dirent.js
+++ b/frontend/src/models/dirent.js
@@ -37,6 +37,9 @@ class Dirent {
if (json.encoded_thumbnail_src) {
this.encoded_thumbnail_src = json.encoded_thumbnail_src;
}
+ if (Utils.isSdocFile(json.name)) {
+ this.is_sdoc_draft = json.is_sdoc_draft;
+ }
}
}
diff --git a/frontend/src/utils/text-translation.js b/frontend/src/utils/text-translation.js
index 6b57f3055e..404785c489 100644
--- a/frontend/src/utils/text-translation.js
+++ b/frontend/src/utils/text-translation.js
@@ -16,6 +16,8 @@ const TextTranslation = {
'OPEN_VIA_CLIENT' : {key : 'Open via Client', value : gettext('Open via Client')},
'LOCK' : {key : 'Lock', value : gettext('Lock')},
'UNLOCK' : {key : 'Unlock', value : gettext('Unlock')},
+ 'MASK_AS_DRAFT' : {key : 'Mask as draft', value : gettext('Mask as draft')},
+ 'UNMASK_AS_DRAFT' : {key : 'Unmask as draft', value : gettext('Unmask as draft')},
'COMMENT' : {key : 'Comment', value : gettext('Comment')},
'HISTORY' : {key : 'History', value : gettext('History')},
'ACCESS_LOG' : {key : 'Access Log', value : gettext('Access Log')},
diff --git a/frontend/src/utils/utils.js b/frontend/src/utils/utils.js
index 45b571e117..82b7853b1d 100644
--- a/frontend/src/utils/utils.js
+++ b/frontend/src/utils/utils.js
@@ -526,7 +526,7 @@ export const Utils = {
getFileOperationList: function(isRepoOwner, currentRepoInfo, dirent, isContextmenu) {
let list = [];
- const { SHARE, DOWNLOAD, DELETE, RENAME, MOVE, COPY, TAGS, UNLOCK, LOCK,
+ const { SHARE, DOWNLOAD, DELETE, RENAME, MOVE, COPY, TAGS, UNLOCK, LOCK, MASK_AS_DRAFT, UNMASK_AS_DRAFT,
COMMENT, HISTORY, ACCESS_LOG, OPEN_VIA_CLIENT, ONLYOFFICE_CONVERT } = TextTranslation;
const permission = dirent.permission;
const { isCustomPermission, customPermission } = Utils.getUserPermission(permission);
@@ -594,6 +594,13 @@ export const Utils = {
}
list.push('Divider');
+ if (Utils.isSdocFile(dirent.name)) {
+ if (dirent.is_sdoc_draft) {
+ list.push(UNMASK_AS_DRAFT);
+ } else {
+ list.push(MASK_AS_DRAFT);
+ }
+ }
if (enableFileComment) {
list.push(COMMENT);
}
@@ -804,6 +811,20 @@ export const Utils = {
}
},
+ isSdocFile: function(filePath) {
+ let index = filePath.lastIndexOf('.');
+ if (index === -1) {
+ return false;
+ } else {
+ let type = filePath.substring(index).toLowerCase();
+ if (type === '.sdoc') {
+ return true;
+ } else {
+ return false;
+ }
+ }
+ },
+
isInternalFileLink: function(url, repoID) {
var re = new RegExp(serviceURL + '/lib/' + repoID + '/file.*');
return re.test(url);
diff --git a/seahub/api2/endpoints/dir.py b/seahub/api2/endpoints/dir.py
index 31e394239f..7329ad8010 100644
--- a/seahub/api2/endpoints/dir.py
+++ b/seahub/api2/endpoints/dir.py
@@ -20,10 +20,10 @@ from seahub.api2.views import get_dir_file_recursively
from seahub.thumbnail.utils import get_thumbnail_src
from seahub.views import check_folder_permission
from seahub.utils import check_filename_with_rename, is_valid_dirent_name, \
- normalize_dir_path, is_pro_version, FILEEXT_TYPE_MAP
+ normalize_dir_path, is_pro_version, FILEEXT_TYPE_MAP, get_file_type_and_ext
from seahub.utils.timeutils import timestamp_to_isoformat_timestr
from seahub.utils.file_tags import get_files_tags_in_dir
-from seahub.utils.file_types import IMAGE, VIDEO, XMIND
+from seahub.utils.file_types import IMAGE, VIDEO, XMIND, SEADOC
from seahub.base.models import UserStarredFiles
from seahub.base.templatetags.seahub_tags import email2nickname, \
email2contact_email
@@ -98,6 +98,17 @@ def get_dir_file_info_list(username, request_type, repo_obj, parent_dir,
logger.error(e)
files_tags_in_dir = {}
+ try:
+ from seahub.tags.models import FileUUIDMap
+ from seahub.seadoc.models import SeadocDraft
+ file_uuid_queryset = FileUUIDMap.objects.get_fileuuidmaps_by_parent_path(
+ repo_id, parent_dir)
+ file_uuid_list = [item.uuid for item in file_uuid_queryset]
+ seadoc_draft_queryset = SeadocDraft.objects.list_by_doc_uuids(
+ file_uuid_list)
+ except Exception as e:
+ logger.error(e)
+
for dirent in file_list:
file_name = dirent.obj_name
@@ -166,6 +177,22 @@ def get_dir_file_info_list(username, request_type, repo_obj, parent_dir,
src = get_thumbnail_src(repo_id, thumbnail_size, file_path)
file_info['encoded_thumbnail_src'] = quote(src)
+ # sdoc
+ filetype, fileext = get_file_type_and_ext(file_info['name'])
+ if filetype == SEADOC:
+ try:
+ file_uuid_map = file_uuid_queryset.filter(
+ filename=file_name).first()
+ if file_uuid_map:
+ sdoc_draft = seadoc_draft_queryset.filter(
+ doc_uuid=file_uuid_map.uuid).first()
+ if sdoc_draft:
+ file_info['is_sdoc_draft'] = True
+ else:
+ file_info['is_sdoc_draft'] = False
+ except Exception as e:
+ logger.error(e)
+
file_info_list.append(file_info)
dir_info_list.sort(key=lambda x: x['name'].lower())
diff --git a/seahub/api2/endpoints/file.py b/seahub/api2/endpoints/file.py
index 03effbfe5b..14a234ac61 100644
--- a/seahub/api2/endpoints/file.py
+++ b/seahub/api2/endpoints/file.py
@@ -29,6 +29,7 @@ from seahub.constants import PERMISSION_READ_WRITE
from seahub.utils.repo import parse_repo_perm, is_repo_admin, is_repo_owner
from seahub.utils.file_types import MARKDOWN, TEXT, SEADOC
from seahub.tags.models import FileUUIDMap
+from seahub.seadoc.models import SeadocHistoryName, SeadocDraft
from seahub.settings import MAX_UPLOAD_FILE_NAME_LEN, OFFICE_TEMPLATE_ROOT
@@ -717,8 +718,12 @@ class FileView(APIView):
try: # rm sdoc fileuuid
filetype, fileext = get_file_type_and_ext(file_name)
if filetype == SEADOC:
+ from seahub.seadoc.utils import get_seadoc_file_uuid
+ file_uuid = get_seadoc_file_uuid(repo, path)
FileUUIDMap.objects.delete_fileuuidmap_by_path(
repo_id, parent_dir, file_name, is_dir=False)
+ SeadocHistoryName.objects.filter(doc_uuid=file_uuid).delete()
+ SeadocDraft.objects.filter(doc_uuid=file_uuid).delete()
except Exception as e:
logger.error(e)
diff --git a/seahub/api2/views.py b/seahub/api2/views.py
index a5ae2a6149..9b671cc51d 100644
--- a/seahub/api2/views.py
+++ b/seahub/api2/views.py
@@ -73,6 +73,7 @@ from seahub.utils import gen_file_get_url, gen_token, gen_file_upload_url, \
normalize_file_path, get_no_duplicate_obj_name, normalize_dir_path
from seahub.tags.models import FileUUIDMap
+from seahub.seadoc.models import SeadocHistoryName, SeadocDraft
from seahub.utils.file_types import IMAGE, SEADOC
from seahub.utils.file_revisions import get_file_revisions_after_renamed
from seahub.utils.devices import do_unlink_device
@@ -3154,8 +3155,12 @@ class FileView(APIView):
try: # rm sdoc fileuuid
filetype, fileext = get_file_type_and_ext(file_name)
if filetype == SEADOC:
+ from seahub.seadoc.utils import get_seadoc_file_uuid
+ file_uuid = get_seadoc_file_uuid(repo, path)
FileUUIDMap.objects.delete_fileuuidmap_by_path(
repo_id, parent_dir, file_name, is_dir=False)
+ SeadocHistoryName.objects.filter(doc_uuid=file_uuid).delete()
+ SeadocDraft.objects.filter(doc_uuid=file_uuid).delete()
except Exception as e:
logger.error(e)
diff --git a/seahub/seadoc/apis.py b/seahub/seadoc/apis.py
index c26a36acac..3e74d34c71 100644
--- a/seahub/seadoc/apis.py
+++ b/seahub/seadoc/apis.py
@@ -31,7 +31,7 @@ from seahub.tags.models import FileUUIDMap
from seahub.utils.error_msg import file_type_error_msg
from seahub.utils.repo import parse_repo_perm
from seahub.utils.file_revisions import get_file_revisions_within_limit
-from seahub.seadoc.models import SeadocHistoryName
+from seahub.seadoc.models import SeadocHistoryName, SeadocDraft
from seahub.avatar.templatetags.avatar_tags import api_avatar_url
from seahub.base.templatetags.seahub_tags import email2nickname, \
email2contact_email
@@ -504,3 +504,89 @@ class SeadocHistory(APIView):
'obj_id': obj_id,
'name': new_name,
})
+
+
+class SeadocMaskAsDraft(APIView):
+
+ authentication_classes = (TokenAuthentication, SessionAuthentication)
+ permission_classes = (IsAuthenticated,)
+ throttle_classes = (UserRateThrottle, )
+
+ def post(self, request, repo_id):
+ """ Mask as draft
+ """
+ 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)
+
+ 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, path)
+ if not permission:
+ error_msg = 'Permission denied.'
+ return api_error(status.HTTP_403_FORBIDDEN, error_msg)
+
+ #
+ file_uuid = get_seadoc_file_uuid(repo, path)
+ exist_draft = SeadocDraft.objects.get_by_doc_uuid(file_uuid)
+ if exist_draft:
+ error_msg = '%s is already draft' % filename
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ draft = SeadocDraft.objects.mask_as_draft(file_uuid, username)
+
+ return Response(draft.to_dict())
+
+ def delete(self, request, repo_id):
+ """ Unmask as draft
+ """
+ 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)
+
+ 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, path)
+ if not permission:
+ error_msg = 'Permission denied.'
+ return api_error(status.HTTP_403_FORBIDDEN, error_msg)
+
+ #
+ file_uuid = get_seadoc_file_uuid(repo, path)
+ SeadocDraft.objects.unmask_as_draft(file_uuid)
+
+ return Response({'success': True})
diff --git a/seahub/seadoc/models.py b/seahub/seadoc/models.py
index f84c2bc6da..47f9ee1a89 100644
--- a/seahub/seadoc/models.py
+++ b/seahub/seadoc/models.py
@@ -1,5 +1,7 @@
from django.db import models
+from seahub.utils.timeutils import datetime_to_isoformat_timestr
+
class SeadocHistoryNameManager(models.Manager):
def update_name(self, doc_uuid, obj_id, name):
@@ -31,3 +33,40 @@ class SeadocHistoryName(models.Model):
'obj_id': self.obj_id,
'name': self.name,
}
+
+
+class SeadocDraftManager(models.Manager):
+
+ def get_by_doc_uuid(self, doc_uuid):
+ return self.filter(doc_uuid=doc_uuid).first()
+
+ def mask_as_draft(self, doc_uuid, username):
+ return self.create(doc_uuid=doc_uuid, username=username)
+
+ def unmask_as_draft(self, doc_uuid):
+ return self.filter(doc_uuid=doc_uuid).delete()
+
+ def list_by_doc_uuids(self, doc_uuid_list):
+ return self.filter(doc_uuid__in=doc_uuid_list)
+
+ def list_by_username(self, username):
+ return self.filter(username=username)
+
+
+class SeadocDraft(models.Model):
+ doc_uuid = models.CharField(max_length=36, unique=True)
+ username = models.CharField(max_length=255, db_index=True)
+ created_at = models.DateTimeField(auto_now_add=True)
+
+ objects = SeadocDraftManager()
+
+ class Meta:
+ db_table = 'sdoc_draft'
+
+ def to_dict(self):
+ return {
+ 'id': self.pk,
+ 'doc_uuid': self.doc_uuid,
+ 'username': self.username,
+ 'created_at': datetime_to_isoformat_timestr(self.created_at),
+ }
diff --git a/seahub/seadoc/urls.py b/seahub/seadoc/urls.py
index 956956aee4..0db00e0b17 100644
--- a/seahub/seadoc/urls.py
+++ b/seahub/seadoc/urls.py
@@ -1,6 +1,6 @@
from django.urls import re_path
from .apis import SeadocAccessToken, SeadocUploadLink, SeadocDownloadLink, SeadocUploadFile, \
- SeadocUploadImage, SeadocDownloadImage, SeadocCopyHistoryFile, SeadocHistory
+ SeadocUploadImage, SeadocDownloadImage, SeadocCopyHistoryFile, SeadocHistory, SeadocMaskAsDraft
urlpatterns = [
re_path(r'^access-token/(?P[-0-9a-f]{36})/$', SeadocAccessToken.as_view(), name='seadoc_access_token'),
@@ -11,4 +11,5 @@ urlpatterns = [
re_path(r'^download-image/(?P[-0-9a-f]{36})/(?P.*)$', SeadocDownloadImage.as_view(), name='seadoc_download_image'),
re_path(r'^copy-history-file/(?P[-0-9a-f]{36})/$', SeadocCopyHistoryFile.as_view(), name='seadoc_copy_history_file'),
re_path(r'^history/(?P[-0-9a-f]{36})/$', SeadocHistory.as_view(), name='seadoc_history'),
+ re_path(r'^mask-as-draft/(?P[-0-9a-f]{36})/$', SeadocMaskAsDraft.as_view(), name='seadoc_mask_as_draft'),
]
diff --git a/sql/mysql.sql b/sql/mysql.sql
index be7c8e69b4..325b479d51 100644
--- a/sql/mysql.sql
+++ b/sql/mysql.sql
@@ -1385,3 +1385,13 @@ CREATE TABLE `history_name` (
KEY `history_name_doc_uuid` (`doc_uuid`),
UNIQUE KEY `history_name_doc_uuid_obj_id` (`doc_uuid`, `obj_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+CREATE TABLE `sdoc_draft` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `doc_uuid` char(36) NOT NULL,
+ `username` varchar(255) NOT NULL,
+ `created_at` datetime NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `sdoc_draft_doc_uuid` (`doc_uuid`),
+ KEY `sdoc_draft_username` (`username`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;