1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-05-10 00:47:19 +00:00

Improve code of draft ()

This commit is contained in:
C_Q 2018-12-28 23:22:49 +08:00 committed by Daniel Pan
parent 3b1caf2140
commit b196b1cf0a
9 changed files with 579 additions and 158 deletions

View File

@ -5,8 +5,8 @@ import PropTypes from 'prop-types';
import Prism from 'prismjs';
/* eslint-enable */
import { siteRoot, gettext, reviewID, draftOriginFilePath, draftFilePath, draftOriginRepoID,
draftFileName, opStatus, publishFileVersion, originFileVersion, author, authorAvatar
} from './utils/constants';
draftFileName, opStatus, publishFileVersion, originFileVersion, author, authorAvatar,
draftFileExists, originFileExists } from './utils/constants';
import { seafileAPI } from './utils/seafile-api';
import axios from 'axios';
import DiffViewer from '@seafile/seafile-editor/dist/viewer/diff-viewer';
@ -71,36 +71,77 @@ class DraftReview extends React.Component {
}
initialContent = () => {
if (publishFileVersion == 'None') {
axios.all([
seafileAPI.getFileDownloadLink(draftOriginRepoID, draftFilePath),
seafileAPI.getFileDownloadLink(draftOriginRepoID, draftOriginFilePath)
]).then(axios.spread((res1, res2) => {
switch(this.state.reviewStatus) {
case 'closed':
this.setState({
isLoading: false,
isShowDiff: false
})
break;
case "open":
if (!draftFileExists) {
this.setState({
isLoading: false,
isShowDiff: false
})
return;
}
if (!originFileExists) {
seafileAPI.getFileDownloadLink(draftOriginRepoID, draftFilePath)
.then(res => {
seafileAPI.getFileContent(res.data)
.then(res => {
this.setState({
draftContent: res.data,
draftOriginContent: res.data,
isLoading: false,
isShowDiff: false
});
})
})
return;
}
axios.all([
seafileAPI.getFileContent(res1.data),
seafileAPI.getFileContent(res2.data)
seafileAPI.getFileDownloadLink(draftOriginRepoID, draftFilePath),
seafileAPI.getFileDownloadLink(draftOriginRepoID, draftOriginFilePath)
]).then(axios.spread((res1, res2) => {
axios.all([
seafileAPI.getFileContent(res1.data),
seafileAPI.getFileContent(res2.data)
]).then(axios.spread((draftContent, draftOriginContent) => {
this.setState({
draftContent: draftContent.data,
draftOriginContent: draftOriginContent.data,
isLoading: false
});
}));
}));
break;
case "finished":
if (!originFileExists) {
this.setState({
isLoading: false,
isShowDiff: false
})
return;
}
let dl0 = siteRoot + 'repo/' + draftOriginRepoID + '/' + publishFileVersion + '/download?' + 'p=' + draftOriginFilePath;
let dl = siteRoot + 'repo/' + draftOriginRepoID + '/' + originFileVersion + '/download?' + 'p=' + draftOriginFilePath;
axios.all([
seafileAPI.getFileContent(dl0),
seafileAPI.getFileContent(dl)
]).then(axios.spread((draftContent, draftOriginContent) => {
this.setState({
draftContent: draftContent.data,
draftOriginContent: draftOriginContent.data,
isLoading: false
isLoading: false,
});
}));
}));
} else {
let dl0 = siteRoot + 'repo/' + draftOriginRepoID + '/' + publishFileVersion + '/download?' + 'p=' + draftOriginFilePath;
let dl = siteRoot + 'repo/' + draftOriginRepoID + '/' + originFileVersion + '/download?' + 'p=' + draftOriginFilePath;
axios.all([
seafileAPI.getFileContent(dl0),
seafileAPI.getFileContent(dl)
]).then(axios.spread((draftContent, draftOriginContent) => {
this.setState({
draftContent: draftContent.data,
draftOriginContent: draftOriginContent.data,
isLoading: false,
});
}));
}
break;
}
}
componentWillUnmount() {
@ -505,6 +546,182 @@ class DraftReview extends React.Component {
this.toggleCommentDialog();
}
showDiffViewer = () => {
return (
<div>
{this.state.isShowDiff ?
<DiffViewer
newMarkdownContent={this.state.draftContent}
oldMarkdownContent={this.state.draftOriginContent}
ref="diffViewer"
/>
:
<DiffViewer
newMarkdownContent={this.state.draftContent}
oldMarkdownContent={this.state.draftContent}
ref="diffViewer"
/>
}
<i className="fa fa-plus-square review-comment-btn" ref="commentbtn" onMouseDown={this.addComment}></i>
</div>
)
}
renderContent = () => {
switch(this.state.reviewStatus) {
case "closed":
return <p className="error">{gettext('The review has been closed.')}</p>;
case "open":
if (!draftFileExists) {
return <p className="error">{gettext('Draft has been deleted.')}</p>;
}
return this.showDiffViewer();
case "finished":
if (!originFileExists) {
return <p className="error">{gettext('Original file has been deleted.')}</p>
}
return this.showDiffViewer();
}
}
showDiffButton = () => {
return (
<div className={'seafile-toggle-diff'}>
<label className="custom-switch" id="toggle-diff">
<input type="checkbox" checked={this.state.isShowDiff && 'checked'}
name="option" className="custom-switch-input"
onChange={this.onSwitchShowDiff}/>
<span className="custom-switch-indicator"></span>
</label>
<Tooltip placement="bottom" isOpen={this.state.showDiffTip}
target="toggle-diff" toggle={this.toggleDiffTip}>
{gettext('View diff')}</Tooltip>
</div>
)
}
renderDiffButton = () => {
switch(this.state.reviewStatus) {
case "closed":
return;
case "open":
if (!draftFileExists) {
return;
}
if (!originFileExists) {
return;
}
return this.showDiffButton();
case "finished":
if (!originFileExists) {
return;
}
return this.showDiffButton();
}
}
renderGo = (OriginFileLink, draftLink) => {
let viewFile = <a href={OriginFileLink} className="view-file-link">{gettext('View File')}</a>;
let editDraft = <a href={draftLink} className="draft-link">{gettext('Edit draft')}</a>;
switch(this.state.reviewStatus) {
case "closed":
return viewFile;
case "open":
if (!draftFileExists) {
return viewFile;
}
return editDraft;
case "finished":
if (!originFileExists) {
return;
}
return viewFile;
}
}
showNavItem = (showTab) => {
switch(showTab) {
case "info":
return (
<NavItem className="nav-item">
<NavLink
className={classnames({ active: this.state.activeTab === 'reviewInfo' })}
onClick={() => { this.tabItemClick('reviewInfo');}}
>
<i className="fas fa-info-circle"></i>
</NavLink>
</NavItem>
);
case "comments":
return (
<NavItem className="nav-item">
<NavLink
className={classnames({ active: this.state.activeTab === 'comments' })}
onClick={() => {this.tabItemClick('comments');}}
>
<i className="fa fa-comments"></i>
{ this.state.commentsNumber > 0 &&
<div className='comments-number'>{this.state.commentsNumber}</div>}
</NavLink>
</NavItem>
);
case "history":
return (
<NavItem className="nav-item">
<NavLink
className={classnames({ active: this.state.activeTab === 'history' })}
onClick={() => { this.tabItemClick('history');}}
>
<i className="fas fa-history"></i>
</NavLink>
</NavItem>
);
}
}
renderNavItems = () => {
switch(this.state.reviewStatus) {
case "closed":
return (
<Nav tabs className="review-side-panel-nav">
{this.showNavItem("info")}
</Nav>
);
case "open":
if (!draftFileExists) {
return (
<Nav tabs className="review-side-panel-nav">
{this.showNavItem("info")}
</Nav>
);
}
return (
<Nav tabs className="review-side-panel-nav">
{this.showNavItem('info')}
{this.showNavItem('comments')}
{this.showNavItem('history')}
</Nav>
);
case "finished":
if (!originFileExists) {
return (
<Nav tabs className="review-side-panel-nav">
{this.showNavItem("info")}
</Nav>
);
}
return (
<Nav tabs className="review-side-panel-nav">
{this.showNavItem('info')}
{this.showNavItem('comments')}
</Nav>
)
}
}
render() {
const onResizeMove = this.state.inResizing ? this.onResizeMouseMove : null;
const draftLink = siteRoot + 'lib/' + draftOriginRepoID + '/file' + draftFilePath + '?mode=edit';
@ -520,30 +737,20 @@ class DraftReview extends React.Component {
<React.Fragment>
<span className="file-name">{draftFileName}</span>
<span className="file-copywriting">{gettext('review')}</span>
{ opStatus == 'open' && <a href={draftLink} className="draft-link">{gettext('Edit draft')}</a>}
{ opStatus !== 'open' && <a href={OriginFileLink} className="view-file-link">{gettext('View File')}</a>}
{this.renderGo(OriginFileLink, draftLink)}
</React.Fragment>
</div>
</div>
<div className="button-group">
<div className={'seafile-toggle-diff'}>
<label className="custom-switch" id="toggle-diff">
<input type="checkbox" checked={this.state.isShowDiff && 'checked'}
name="option" className="custom-switch-input"
onChange={this.onSwitchShowDiff}/>
<span className="custom-switch-indicator"></span>
</label>
<Tooltip placement="bottom" isOpen={this.state.showDiffTip}
target="toggle-diff" toggle={this.toggleDiffTip}>
{gettext('View diff')}</Tooltip>
</div>
{this.renderDiffButton()}
{
this.state.reviewStatus === 'open' &&
<div className="cur-file-operation">
<button className='btn btn-secondary file-operation-btn' title={gettext('Close review')}
onClick={this.onCloseReview}>{gettext('Close')}</button>
<button className='btn btn-success file-operation-btn' title={gettext('Publish draft')}
{ draftFileExists && <button className='btn btn-success file-operation-btn' title={gettext('Publish draft')}
onClick={this.onPublishReview}>{gettext('Publish')}</button>
}
</div>
}
{
@ -567,56 +774,14 @@ class DraftReview extends React.Component {
</div>
:
<div className="markdown-viewer-render-content article" ref="mainPanel">
{this.state.isShowDiff ?
<DiffViewer
newMarkdownContent={this.state.draftContent}
oldMarkdownContent={this.state.draftOriginContent}
ref="diffViewer"
/>
:
<DiffViewer
newMarkdownContent={this.state.draftContent}
oldMarkdownContent={this.state.draftContent}
ref="diffViewer"
/>
}
<i className="fa fa-plus-square review-comment-btn" ref="commentbtn" onMouseDown={this.addComment}></i>
{this.renderContent()}
</div>
}
</div>
<div className="cur-view-right-part" style={{width:(this.state.commentWidth)+'%'}}>
<div className="seafile-comment-resize" onMouseDown={this.onResizeMouseDown}></div>
<div className="review-side-panel">
<Nav tabs className="review-side-panel-nav">
<NavItem className="nav-item">
<NavLink
className={classnames({ active: this.state.activeTab === 'reviewInfo' })}
onClick={() => { this.tabItemClick('reviewInfo');}}
>
<i className="fas fa-info-circle"></i>
</NavLink>
</NavItem>
<NavItem className="nav-item">
<NavLink
className={classnames({ active: this.state.activeTab === 'comments' })}
onClick={() => { this.tabItemClick('comments');}}
>
<i className="fa fa-comments"></i>
{ this.state.commentsNumber > 0 &&
<div className='comments-number'>{this.state.commentsNumber}</div>}
</NavLink>
</NavItem>
{ this.state.reviewStatus == 'finished' ? '':
<NavItem className="nav-item">
<NavLink
className={classnames({ active: this.state.activeTab === 'history' })}
onClick={() => { this.tabItemClick('history');}}
>
<i className="fas fa-history"></i>
</NavLink>
</NavItem>
}
</Nav>
{this.renderNavItems()}
<TabContent activeTab={this.state.activeTab}>
<TabPane tabId="reviewInfo">
<div className="review-side-panel-body">

View File

@ -64,3 +64,5 @@ export const publishFileVersion = window.draftReview ? window.draftReview.config
export const originFileVersion = window.draftReview ? window.draftReview.config.originFileVersion : '';
export const author = window.draftReview ? window.draftReview.config.author : '';
export const authorAvatar = window.draftReview ? window.draftReview.config.authorAvatar : '';
export const originFileExists = window.draftReview ? window.draftReview.config.originFileExists : '';
export const draftFileExists = window.draftReview ? window.draftReview.config.draftFileExists : '';

View File

@ -18,7 +18,7 @@ from seahub.constants import PERMISSION_READ_WRITE
from seahub.views import check_folder_permission
from seahub.drafts.models import Draft, DraftReview, DraftReviewExist, \
DraftFileConflict, ReviewReviewer
DraftFileConflict, ReviewReviewer, OriginalFileConflict
from seahub.drafts.signals import update_review_successful
@ -62,21 +62,21 @@ class DraftReviewsView(APIView):
return api_error(status.HTTP_404_NOT_FOUND,
'Draft %s not found.' % draft_id)
origin_repo_id = d.origin_repo_id
file_path = d.draft_file_path
draft_file = seafile_api.get_file_id_by_path(origin_repo_id, file_path)
if not draft_file:
error_msg = 'Draft file not found.'
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
# perm check
perm = check_folder_permission(request, d.origin_repo_id, '/')
perm = check_folder_permission(request, origin_repo_id, '/')
if perm is None:
return api_error(status.HTTP_403_FORBIDDEN,
'Permission denied.')
username = request.user.username
try:
d_r = DraftReview.objects.get(creator=username, draft_id=d)
if d_r.status == 'closed':
d_r.delete()
except DraftReview.DoesNotExist:
pass
try:
d_r = DraftReview.objects.add(creator=username, draft=d)
except (DraftReviewExist):
@ -129,7 +129,14 @@ class DraftReviewView(APIView):
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
r.publish(operator=username)
try:
r.publish(operator=username)
except DraftFileConflict:
error_msg = 'There is a conflict between the draft and the original file.'
return api_error(status.HTTP_409_CONFLICT, error_msg)
except OriginalFileConflict:
error_msg = 'Original file not found.'
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
reviewers = ReviewReviewer.objects.filter(review_id=r)
# send notice to other reviewers if has

View File

@ -40,13 +40,14 @@ class DraftsView(APIView):
"""List all user drafts.
"""
username = request.user.username
data = [x.to_dict() for x in Draft.objects.filter(username=username)]
data = Draft.objects.list_draft_by_username(username)
draft_counts = len(data)
result = {}
result['data'] = data
result['draft_counts'] = draft_counts
return Response(result)
@add_org_context
@ -107,8 +108,7 @@ class DraftView(APIView):
# perm check
repo_id = d.origin_repo_id
uuid = d.origin_file_uuid
file_path = posixpath.join(uuid.parent_path, uuid.filename)
perm = check_folder_permission(request, repo_id, file_path)
perm = check_folder_permission(request, repo_id, uuid.parent_path)
if perm != PERMISSION_READ_WRITE:
error_msg = 'Permission denied.'

View File

@ -23,7 +23,67 @@ class DraftFileConflict(Exception):
pass
class OriginalFileConflict(Exception):
pass
class DraftManager(models.Manager):
def list_draft_by_username(self, username, with_reviews=True):
"""list all user drafts
If with_reviews is true, return the draft associated review
"""
repo_cache = {}
def get_repo_with_cache(repo_id, repo_cache):
"""return repo object
Avoid loading the same repo multiple times
"""
if repo_id in repo_cache:
return repo_cache[repo_id]
repo = seafile_api.get_repo(repo_id)
repo_cache[repo_id] = repo
return repo
data = []
qs = self.filter(username=username)
if with_reviews:
qs_r = list(DraftReview.objects.filter(draft_id__in=qs))
for d in qs:
# If repo does not exist, no related items are displayed.
repo = get_repo_with_cache(d.origin_repo_id, repo_cache)
if not repo:
continue
uuid = d.origin_file_uuid
file_path = posixpath.join(uuid.parent_path, uuid.filename)
draft = {}
# Query whether there is an associated review
if with_reviews:
draft['review_id'] = None
draft['review_status'] = None
for r in qs_r:
if r.draft_id == d:
draft['review_id'] = r.id
draft['review_status'] = r.status
draft['id'] = d.id
draft['owner'] = d.username
draft['repo_name'] = repo.name
draft['owner_nickname'] = email2nickname(d.username)
draft['origin_repo_id'] = d.origin_repo_id
draft['origin_file_path'] = file_path
draft['origin_file_version'] = d.origin_file_version
draft['draft_file_path'] = d.draft_file_path
draft['created_at'] = datetime_to_isoformat_timestr(d.created_at)
draft['updated_at'] = datetime_to_isoformat_timestr(d.updated_at)
data.append(draft)
return data
def create_exist_file_draft(self, repo, username, file_uuid, file_path):
# create drafts dir if does not exist
draft_dir_id = seafile_api.get_dir_id_by_path(repo.id, '/Drafts')
@ -36,16 +96,41 @@ class DraftManager(models.Manager):
draft_file_path = '/Drafts/' + draft_file_name
try:
# Determine if there is a draft of the file
d = self.get(origin_file_uuid=file_uuid)
except Draft.DoesNotExist:
try:
# Determine if there is a draft with the same name as
# the generated draft file path
d_2 = self.get(origin_repo_id=repo.id, draft_file_path=draft_file_path)
d_2.delete(operator=username)
except Draft.DoesNotExist:
pass
# copy file to draft dir
seafile_api.copy_file(repo.id, file_uuid.parent_path, file_uuid.filename,
repo.id, '/Drafts', draft_file_name,
username=username, need_progress=0, synchronous=1)
return draft_file_path
if d:
raise DraftFileExist
file_id = seafile_api.get_file_id_by_path(repo.id, d.draft_file_path)
# If the database entry exists and the draft file exists,
# then raise DraftFileExist
if file_id:
raise DraftFileExist
# If the database entry exists and the draft file does not exist,
# delete the database entry
else:
d.delete(operator=username)
# copy file to draft dir
seafile_api.copy_file(repo.id, file_uuid.parent_path, file_uuid.filename,
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)
@ -90,7 +175,7 @@ class Draft(TimestampedModel):
draft_file_name, operator)
if hasattr(self, 'draftreview'):
if self.draftreview.status == 'closed':
if self.draftreview.status != 'finished':
self.draftreview.delete()
super(Draft, self).delete()
@ -108,48 +193,44 @@ class Draft(TimestampedModel):
file_id = seafile_api.get_file_id_by_path(self.origin_repo_id,
origin_file_path)
if not file_id:
raise DraftFileConflict
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
if file_id:
if file_id != self.origin_file_version and self.draft_file_path != origin_file_path:
raise DraftFileConflict
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
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, draft_file_path, draft_file_name,
self.origin_repo_id, self.origin_file_uuid.parent_path,
file_name, replace=1,
username=operator, need_progress=0, synchronous=1
)
# move draft file to origin file
seafile_api.move_file(
self.origin_repo_id, draft_file_path, draft_file_name,
self.origin_repo_id, self.origin_file_uuid.parent_path,
file_name, replace=1,
username=operator, need_progress=0, synchronous=1
)
else:
# move draft file to origin file
seafile_api.move_file(
self.origin_repo_id, draft_file_path, draft_file_name,
self.origin_repo_id, self.origin_file_uuid.parent_path,
file_name, replace=1,
username=operator, need_progress=0, synchronous=1
)
def to_dict(self):
uuid = self.origin_file_uuid
file_path = posixpath.join(uuid.parent_path, uuid.filename)
repo = seafile_api.get_repo(self.origin_repo_id)
review_id = None
review_status = None
if hasattr(self, 'draftreview'):
review_id = self.draftreview.id
review_status = self.draftreview.status
return {
'id': self.pk,
'review_id': review_id,
'review_status': review_status,
'owner': self.username,
'repo_name': repo.name,
'owner_nickname': email2nickname(self.username),
'origin_repo_id': self.origin_repo_id,
'origin_file_path': file_path,
@ -166,10 +247,14 @@ class DraftReviewExist(Exception):
class DraftReviewManager(models.Manager):
def add(self, creator, draft):
has_review = hasattr(draft, 'draftreview')
if has_review:
raise DraftReviewExist
try:
d_r = self.get(creator=creator, draft_id=draft)
if d_r.status == 'closed':
d_r.delete()
if d_r.status == 'open':
raise DraftReviewExist
except DraftReview.DoesNotExist:
pass
draft_review = self.model(creator=creator,
author=draft.username,
@ -183,33 +268,90 @@ class DraftReviewManager(models.Manager):
return draft_review
def get_reviews_by_creator_and_status(self, creator, status):
def get_reviews_by_creator_and_status(self, creator, status, with_reviewers=True):
"""
List all reviews as creator according to a certain status
"""
repo_cache = {}
def get_repo_with_cache(repo_id, repo_cache):
"""return repo object
Avoid loading the same repo multiple times
"""
if repo_id in repo_cache:
return repo_cache[repo_id]
repo = seafile_api.get_repo(repo_id)
repo_cache[repo_id] = repo
return repo
from seahub.api2.utils import user_to_dict
reviews = self.filter(creator=creator, status=status)
reviewers = ReviewReviewer.objects.filter(review_id__in=reviews)
if with_reviewers:
reviewers = ReviewReviewer.objects.filter(review_id__in=reviews)
data = []
for review in reviews:
reviewer_list = []
for r in reviewers:
if review.id == r.review_id_id:
reviewer = user_to_dict(r.reviewer, avatar_size=64)
reviewer_list.append(reviewer)
if with_reviewers:
reviewer_list = []
for r in reviewers:
if review.id == r.review_id_id:
reviewer = user_to_dict(r.reviewer, avatar_size=64)
reviewer_list.append(reviewer)
author = user_to_dict(review.creator, avatar_size=64)
review = review.to_dict()
review.update({'reviewers': reviewer_list})
review.update({'author': author})
data.append(review)
# If repo does not exist, no related items are displayed.
repo = get_repo_with_cache(review.origin_repo_id, repo_cache)
if not repo:
continue
review_obj = {}
review_obj['id'] = review.id
review_obj['creator'] = review.creator
review_obj['status'] = review.status
review_obj['creator_name'] = email2nickname(review.creator)
review_obj['draft_origin_repo_id'] = review.origin_repo_id
review_obj['draft_origin_repo_name'] = repo.name
review_obj['draft_origin_file_version'] = review.origin_file_version
review_obj['draft_publish_file_version'] = review.publish_file_version
review_obj['draft_file_path'] = review.draft_file_path
review_obj['created_at'] = datetime_to_isoformat_timestr(review.created_at)
review_obj['updated_at'] = datetime_to_isoformat_timestr(review.updated_at)
if review_obj and with_reviewers:
review_obj.update({'reviewers': reviewer_list})
if review_obj:
review_obj.update({'author': author})
data.append(review_obj)
return data
def get_reviews_by_reviewer_and_status(self, reviewer, status):
"""
List all reviews as reviewers according to a certain status
"""
repo_cache = {}
def get_repo_with_cache(repo_id, repo_cache):
"""return repo object
Avoid loading the same repo multiple times
"""
if repo_id in repo_cache:
return repo_cache[repo_id]
repo = seafile_api.get_repo(repo_id)
repo_cache[repo_id] = repo
return repo
from seahub.api2.utils import user_to_dict
reviews = self.filter(reviewreviewer__reviewer=reviewer, status=status)
reviewers = ReviewReviewer.objects.filter(review_id__in=reviews)
data = []
@ -222,10 +364,28 @@ class DraftReviewManager(models.Manager):
author = user_to_dict(review.creator, avatar_size=64)
review = review.to_dict()
review.update({'author': author})
review.update({'reviewers': reviewer_list})
data.append(review)
# If repo does not exist, no related items are displayed.
repo = get_repo_with_cache(review.origin_repo_id, repo_cache)
if not repo:
continue
review_obj = {}
review_obj['id'] = review.id
review_obj['creator'] = review.creator
review_obj['status'] = review.status
review_obj['creator_name'] = email2nickname(review.creator)
review_obj['draft_origin_repo_id'] = review.origin_repo_id
review_obj['draft_origin_repo_name'] = repo.name
review_obj['draft_origin_file_version'] = review.origin_file_version
review_obj['draft_publish_file_version'] = review.publish_file_version
review_obj['draft_file_path'] = review.draft_file_path
review_obj['created_at'] = datetime_to_isoformat_timestr(review.created_at)
review_obj['updated_at'] = datetime_to_isoformat_timestr(review.updated_at)
if review_obj:
review_obj.update({'reviewers': reviewer_list})
review_obj.update({'author': author})
data.append(review_obj)
return data
@ -244,17 +404,12 @@ class DraftReview(TimestampedModel):
objects = DraftReviewManager()
def to_dict(self):
r_repo = seafile_api.get_repo(self.origin_repo_id)
if not r_repo:
raise DraftFileConflict
return {
'id': self.pk,
'creator': self.creator,
'status': self.status,
'creator_name': email2nickname(self.creator),
'draft_origin_repo_id': self.origin_repo_id,
'draft_origin_repo_name': r_repo.name,
'draft_origin_file_version': self.origin_file_version,
'draft_publish_file_version': self.publish_file_version,
'draft_file_path': self.draft_file_path,
@ -298,6 +453,10 @@ class DraftReview(TimestampedModel):
# get draft published version
file_id = seafile_api.get_file_id_by_path(self.origin_repo_id, origin_file_path)
if not file_id:
raise OriginalFileConflict
self.publish_file_version = file_id
self.status = 'finished'
self.save()

View File

@ -54,8 +54,11 @@ def has_draft_file(repo_id, file_path):
from .models import Draft
if file_uuid:
try:
Draft.objects.get(origin_file_uuid=file_uuid)
has_draft = True
d = Draft.objects.get(origin_file_uuid=file_uuid)
file_id = seafile_api.get_file_id_by_path(repo_id, d.draft_file_path)
if file_id:
has_draft = True
except Draft.DoesNotExist:
pass

View File

@ -4,11 +4,12 @@ import posixpath
from django.shortcuts import render, get_object_or_404
from django.utils.translation import ugettext as _
from seaserv import seafile_api
from seahub.base.templatetags.seahub_tags import email2nickname
from seahub.auth.decorators import login_required
from seahub.views import check_folder_permission
from seahub.utils import render_permission_error
from seahub.utils import render_permission_error, render_error
from seahub.drafts.models import Draft, DraftReview
from seahub.api2.utils import user_to_dict
@ -29,15 +30,22 @@ def review(request, pk):
# check perm
uuid = d_r.origin_file_uuid
file_path = posixpath.join(uuid.parent_path, uuid.filename)
origin_repo_id = d_r.origin_repo_id
permission = check_folder_permission(request, origin_repo_id, '/')
if not permission:
return render_permission_error(request, _(u'Permission denied.'))
origin_file_path = posixpath.join(uuid.parent_path, uuid.filename)
origin_file = seafile_api.get_file_id_by_path(origin_repo_id, origin_file_path)
origin_file_exists = True
if not origin_file:
origin_file_exists = False
draft_file = seafile_api.get_file_id_by_path(origin_repo_id, d_r.draft_file_path)
draft_file_exists = True
if not draft_file:
draft_file_exists = False
draft_file_name = os.path.basename(d_r.draft_file_path)
author_info = user_to_dict(d_r.author, avatar_size=32)
@ -47,7 +55,7 @@ def review(request, pk):
"review_id": pk,
"draft_repo_id": d_r.origin_repo_id,
"draft_origin_repo_id": d_r.origin_repo_id,
"draft_origin_file_path": file_path,
"draft_origin_file_path": origin_file_path,
"draft_file_path": d_r.draft_file_path,
"draft_file_name": draft_file_name,
"origin_file_version": d_r.origin_file_version,
@ -55,5 +63,7 @@ def review(request, pk):
"status": d_r.status,
"permission": permission,
"author": author_info['user_name'],
'author_avatar_url': author_info['avatar_url']
"author_avatar_url": author_info['avatar_url'],
"origin_file_exists": origin_file_exists,
"draft_file_exists": draft_file_exists
})

View File

@ -18,6 +18,8 @@
originFileVersion: '{{ origin_file_version }}',
author: '{{ author }}',
authorAvatar: '{{ author_avatar_url }}',
originFileExists: '{{ origin_file_exists }}' === 'True',
draftFileExists: '{{ draft_file_exists }}' === 'True',
}
};
</script>

View File

@ -1,3 +1,4 @@
from django.core.urlresolvers import reverse
from seahub.drafts.models import Draft, DraftReview, ReviewReviewer, \
DraftFileExist
from seahub.test_utils import BaseTestCase
@ -6,6 +7,52 @@ from seaserv import seafile_api
class DraftManagerTest(BaseTestCase):
def test_list_draft_by_username(self):
assert len(Draft.objects.all()) == 0
Draft.objects.add(self.user.username, self.repo, self.file)
draft_list = Draft.objects.list_draft_by_username(self.user.username)
assert len(draft_list) == 1
def test_list_draft_by_username_with_invalid_repo(self):
self.login_as(self.user)
assert len(Draft.objects.all()) == 0
Draft.objects.add(self.user.username, self.repo, self.file)
url = reverse('api2-repo', args=[self.repo.id])
resp = self.client.delete(url, {}, 'application/x-www-form-urlencoded')
self.assertEqual(200, resp.status_code)
draft_list = Draft.objects.list_draft_by_username(self.user.username)
assert len(draft_list) == 0
assert len(Draft.objects.all()) == 1
def test_list_draft_by_username_with_invalid_origin_file(self):
self.login_as(self.user)
assert len(Draft.objects.all()) == 0
url = reverse('api-v2.1-drafts')
resp = self.client.post(url, {
'repo_id': self.repo.id,
'file_path': self.file,
})
self.assertEqual(200, resp.status_code)
file_url = reverse('api-v2.1-file-view', args=[self.repo.id])
d_resp = self.client.delete(file_url + '?p=' + self.file,
{}, 'application/x-www-form-urlencoded')
self.assertEqual(200, d_resp.status_code)
draft_list = Draft.objects.list_draft_by_username(self.user.username)
assert len(draft_list) == 1
assert len(Draft.objects.all()) == 1
def test_add(self):
assert len(Draft.objects.all()) == 0
draft = Draft.objects.add(self.user.username, self.repo, self.file)
@ -83,6 +130,32 @@ class DraftReviewManagerTest(BaseTestCase):
DraftReview.objects.add(self.user.username, self.draft)
assert(len(DraftReview.objects.get_reviews_by_creator_and_status(self.user.username, 'open')) == 1)
def test_get_reviews_by_creator_and_status_with_invalid_repo(self):
self.login_as(self.user)
assert(len(DraftReview.objects.get_reviews_by_creator_and_status(self.user.username, 'open')) == 0)
DraftReview.objects.add(self.user.username, self.draft)
resp = self.client.delete(
reverse('api2-repo', args=[self.repo.id])
)
self.assertEqual(200, resp.status_code)
assert(len(DraftReview.objects.all()) == 1)
assert(len(DraftReview.objects.get_reviews_by_creator_and_status(self.user.username, 'open')) == 0)
def test_get_reviews_by_creator_and_status_with_invalid_origin_file(self):
self.login_as(self.user)
assert(len(DraftReview.objects.get_reviews_by_creator_and_status(self.user.username, 'open')) == 0)
DraftReview.objects.add(self.user.username, self.draft)
url = reverse('api-v2.1-file-view', args=[self.repo.id])
d_resp = self.client.delete(url + '?p=' + self.file,
{}, 'application/x-www-form-urlencoded')
self.assertEqual(200, d_resp.status_code)
assert(len(DraftReview.objects.all()) == 1)
assert(len(DraftReview.objects.get_reviews_by_creator_and_status(self.user.username, 'open')) == 1)
def test_get_reviews_by_reviewer_and_status(self):
assert(len(DraftReview.objects.get_reviews_by_reviewer_and_status('foo@foo.com', 'open')) == 0)
review = DraftReview.objects.add(self.user.username, self.draft)