diff --git a/frontend/src/components/dialog/create-file-dialog.js b/frontend/src/components/dialog/create-file-dialog.js
index 7d83d467df..225539dc1b 100644
--- a/frontend/src/components/dialog/create-file-dialog.js
+++ b/frontend/src/components/dialog/create-file-dialog.js
@@ -16,6 +16,7 @@ class CreateFile extends React.Component {
this.state = {
parentPath: '',
childName: props.fileType,
+ isDraft: false,
};
this.newInput = React.createRef();
}
@@ -28,7 +29,8 @@ class CreateFile extends React.Component {
handleSubmit = () => {
let path = this.state.parentPath + this.state.childName;
- this.props.onAddFile(path);
+ let isDraft = this.state.isDraft;
+ this.props.onAddFile(path, isDraft);
}
handleKeyPress = (e) => {
@@ -37,6 +39,55 @@ class CreateFile extends React.Component {
}
}
+ handleCheck = () => {
+ let pos = this.state.childName.lastIndexOf(".");
+
+ if (this.state.isDraft) {
+ // from draft to not draft
+ // case 1, normally, the file name is ended with `(draft)`, like `test(draft).md`
+ // case 2, the file name is not ended with `(draft)`, the user has deleted some characters, like `test(dra.md`
+ let p = this.state.childName.substring(pos-7, pos);
+ let fileName = this.state.childName.substring(0, pos-7);
+ let fileType = this.state.childName.substring(pos);
+ if (p === '(draft)') {
+ // remove `(draft)` from file name
+ this.setState({
+ childName: fileName + fileType,
+ isDraft: !this.state.isDraft
+ })
+ } else {
+ // don't change file name
+ this.setState({
+ isDraft: !this.state.isDraft
+ })
+ }
+ }
+
+ if (!this.state.isDraft) {
+ // from not draft to draft
+ // case 1, test.md ===> test(draft).md
+ // case 2, .md ===> (draft).md
+ // case 3, no '.' in the file name, don't change the file name
+ if (pos > 0) {
+ let fileName = this.state.childName.substring(0, pos);
+ let fileType = this.state.childName.substring(pos);
+ this.setState({
+ childName: fileName + '(draft)' + fileType,
+ isDraft: !this.state.isDraft
+ })
+ } else if (pos === 0 ) {
+ this.setState({
+ childName: '(draft)' + this.state.childname,
+ isDraft: !this.state.isdraft
+ })
+ } else {
+ this.setState({
+ isDraft: !this.state.isdraft
+ })
+ }
+ }
+ }
+
toggle = () => {
this.props.addFileCancel();
}
@@ -67,6 +118,13 @@ class CreateFile extends React.Component {
{this.newInput = input;}} id="fileName" placeholder={gettext('newName')} value={this.state.childName} onChange={this.handleChange}/>
+
+
+
+ {' '}{gettext("This is a draft.")}
+
+
+
diff --git a/frontend/src/pages/drafts/draft-content.js b/frontend/src/pages/drafts/draft-content.js
index 34c812d3b2..9771d4ce7b 100644
--- a/frontend/src/pages/drafts/draft-content.js
+++ b/frontend/src/pages/drafts/draft-content.js
@@ -64,7 +64,8 @@ class DraftContent extends React.Component {
let draft = this.state.currentDraft;
editUtilties.createDraftReview(draft.id).then(res => {
- window.open(siteRoot + 'drafts/review/' + res.data.id);
+ const w = window.open()
+ w.location = siteRoot + 'drafts/review/' + res.data.id;
}).catch((error) => {
if (error.response.status == '409') {
Toast.error('The draft review is existing.');
diff --git a/frontend/src/pages/repo-wiki-mode/main-panel.js b/frontend/src/pages/repo-wiki-mode/main-panel.js
index 44212d3e6d..48b2dd7208 100644
--- a/frontend/src/pages/repo-wiki-mode/main-panel.js
+++ b/frontend/src/pages/repo-wiki-mode/main-panel.js
@@ -158,9 +158,9 @@ class MainPanel extends Component {
this.setState({showFileDialog: !this.state.showFileDialog});
}
- onMainAddFile = (filePath) => {
+ onMainAddFile = (filePath, isDraft) => {
this.setState({showFileDialog: !this.state.showFileDialog});
- this.props.onMainAddFile(filePath);
+ this.props.onMainAddFile(filePath, isDraft);
}
onMainAddFolder = (dirPath) => {
diff --git a/frontend/src/pages/repo-wiki-mode/side-panel.js b/frontend/src/pages/repo-wiki-mode/side-panel.js
index 2846460967..b10824b888 100644
--- a/frontend/src/pages/repo-wiki-mode/side-panel.js
+++ b/frontend/src/pages/repo-wiki-mode/side-panel.js
@@ -127,9 +127,9 @@ class SidePanel extends Component {
this.props.onAddFolderNode(dirPath);
}
- onAddFileNode = (filePath) => {
+ onAddFileNode = (filePath, isDraft) => {
this.setState({showFile: !this.state.showFile});
- this.props.onAddFileNode(filePath);
+ this.props.onAddFileNode(filePath, isDraft);
}
onRenameNode = (newName) => {
diff --git a/frontend/src/repo-wiki-mode.js b/frontend/src/repo-wiki-mode.js
index 17ab222683..a6c7a20ec1 100644
--- a/frontend/src/repo-wiki-mode.js
+++ b/frontend/src/repo-wiki-mode.js
@@ -252,8 +252,8 @@ class Wiki extends Component {
});
}
- onAddFileNode = (filePath) => {
- editorUtilities.createFile(filePath).then(res => {
+ onAddFileNode = (filePath, isDraft) => {
+ editorUtilities.createFile(filePath, isDraft).then(res => {
let tree = this.state.tree_data.clone();
let name = this.getFileNameByPath(filePath);
let index = filePath.lastIndexOf('/');
diff --git a/frontend/src/utils/editor-utilties.js b/frontend/src/utils/editor-utilties.js
index ec86d17f3b..9ef815895d 100644
--- a/frontend/src/utils/editor-utilties.js
+++ b/frontend/src/utils/editor-utilties.js
@@ -38,8 +38,8 @@ class EditorUtilities {
});
}
- createFile(filePath) {
- return seafileAPI.createFile(repoID, filePath);
+ createFile(filePath, isDraft) {
+ return seafileAPI.createFile(repoID, filePath, isDraft);
}
deleteFile(filePath) {
diff --git a/seahub/api2/endpoints/draft_reviews.py b/seahub/api2/endpoints/draft_reviews.py
index 9b1b35adbe..11c85376d9 100644
--- a/seahub/api2/endpoints/draft_reviews.py
+++ b/seahub/api2/endpoints/draft_reviews.py
@@ -1,4 +1,6 @@
# Copyright (c) 2012-2016 Seafile Ltd.
+import os
+
from rest_framework import status
from rest_framework.authentication import SessionAuthentication
from rest_framework.permissions import IsAuthenticated
@@ -89,7 +91,33 @@ class DraftReviewView(APIView):
return api_error(status.HTTP_409_CONFLICT,
'There is a conflict between the draft and the original file')
- file_id = seafile_api.get_file_id_by_path(r.origin_repo_id, r.origin_file_path)
+ origin_file_path = r.origin_file_path
+
+ # if it is a new draft
+ # case1. '/path/test(draft).md' ---> '/path/test.md'
+ # case2. '/path/test(dra.md' ---> '/path/test(dra.md'
+ if d.draft_file_path == r.origin_file_path:
+ new_draft_dir = os.path.dirname(origin_file_path)
+ new_draft_name = os.path.basename(origin_file_path)
+
+ draft_flag = os.path.splitext(new_draft_name)[0][-7:]
+
+ # remove `(draft)` from file name
+ if draft_flag == '(draft)':
+ f = os.path.splitext(new_draft_name)[0][:-7]
+ file_type = os.path.splitext(new_draft_name)[-1]
+ new_draft_name = f + file_type
+
+ if new_draft_dir == '/':
+ origin_file_path = new_draft_dir + new_draft_name
+ else:
+ origin_file_path = new_draft_dir + '/' + new_draft_name
+
+ r.draft_file_path = origin_file_path
+ r.origin_file_path = origin_file_path
+
+ # get draft published version
+ file_id = seafile_api.get_file_id_by_path(r.origin_repo_id, origin_file_path)
r.publish_file_version = file_id
r.save()
d.delete()
diff --git a/seahub/api2/endpoints/drafts.py b/seahub/api2/endpoints/drafts.py
index 38139baa0a..8ffcaaae48 100644
--- a/seahub/api2/endpoints/drafts.py
+++ b/seahub/api2/endpoints/drafts.py
@@ -56,17 +56,17 @@ class DraftsView(APIView):
error_msg = 'Library %s not found.' % repo_id
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
- file_id = seafile_api.get_file_id_by_path(repo.id, file_path)
- if not file_id:
- return api_error(status.HTTP_404_NOT_FOUND,
- "File %s not found" % file_path)
-
# perm check
perm = check_folder_permission(request, repo.id, file_path)
if perm != PERMISSION_READ_WRITE:
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
+ file_id = seafile_api.get_file_id_by_path(repo.id, file_path)
+ if not file_id:
+ return api_error(status.HTTP_404_NOT_FOUND,
+ "File %s not found" % file_path)
+
username = request.user.username
try:
diff --git a/seahub/api2/views.py b/seahub/api2/views.py
index 13fe0d8959..393360f8da 100644
--- a/seahub/api2/views.py
+++ b/seahub/api2/views.py
@@ -58,6 +58,7 @@ from seahub.thumbnail.utils import generate_thumbnail
from seahub.notifications.models import UserNotification
from seahub.options.models import UserOptions
from seahub.profile.models import Profile, DetailedProfile
+from seahub.drafts.models import Draft
from seahub.signals import (repo_created, repo_deleted)
from seahub.share.models import FileShare, OrgFileShare, UploadLinkShare
from seahub.utils import gen_file_get_url, gen_token, gen_file_upload_url, \
@@ -2632,6 +2633,7 @@ class FileView(APIView):
username = request.user.username
parent_dir = os.path.dirname(path)
operation = request.POST.get('operation', '')
+ is_draft = request.POST.get('is_draft', '')
file_info = {}
if operation.lower() == 'rename':
@@ -2772,6 +2774,17 @@ class FileView(APIView):
return api_error(status.HTTP_403_FORBIDDEN,
'You do not have permission to create file.')
+ if is_draft.lower() == 'true':
+ file_name = os.path.basename(path)
+ file_dir = os.path.dirname(path)
+
+ draft_type = os.path.splitext(file_name)[0][-7:]
+ file_type = os.path.splitext(file_name)[-1]
+
+ if draft_type != '(draft)':
+ f = os.path.splitext(file_name)[0]
+ path = file_dir + '/' + f + '(draft)' + file_type
+
new_file_name = os.path.basename(path)
if not seafile_api.is_valid_filename('fake_repo_id', new_file_name):
@@ -2787,6 +2800,10 @@ class FileView(APIView):
return api_error(HTTP_520_OPERATION_FAILED,
'Failed to create file.')
+ if is_draft.lower() == 'true':
+ repo = seafile_api.get_repo(repo_id)
+ Draft.objects.add(username, repo, path, file_exist=False)
+
if request.GET.get('reloaddir', '').lower() == 'true':
return reloaddir(request, repo, parent_dir)
else:
diff --git a/seahub/drafts/models.py b/seahub/drafts/models.py
index c3705efbfe..43e53c0c01 100644
--- a/seahub/drafts/models.py
+++ b/seahub/drafts/models.py
@@ -10,7 +10,7 @@ from seahub.base.fields import LowerCaseCharField
from seahub.base.models import TimestampedModel
from seahub.base.templatetags.seahub_tags import email2nickname
from seahub.tags.models import FileUUIDMap
-from seahub.utils import normalize_file_path
+from seahub.utils import normalize_file_path, EMPTY_SHA1
from seahub.utils.timeutils import datetime_to_isoformat_timestr
from .utils import create_user_draft_repo, get_draft_file_name
@@ -24,24 +24,8 @@ class DraftFileConflict(Exception):
class DraftManager(models.Manager):
- def get_user_draft_repo_id(self, username):
- r = self.filter(username=username).first()
- if r is None:
- return None
- else:
- return r.draft_repo_id
-
- def add(self, username, repo, file_path, file_id=None, org_id=-1):
- file_path = normalize_file_path(file_path)
- parent_path = os.path.dirname(file_path)
- filename = os.path.basename(file_path)
- file_uuid = FileUUIDMap.objects.get_or_create_fileuuidmap(
- repo.id, parent_path, filename, is_dir=False)
-
- if file_id is None:
- file_id = seafile_api.get_file_id_by_path(repo.id, file_path)
-
- # create drafts dir if any
+ def create_exist_file_draft(self, repo, username, file_uuid, file_path):
+ # create drafts dir if any
draft_dir_id = seafile_api.get_dir_id_by_path(repo.id, '/Drafts')
if draft_dir_id is None:
seafile_api.post_dir(repo.id, '/', 'Drafts', username)
@@ -56,10 +40,25 @@ class DraftManager(models.Manager):
repo.id, '/Drafts', draft_file_name,
username=username, need_progress=0, synchronous=1)
+ return draft_file_path
+
+ def add(self, username, repo, file_path, file_exist=True, file_id=None, org_id=-1):
+ file_path = normalize_file_path(file_path)
+ parent_path = os.path.dirname(file_path)
+ filename = os.path.basename(file_path)
+ file_uuid = FileUUIDMap.objects.get_or_create_fileuuidmap(
+ repo.id, parent_path, filename, is_dir=False)
+
+ if file_id is None:
+ file_id = seafile_api.get_file_id_by_path(repo.id, file_path)
+
+ if file_exist:
+ file_path = self.create_exist_file_draft(repo, username, file_uuid, file_path)
+
draft = self.model(username=username,
origin_repo_id=repo.id, origin_file_uuid=file_uuid,
origin_file_version=file_id,
- draft_file_path=draft_file_path)
+ draft_file_path=file_path)
draft.save(using=self._db)
return draft
@@ -80,7 +79,8 @@ class Draft(TimestampedModel):
def delete(self):
draft_file_name = os.path.basename(self.draft_file_path)
- seafile_api.del_file(self.origin_repo_id, '/Drafts/',
+ draft_file_path = os.path.dirname(self.draft_file_path)
+ seafile_api.del_file(self.origin_repo_id, draft_file_path,
draft_file_name, self.username)
super(Draft, self).delete()
@@ -101,22 +101,30 @@ class Draft(TimestampedModel):
if not file_id:
raise DraftFileConflict
- if file_id != self.origin_file_version:
+ draft_file_name = os.path.basename(self.draft_file_path)
+ draft_file_path = os.path.dirname(self.draft_file_path)
+
+ file_name = self.origin_file_uuid.filename
+
+ if file_id != self.origin_file_version and self.draft_file_path != origin_file_path:
raise DraftFileConflict
- draft_file_name = os.path.basename(self.draft_file_path)
+ if self.draft_file_path == origin_file_path:
+ f = os.path.splitext(draft_file_name)[0][:-7]
+ file_type = os.path.splitext(draft_file_name)[-1]
+ file_name = f + file_type
+
# move draft file to origin file
seafile_api.move_file(
- self.origin_repo_id, '/Drafts', draft_file_name,
+ self.origin_repo_id, draft_file_path, draft_file_name,
self.origin_repo_id, self.origin_file_uuid.parent_path,
- self.origin_file_uuid.filename, replace=1,
+ file_name, replace=1,
username=self.username, need_progress=0, synchronous=1
)
-
def to_dict(self):
uuid = self.origin_file_uuid
- file_path = posixpath.join(uuid.parent_path, uuid.filename) # TODO: refactor uuid
+ file_path = posixpath.join(uuid.parent_path, uuid.filename)
repo = seafile_api.get_repo(self.origin_repo_id)