mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-06 01:12:03 +00:00
New draft file (#2465)
This commit is contained in:
@@ -16,6 +16,7 @@ class CreateFile extends React.Component {
|
|||||||
this.state = {
|
this.state = {
|
||||||
parentPath: '',
|
parentPath: '',
|
||||||
childName: props.fileType,
|
childName: props.fileType,
|
||||||
|
isDraft: false,
|
||||||
};
|
};
|
||||||
this.newInput = React.createRef();
|
this.newInput = React.createRef();
|
||||||
}
|
}
|
||||||
@@ -28,7 +29,8 @@ class CreateFile extends React.Component {
|
|||||||
|
|
||||||
handleSubmit = () => {
|
handleSubmit = () => {
|
||||||
let path = this.state.parentPath + this.state.childName;
|
let path = this.state.parentPath + this.state.childName;
|
||||||
this.props.onAddFile(path);
|
let isDraft = this.state.isDraft;
|
||||||
|
this.props.onAddFile(path, isDraft);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleKeyPress = (e) => {
|
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 = () => {
|
toggle = () => {
|
||||||
this.props.addFileCancel();
|
this.props.addFileCancel();
|
||||||
}
|
}
|
||||||
@@ -67,6 +118,13 @@ class CreateFile extends React.Component {
|
|||||||
<Input onKeyPress={this.handleKeyPress} innerRef={input => {this.newInput = input;}} id="fileName" placeholder={gettext('newName')} value={this.state.childName} onChange={this.handleChange}/>
|
<Input onKeyPress={this.handleKeyPress} innerRef={input => {this.newInput = input;}} id="fileName" placeholder={gettext('newName')} value={this.state.childName} onChange={this.handleChange}/>
|
||||||
</Col>
|
</Col>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
<FormGroup row>
|
||||||
|
<Label sm={3} check />
|
||||||
|
<Col sm={9}>
|
||||||
|
<Input type="checkbox" onChange={this.handleCheck}/>{' '}{gettext("This is a draft.")}
|
||||||
|
</Col>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
</Form>
|
</Form>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
|
@@ -64,7 +64,8 @@ class DraftContent extends React.Component {
|
|||||||
let draft = this.state.currentDraft;
|
let draft = this.state.currentDraft;
|
||||||
|
|
||||||
editUtilties.createDraftReview(draft.id).then(res => {
|
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) => {
|
}).catch((error) => {
|
||||||
if (error.response.status == '409') {
|
if (error.response.status == '409') {
|
||||||
Toast.error('The draft review is existing.');
|
Toast.error('The draft review is existing.');
|
||||||
|
@@ -158,9 +158,9 @@ class MainPanel extends Component {
|
|||||||
this.setState({showFileDialog: !this.state.showFileDialog});
|
this.setState({showFileDialog: !this.state.showFileDialog});
|
||||||
}
|
}
|
||||||
|
|
||||||
onMainAddFile = (filePath) => {
|
onMainAddFile = (filePath, isDraft) => {
|
||||||
this.setState({showFileDialog: !this.state.showFileDialog});
|
this.setState({showFileDialog: !this.state.showFileDialog});
|
||||||
this.props.onMainAddFile(filePath);
|
this.props.onMainAddFile(filePath, isDraft);
|
||||||
}
|
}
|
||||||
|
|
||||||
onMainAddFolder = (dirPath) => {
|
onMainAddFolder = (dirPath) => {
|
||||||
|
@@ -127,9 +127,9 @@ class SidePanel extends Component {
|
|||||||
this.props.onAddFolderNode(dirPath);
|
this.props.onAddFolderNode(dirPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
onAddFileNode = (filePath) => {
|
onAddFileNode = (filePath, isDraft) => {
|
||||||
this.setState({showFile: !this.state.showFile});
|
this.setState({showFile: !this.state.showFile});
|
||||||
this.props.onAddFileNode(filePath);
|
this.props.onAddFileNode(filePath, isDraft);
|
||||||
}
|
}
|
||||||
|
|
||||||
onRenameNode = (newName) => {
|
onRenameNode = (newName) => {
|
||||||
|
@@ -252,8 +252,8 @@ class Wiki extends Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onAddFileNode = (filePath) => {
|
onAddFileNode = (filePath, isDraft) => {
|
||||||
editorUtilities.createFile(filePath).then(res => {
|
editorUtilities.createFile(filePath, isDraft).then(res => {
|
||||||
let tree = this.state.tree_data.clone();
|
let tree = this.state.tree_data.clone();
|
||||||
let name = this.getFileNameByPath(filePath);
|
let name = this.getFileNameByPath(filePath);
|
||||||
let index = filePath.lastIndexOf('/');
|
let index = filePath.lastIndexOf('/');
|
||||||
|
@@ -38,8 +38,8 @@ class EditorUtilities {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
createFile(filePath) {
|
createFile(filePath, isDraft) {
|
||||||
return seafileAPI.createFile(repoID, filePath);
|
return seafileAPI.createFile(repoID, filePath, isDraft);
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteFile(filePath) {
|
deleteFile(filePath) {
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
# Copyright (c) 2012-2016 Seafile Ltd.
|
# Copyright (c) 2012-2016 Seafile Ltd.
|
||||||
|
import os
|
||||||
|
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.authentication import SessionAuthentication
|
from rest_framework.authentication import SessionAuthentication
|
||||||
from rest_framework.permissions import IsAuthenticated
|
from rest_framework.permissions import IsAuthenticated
|
||||||
@@ -89,7 +91,33 @@ class DraftReviewView(APIView):
|
|||||||
return api_error(status.HTTP_409_CONFLICT,
|
return api_error(status.HTTP_409_CONFLICT,
|
||||||
'There is a conflict between the draft and the original file')
|
'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.publish_file_version = file_id
|
||||||
r.save()
|
r.save()
|
||||||
d.delete()
|
d.delete()
|
||||||
|
@@ -56,17 +56,17 @@ class DraftsView(APIView):
|
|||||||
error_msg = 'Library %s not found.' % repo_id
|
error_msg = 'Library %s not found.' % repo_id
|
||||||
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
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
|
||||||
perm = check_folder_permission(request, repo.id, file_path)
|
perm = check_folder_permission(request, repo.id, file_path)
|
||||||
if perm != PERMISSION_READ_WRITE:
|
if perm != PERMISSION_READ_WRITE:
|
||||||
error_msg = 'Permission denied.'
|
error_msg = 'Permission denied.'
|
||||||
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
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
|
username = request.user.username
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@@ -58,6 +58,7 @@ from seahub.thumbnail.utils import generate_thumbnail
|
|||||||
from seahub.notifications.models import UserNotification
|
from seahub.notifications.models import UserNotification
|
||||||
from seahub.options.models import UserOptions
|
from seahub.options.models import UserOptions
|
||||||
from seahub.profile.models import Profile, DetailedProfile
|
from seahub.profile.models import Profile, DetailedProfile
|
||||||
|
from seahub.drafts.models import Draft
|
||||||
from seahub.signals import (repo_created, repo_deleted)
|
from seahub.signals import (repo_created, repo_deleted)
|
||||||
from seahub.share.models import FileShare, OrgFileShare, UploadLinkShare
|
from seahub.share.models import FileShare, OrgFileShare, UploadLinkShare
|
||||||
from seahub.utils import gen_file_get_url, gen_token, gen_file_upload_url, \
|
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
|
username = request.user.username
|
||||||
parent_dir = os.path.dirname(path)
|
parent_dir = os.path.dirname(path)
|
||||||
operation = request.POST.get('operation', '')
|
operation = request.POST.get('operation', '')
|
||||||
|
is_draft = request.POST.get('is_draft', '')
|
||||||
|
|
||||||
file_info = {}
|
file_info = {}
|
||||||
if operation.lower() == 'rename':
|
if operation.lower() == 'rename':
|
||||||
@@ -2772,6 +2774,17 @@ class FileView(APIView):
|
|||||||
return api_error(status.HTTP_403_FORBIDDEN,
|
return api_error(status.HTTP_403_FORBIDDEN,
|
||||||
'You do not have permission to create file.')
|
'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)
|
new_file_name = os.path.basename(path)
|
||||||
|
|
||||||
if not seafile_api.is_valid_filename('fake_repo_id', new_file_name):
|
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,
|
return api_error(HTTP_520_OPERATION_FAILED,
|
||||||
'Failed to create file.')
|
'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':
|
if request.GET.get('reloaddir', '').lower() == 'true':
|
||||||
return reloaddir(request, repo, parent_dir)
|
return reloaddir(request, repo, parent_dir)
|
||||||
else:
|
else:
|
||||||
|
@@ -10,7 +10,7 @@ from seahub.base.fields import LowerCaseCharField
|
|||||||
from seahub.base.models import TimestampedModel
|
from seahub.base.models import TimestampedModel
|
||||||
from seahub.base.templatetags.seahub_tags import email2nickname
|
from seahub.base.templatetags.seahub_tags import email2nickname
|
||||||
from seahub.tags.models import FileUUIDMap
|
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 seahub.utils.timeutils import datetime_to_isoformat_timestr
|
||||||
from .utils import create_user_draft_repo, get_draft_file_name
|
from .utils import create_user_draft_repo, get_draft_file_name
|
||||||
|
|
||||||
@@ -24,24 +24,8 @@ class DraftFileConflict(Exception):
|
|||||||
|
|
||||||
|
|
||||||
class DraftManager(models.Manager):
|
class DraftManager(models.Manager):
|
||||||
def get_user_draft_repo_id(self, username):
|
def create_exist_file_draft(self, repo, username, file_uuid, file_path):
|
||||||
r = self.filter(username=username).first()
|
# create drafts dir if any
|
||||||
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
|
|
||||||
draft_dir_id = seafile_api.get_dir_id_by_path(repo.id, '/Drafts')
|
draft_dir_id = seafile_api.get_dir_id_by_path(repo.id, '/Drafts')
|
||||||
if draft_dir_id is None:
|
if draft_dir_id is None:
|
||||||
seafile_api.post_dir(repo.id, '/', 'Drafts', username)
|
seafile_api.post_dir(repo.id, '/', 'Drafts', username)
|
||||||
@@ -56,10 +40,25 @@ class DraftManager(models.Manager):
|
|||||||
repo.id, '/Drafts', draft_file_name,
|
repo.id, '/Drafts', draft_file_name,
|
||||||
username=username, need_progress=0, synchronous=1)
|
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,
|
draft = self.model(username=username,
|
||||||
origin_repo_id=repo.id, origin_file_uuid=file_uuid,
|
origin_repo_id=repo.id, origin_file_uuid=file_uuid,
|
||||||
origin_file_version=file_id,
|
origin_file_version=file_id,
|
||||||
draft_file_path=draft_file_path)
|
draft_file_path=file_path)
|
||||||
draft.save(using=self._db)
|
draft.save(using=self._db)
|
||||||
return draft
|
return draft
|
||||||
|
|
||||||
@@ -80,7 +79,8 @@ class Draft(TimestampedModel):
|
|||||||
|
|
||||||
def delete(self):
|
def delete(self):
|
||||||
draft_file_name = os.path.basename(self.draft_file_path)
|
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)
|
draft_file_name, self.username)
|
||||||
|
|
||||||
super(Draft, self).delete()
|
super(Draft, self).delete()
|
||||||
@@ -101,22 +101,30 @@ class Draft(TimestampedModel):
|
|||||||
if not file_id:
|
if not file_id:
|
||||||
raise DraftFileConflict
|
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
|
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
|
# move draft file to origin file
|
||||||
seafile_api.move_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_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
|
username=self.username, need_progress=0, synchronous=1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
uuid = self.origin_file_uuid
|
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)
|
repo = seafile_api.get_repo(self.origin_repo_id)
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user