diffs: add comments to changeset diffs
This commit is contained in:
parent
d1a7921cfc
commit
e97cb5ad1e
12 changed files with 563 additions and 124 deletions
|
|
@ -156,15 +156,24 @@ class ChangesetController(BaseRepoController):
|
|||
c.ignorews_url = _ignorews_url
|
||||
c.context_url = _context_url
|
||||
c.fulldiff = fulldiff = request.GET.get('fulldiff')
|
||||
|
||||
# fetch global flags of ignore ws or context lines
|
||||
context_lcl = get_line_ctx('', request.GET)
|
||||
ign_whitespace_lcl = get_ignore_ws('', request.GET)
|
||||
|
||||
# diff_limit will cut off the whole diff if the limit is applied
|
||||
# otherwise it will just hide the big files from the front-end
|
||||
diff_limit = self.cut_off_limit_diff
|
||||
file_limit = self.cut_off_limit_file
|
||||
|
||||
# get ranges of commit ids if preset
|
||||
commit_range = commit_id_range.split('...')[:2]
|
||||
enable_comments = True
|
||||
|
||||
try:
|
||||
pre_load = ['affected_files', 'author', 'branch', 'date',
|
||||
'message', 'parents']
|
||||
|
||||
if len(commit_range) == 2:
|
||||
enable_comments = False
|
||||
commits = c.rhodecode_repo.get_commits(
|
||||
start_id=commit_range[0], end_id=commit_range[1],
|
||||
pre_load=pre_load)
|
||||
|
|
@ -190,60 +199,45 @@ class ChangesetController(BaseRepoController):
|
|||
c.lines_deleted = 0
|
||||
|
||||
c.commit_statuses = ChangesetStatus.STATUSES
|
||||
c.comments = []
|
||||
c.statuses = []
|
||||
c.inline_comments = []
|
||||
c.inline_cnt = 0
|
||||
c.files = []
|
||||
|
||||
c.statuses = []
|
||||
c.comments = []
|
||||
if len(c.commit_ranges) == 1:
|
||||
commit = c.commit_ranges[0]
|
||||
c.comments = ChangesetCommentsModel().get_comments(
|
||||
c.rhodecode_db_repo.repo_id,
|
||||
revision=commit.raw_id)
|
||||
c.statuses.append(ChangesetStatusModel().get_status(
|
||||
c.rhodecode_db_repo.repo_id, commit.raw_id))
|
||||
# comments from PR
|
||||
statuses = ChangesetStatusModel().get_statuses(
|
||||
c.rhodecode_db_repo.repo_id, commit.raw_id,
|
||||
with_revisions=True)
|
||||
prs = set(st.pull_request for st in statuses
|
||||
if st is st.pull_request is not None)
|
||||
|
||||
# from associated statuses, check the pull requests, and
|
||||
# show comments from them
|
||||
for pr in prs:
|
||||
c.comments.extend(pr.comments)
|
||||
|
||||
# Iterate over ranges (default commit view is always one commit)
|
||||
for commit in c.commit_ranges:
|
||||
if method == 'show':
|
||||
c.statuses.extend([ChangesetStatusModel().get_status(
|
||||
c.rhodecode_db_repo.repo_id, commit.raw_id)])
|
||||
|
||||
c.comments.extend(ChangesetCommentsModel().get_comments(
|
||||
c.rhodecode_db_repo.repo_id,
|
||||
revision=commit.raw_id))
|
||||
|
||||
# comments from PR
|
||||
st = ChangesetStatusModel().get_statuses(
|
||||
c.rhodecode_db_repo.repo_id, commit.raw_id,
|
||||
with_revisions=True)
|
||||
|
||||
# from associated statuses, check the pull requests, and
|
||||
# show comments from them
|
||||
|
||||
prs = set(x.pull_request for x in
|
||||
filter(lambda x: x.pull_request is not None, st))
|
||||
for pr in prs:
|
||||
c.comments.extend(pr.comments)
|
||||
|
||||
inlines = ChangesetCommentsModel().get_inline_comments(
|
||||
c.rhodecode_db_repo.repo_id, revision=commit.raw_id)
|
||||
c.inline_comments.extend(inlines.iteritems())
|
||||
|
||||
c.changes[commit.raw_id] = []
|
||||
|
||||
commit2 = commit
|
||||
commit1 = commit.parents[0] if commit.parents else EmptyCommit()
|
||||
|
||||
# fetch global flags of ignore ws or context lines
|
||||
context_lcl = get_line_ctx('', request.GET)
|
||||
ign_whitespace_lcl = get_ignore_ws('', request.GET)
|
||||
|
||||
_diff = c.rhodecode_repo.get_diff(
|
||||
commit1, commit2,
|
||||
ignore_whitespace=ign_whitespace_lcl, context=context_lcl)
|
||||
|
||||
# diff_limit will cut off the whole diff if the limit is applied
|
||||
# otherwise it will just hide the big files from the front-end
|
||||
diff_limit = self.cut_off_limit_diff
|
||||
file_limit = self.cut_off_limit_file
|
||||
|
||||
diff_processor = diffs.DiffProcessor(
|
||||
_diff, format='newdiff', diff_limit=diff_limit,
|
||||
file_limit=file_limit, show_full_diff=fulldiff)
|
||||
|
||||
commit_changes = OrderedDict()
|
||||
if method == 'show':
|
||||
_parsed = diff_processor.prepare()
|
||||
|
|
@ -259,10 +253,15 @@ class ChangesetController(BaseRepoController):
|
|||
return None
|
||||
return get_node
|
||||
|
||||
inline_comments = ChangesetCommentsModel().get_inline_comments(
|
||||
c.rhodecode_db_repo.repo_id, revision=commit.raw_id)
|
||||
c.inline_cnt += len(inline_comments)
|
||||
|
||||
diffset = codeblocks.DiffSet(
|
||||
repo_name=c.repo_name,
|
||||
source_node_getter=_node_getter(commit1),
|
||||
target_node_getter=_node_getter(commit2),
|
||||
comments=inline_comments
|
||||
).render_patchset(_parsed, commit1.raw_id, commit2.raw_id)
|
||||
c.changes[commit.raw_id] = diffset
|
||||
else:
|
||||
|
|
@ -273,10 +272,6 @@ class ChangesetController(BaseRepoController):
|
|||
# sort comments by how they were generated
|
||||
c.comments = sorted(c.comments, key=lambda x: x.comment_id)
|
||||
|
||||
# count inline comments
|
||||
for __, lines in c.inline_comments:
|
||||
for comments in lines.values():
|
||||
c.inline_cnt += len(comments)
|
||||
|
||||
if len(c.commit_ranges) == 1:
|
||||
c.commit = c.commit_ranges[0]
|
||||
|
|
|
|||
|
|
@ -358,6 +358,7 @@ class DiffSet(object):
|
|||
source_nodes=None, target_nodes=None,
|
||||
max_file_size_limit=150 * 1024, # files over this size will
|
||||
# use fast highlighting
|
||||
comments=None,
|
||||
):
|
||||
|
||||
self.highlight_mode = highlight_mode
|
||||
|
|
@ -367,7 +368,7 @@ class DiffSet(object):
|
|||
self.source_nodes = source_nodes or {}
|
||||
self.target_nodes = target_nodes or {}
|
||||
self.repo_name = repo_name
|
||||
|
||||
self.comments = comments or {}
|
||||
self.max_file_size_limit = max_file_size_limit
|
||||
|
||||
def render_patchset(self, patchset, source_ref=None, target_ref=None):
|
||||
|
|
@ -537,6 +538,8 @@ class DiffSet(object):
|
|||
original.lineno = before['old_lineno']
|
||||
original.content = before['line']
|
||||
original.action = self.action_to_op(before['action'])
|
||||
original.comments = self.get_comments_for('old',
|
||||
source_file, before['old_lineno'])
|
||||
|
||||
if after:
|
||||
if after['action'] == 'new-no-nl':
|
||||
|
|
@ -548,6 +551,8 @@ class DiffSet(object):
|
|||
modified.lineno = after['new_lineno']
|
||||
modified.content = after['line']
|
||||
modified.action = self.action_to_op(after['action'])
|
||||
modified.comments = self.get_comments_for('new',
|
||||
target_file, after['new_lineno'])
|
||||
|
||||
# diff the lines
|
||||
if before_tokens and after_tokens:
|
||||
|
|
@ -569,6 +574,20 @@ class DiffSet(object):
|
|||
|
||||
return lines
|
||||
|
||||
def get_comments_for(self, version, file, line_number):
|
||||
if hasattr(file, 'unicode_path'):
|
||||
file = file.unicode_path
|
||||
|
||||
if not isinstance(file, basestring):
|
||||
return None
|
||||
|
||||
line_key = {
|
||||
'old': 'o',
|
||||
'new': 'n',
|
||||
}[version] + str(line_number)
|
||||
|
||||
return self.comments.get(file, {}).get(line_key)
|
||||
|
||||
def get_line_tokens(self, line_text, line_number, file=None):
|
||||
filenode = None
|
||||
filename = None
|
||||
|
|
@ -619,22 +638,26 @@ class DiffSet(object):
|
|||
if line.original:
|
||||
if line.original.action == ' ':
|
||||
yield (line.original.lineno, line.modified.lineno,
|
||||
line.original.action, line.original.content)
|
||||
line.original.action, line.original.content,
|
||||
line.original.comments)
|
||||
continue
|
||||
|
||||
if line.original.action == '-':
|
||||
yield (line.original.lineno, None,
|
||||
line.original.action, line.original.content)
|
||||
line.original.action, line.original.content,
|
||||
line.original.comments)
|
||||
|
||||
if line.modified.action == '+':
|
||||
buf.append((
|
||||
None, line.modified.lineno,
|
||||
line.modified.action, line.modified.content))
|
||||
line.modified.action, line.modified.content,
|
||||
line.modified.comments))
|
||||
continue
|
||||
|
||||
if line.modified:
|
||||
yield (None, line.modified.lineno,
|
||||
line.modified.action, line.modified.content)
|
||||
line.modified.action, line.modified.content,
|
||||
line.modified.comments)
|
||||
|
||||
for b in buf:
|
||||
yield b
|
||||
|
|
|
|||
|
|
@ -730,6 +730,7 @@ input.filediff-collapse-state {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filediff {
|
||||
border: 1px solid @grey5;
|
||||
|
||||
|
|
@ -785,12 +786,13 @@ input.filediff-collapse-state {
|
|||
.filediff-menu {
|
||||
float: right;
|
||||
|
||||
a, span {
|
||||
&> a, &> span {
|
||||
padding: 5px;
|
||||
display: block;
|
||||
float: left
|
||||
}
|
||||
}
|
||||
|
||||
.pill {
|
||||
&[op="name"] {
|
||||
background: none;
|
||||
|
|
@ -857,7 +859,87 @@ input.filediff-collapse-state {
|
|||
.filediff-collapsed .filediff-expand-button {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
@comment-padding: 5px;
|
||||
|
||||
/**** COMMENTS ****/
|
||||
|
||||
.filediff-menu {
|
||||
.show-comment-button {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
&.hide-comments {
|
||||
.inline-comments {
|
||||
display: none;
|
||||
}
|
||||
.filediff-menu {
|
||||
.show-comment-button {
|
||||
display: inline;
|
||||
}
|
||||
.show-comment-button {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
.inline-comments {
|
||||
border-radius: @border-radius;
|
||||
background: @grey6;
|
||||
.comment {
|
||||
margin: 0;
|
||||
border-radius: @border-radius;
|
||||
}
|
||||
.comment-outdated {
|
||||
opacity: 0.5;
|
||||
}
|
||||
.comment-inline {
|
||||
background: white;
|
||||
padding: (@comment-padding + 3px) @comment-padding;
|
||||
border: @comment-padding solid @grey6;
|
||||
|
||||
.text {
|
||||
border: none;
|
||||
}
|
||||
.meta {
|
||||
border-bottom: 1px solid @grey6;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
}
|
||||
.comment-selected {
|
||||
border-left: 6px solid @comment-highlight-color;
|
||||
}
|
||||
.comment-inline-form {
|
||||
padding: @comment-padding;
|
||||
display: none;
|
||||
}
|
||||
.cb-comment-add-button {
|
||||
margin: @comment-padding;
|
||||
}
|
||||
/* hide add comment button when form is open */
|
||||
.comment-inline-form-open + .cb-comment-add-button {
|
||||
display: none;
|
||||
}
|
||||
.comment-inline-form-open {
|
||||
display: block;
|
||||
}
|
||||
/* hide add comment button when form but no comments */
|
||||
.comment-inline-form:first-child + .cb-comment-add-button {
|
||||
display: none;
|
||||
}
|
||||
/* hide add comment button when no comments or form */
|
||||
.cb-comment-add-button:first-child {
|
||||
display: none;
|
||||
}
|
||||
/* hide add comment button when only comment is being deleted */
|
||||
.comment-deleting:first-child + .cb-comment-add-button {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
/**** END COMMENTS ****/
|
||||
|
||||
}
|
||||
|
||||
|
||||
table.cb {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
|
|
@ -956,6 +1038,27 @@ table.cb {
|
|||
font-family: @font-family-monospace;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
&> button.cb-comment-box-opener {
|
||||
padding: 2px 6px 2px 6px;
|
||||
margin-left: -20px;
|
||||
margin-top: -2px;
|
||||
border-radius: @border-radius;
|
||||
position: absolute;
|
||||
display: none;
|
||||
}
|
||||
.cb-comment {
|
||||
margin-top: 10px;
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
button.cb-comment-box-opener {
|
||||
display: block;
|
||||
}
|
||||
&+ td button.cb-comment-box-opener {
|
||||
display: block
|
||||
}
|
||||
}
|
||||
|
||||
&.cb-lineno {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,9 @@ a { cursor: pointer; }
|
|||
clear: both;
|
||||
}
|
||||
|
||||
.js-template { /* mark a template for javascript use */
|
||||
display: none;
|
||||
}
|
||||
|
||||
.linebreak {
|
||||
display: block;
|
||||
|
|
|
|||
|
|
@ -323,7 +323,7 @@ var bindToggleButtons = function() {
|
|||
};
|
||||
|
||||
var linkifyComments = function(comments) {
|
||||
|
||||
/* TODO: dan: remove this - it should no longer needed */
|
||||
for (var i = 0; i < comments.length; i++) {
|
||||
var comment_id = $(comments[i]).data('comment-id');
|
||||
var prev_comment_id = $(comments[i - 1]).data('comment-id');
|
||||
|
|
@ -347,7 +347,7 @@ var linkifyComments = function(comments) {
|
|||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Iterates over all the inlines, and places them inside proper blocks of data
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -114,6 +114,218 @@ c.template_context['visual']['default_renderer'] = h.get_visual_attr(c, 'default
|
|||
rhodecode_edition: '${c.rhodecode_edition}'
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Rhodecode = (function() {
|
||||
function _Rhodecode() {
|
||||
this.comments = new (function() { /* comments controller */
|
||||
var self = this;
|
||||
|
||||
this.cancelComment = function(node) {
|
||||
var $node = $(node);
|
||||
var $td = $node.closest('td');
|
||||
$node.closest('.comment-inline-form').removeClass('comment-inline-form-open');
|
||||
return false;
|
||||
}
|
||||
this.getLineNumber = function(node) {
|
||||
var $node = $(node);
|
||||
return $node.closest('td').attr('data-line-number');
|
||||
}
|
||||
this.scrollToComment = function(node, offset) {
|
||||
if (!node) {
|
||||
node = $('.comment-selected');
|
||||
if (!node.length) {
|
||||
node = $('comment-current')
|
||||
}
|
||||
}
|
||||
$comment = $(node).closest('.comment-current');
|
||||
$comments = $('.comment-current');
|
||||
|
||||
$('.comment-selected').removeClass('comment-selected');
|
||||
|
||||
var nextIdx = $('.comment-current').index($comment) + offset;
|
||||
if (nextIdx >= $comments.length) {
|
||||
nextIdx = 0;
|
||||
}
|
||||
var $next = $('.comment-current').eq(nextIdx);
|
||||
var $cb = $next.closest('.cb');
|
||||
$cb.removeClass('cb-collapsed')
|
||||
|
||||
var $filediffCollapseState = $cb.closest('.filediff').prev();
|
||||
$filediffCollapseState.prop('checked', false);
|
||||
$next.addClass('comment-selected');
|
||||
scrollToElement($next);
|
||||
return false;
|
||||
}
|
||||
this.nextComment = function(node) {
|
||||
return self.scrollToComment(node, 1);
|
||||
}
|
||||
this.prevComment = function(node) {
|
||||
return self.scrollToComment(node, -1);
|
||||
}
|
||||
this.deleteComment = function(node) {
|
||||
if (!confirm(_gettext('Delete this comment?'))) {
|
||||
return false;
|
||||
}
|
||||
var $node = $(node);
|
||||
var $td = $node.closest('td');
|
||||
var $comment = $node.closest('.comment');
|
||||
var comment_id = $comment.attr('data-comment-id');
|
||||
var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id);
|
||||
var postData = {
|
||||
'_method': 'delete',
|
||||
'csrf_token': CSRF_TOKEN
|
||||
};
|
||||
|
||||
$comment.addClass('comment-deleting');
|
||||
$comment.hide('fast');
|
||||
|
||||
var success = function(response) {
|
||||
$comment.remove();
|
||||
return false;
|
||||
};
|
||||
var failure = function(data, textStatus, xhr) {
|
||||
alert("error processing request: " + textStatus);
|
||||
$comment.show('fast');
|
||||
$comment.removeClass('comment-deleting');
|
||||
return false;
|
||||
};
|
||||
ajaxPOST(url, postData, success, failure);
|
||||
}
|
||||
this.createComment = function(node) {
|
||||
var $node = $(node);
|
||||
var $td = $node.closest('td');
|
||||
var $form = $td.find('.comment-inline-form');
|
||||
|
||||
if (!$form.length) {
|
||||
var tmpl = $('#cb-comment-inline-form-template').html();
|
||||
var f_path = $node.closest('.filediff').attr('data-f-path');
|
||||
var lineno = self.getLineNumber(node);
|
||||
tmpl = tmpl.format(f_path, lineno);
|
||||
$form = $(tmpl);
|
||||
|
||||
var $comments = $td.find('.inline-comments');
|
||||
if (!$comments.length) {
|
||||
$comments = $(
|
||||
$('#cb-comments-inline-container-template').html());
|
||||
$td.append($comments);
|
||||
}
|
||||
|
||||
$td.find('.cb-comment-add-button').before($form);
|
||||
|
||||
var pullRequestId = templateContext.pull_request_data.pull_request_id;
|
||||
var commitId = templateContext.commit_data.commit_id;
|
||||
var _form = $form[0];
|
||||
var commentForm = new CommentForm(_form, commitId, pullRequestId, lineno, false);
|
||||
var cm = commentForm.getCmInstance();
|
||||
|
||||
// set a CUSTOM submit handler for inline comments.
|
||||
commentForm.setHandleFormSubmit(function(o) {
|
||||
var text = commentForm.cm.getValue();
|
||||
|
||||
if (text === "") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (lineno === undefined) {
|
||||
alert('missing line !');
|
||||
return;
|
||||
}
|
||||
if (f_path === undefined) {
|
||||
alert('missing file path !');
|
||||
return;
|
||||
}
|
||||
|
||||
var excludeCancelBtn = false;
|
||||
var submitEvent = true;
|
||||
commentForm.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
|
||||
commentForm.cm.setOption("readOnly", true);
|
||||
var postData = {
|
||||
'text': text,
|
||||
'f_path': f_path,
|
||||
'line': lineno,
|
||||
'csrf_token': CSRF_TOKEN
|
||||
};
|
||||
var submitSuccessCallback = function(json_data) {
|
||||
$form.remove();
|
||||
console.log(json_data)
|
||||
try {
|
||||
var html = json_data.rendered_text;
|
||||
var lineno = json_data.line_no;
|
||||
var target_id = json_data.target_id;
|
||||
|
||||
$comments.find('.cb-comment-add-button').before(html);
|
||||
console.log(lineno, target_id, $comments);
|
||||
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
|
||||
// re trigger the linkification of next/prev navigation
|
||||
linkifyComments($('.inline-comment-injected'));
|
||||
timeagoActivate();
|
||||
bindDeleteCommentButtons();
|
||||
commentForm.setActionButtonsDisabled(false);
|
||||
|
||||
};
|
||||
var submitFailCallback = function(){
|
||||
commentForm.resetCommentFormState(text)
|
||||
};
|
||||
commentForm.submitAjaxPOST(
|
||||
commentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
|
||||
});
|
||||
|
||||
setTimeout(function() {
|
||||
// callbacks
|
||||
if (cm !== undefined) {
|
||||
cm.focus();
|
||||
}
|
||||
}, 10);
|
||||
|
||||
$.Topic('/ui/plugins/code/comment_form_built').prepareOrPublish({
|
||||
form: _form,
|
||||
parent: $td[0],
|
||||
lineno: lineno,
|
||||
f_path: f_path}
|
||||
);
|
||||
}
|
||||
|
||||
$form.addClass('comment-inline-form-open');
|
||||
}
|
||||
|
||||
this.renderInlineComments = function(file_comments) {
|
||||
show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true;
|
||||
|
||||
for (var i = 0; i < file_comments.length; i++) {
|
||||
var box = file_comments[i];
|
||||
|
||||
var target_id = $(box).attr('target_id');
|
||||
|
||||
// actually comments with line numbers
|
||||
var comments = box.children;
|
||||
|
||||
for (var j = 0; j < comments.length; j++) {
|
||||
var data = {
|
||||
'rendered_text': comments[j].outerHTML,
|
||||
'line_no': $(comments[j]).attr('line'),
|
||||
'target_id': target_id
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// since order of injection is random, we're now re-iterating
|
||||
// from correct order and filling in links
|
||||
linkifyComments($('.inline-comment-injected'));
|
||||
bindDeleteCommentButtons();
|
||||
firefoxAnchorFix();
|
||||
};
|
||||
|
||||
})();
|
||||
}
|
||||
return new _Rhodecode();
|
||||
})();
|
||||
|
||||
</script>
|
||||
<%include file="/base/plugins_base.html"/>
|
||||
<!--[if lt IE 9]>
|
||||
|
|
|
|||
|
|
@ -147,8 +147,7 @@
|
|||
${ungettext("%d Commit comment", "%d Commit comments", len(c.comments)) % len(c.comments)}
|
||||
%endif
|
||||
%if c.inline_cnt:
|
||||
## this is replaced with a proper link to first comment via JS linkifyComments() func
|
||||
<a href="#inline-comments" id="inline-comments-counter">${ungettext("%d Inline Comment", "%d Inline Comments", c.inline_cnt) % c.inline_cnt}</a>
|
||||
<a href="#" onclick="return Rhodecode.comments.nextComment();" id="inline-comments-counter">${ungettext("%d Inline Comment", "%d Inline Comments", c.inline_cnt) % c.inline_cnt}</a>
|
||||
%else:
|
||||
${ungettext("%d Inline Comment", "%d Inline Comments", c.inline_cnt) % c.inline_cnt}
|
||||
%endif
|
||||
|
|
@ -171,23 +170,23 @@
|
|||
</div><!-- end sidebar -->
|
||||
</div> <!-- end summary -->
|
||||
<div class="cs_files">
|
||||
${cbdiffs.render_diffset_menu()}
|
||||
|
||||
<%namespace name="cbdiffs" file="/codeblocks/diffs.html"/>
|
||||
${cbdiffs.render_diffset(c.changes[c.commit.raw_id], commit=c.commit)}
|
||||
<%namespace name="cbdiffs" file="/codeblocks/diffs.html"/>
|
||||
${cbdiffs.render_diffset_menu()}
|
||||
${cbdiffs.render_diffset(
|
||||
c.changes[c.commit.raw_id], commit=c.commit, use_comments=True)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
## template for inline comment form
|
||||
<%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
|
||||
${comment.comment_inline_form()}
|
||||
|
||||
## render comments and inlines
|
||||
## ## render comments and inlines
|
||||
${comment.generate_comments()}
|
||||
|
||||
## main comment form and it status
|
||||
${comment.comments(h.url('changeset_comment', repo_name=c.repo_name, revision=c.commit.raw_id),
|
||||
h.commit_status(c.rhodecode_db_repo, c.commit.raw_id))}
|
||||
</div>
|
||||
|
||||
## FORM FOR MAKING JS ACTION AS CHANGESET COMMENTS
|
||||
<script type="text/javascript">
|
||||
|
|
@ -310,7 +309,6 @@
|
|||
|
||||
// inject comments into their proper positions
|
||||
var file_comments = $('.inline-comment-placeholder');
|
||||
renderInlineComments(file_comments, true);
|
||||
})
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,14 @@
|
|||
<%namespace name="base" file="/base/base.html"/>
|
||||
|
||||
<%def name="comment_block(comment, inline=False)">
|
||||
<div class="comment ${'comment-inline' if inline else ''}" id="comment-${comment.comment_id}" line="${comment.line_no}" data-comment-id="${comment.comment_id}">
|
||||
<div
|
||||
class="comment
|
||||
${'comment-inline' if inline else ''}
|
||||
${'comment-outdated' if comment.outdated else 'comment-current'}"
|
||||
"
|
||||
id="comment-${comment.comment_id}"
|
||||
line="${comment.line_no}"
|
||||
data-comment-id="${comment.comment_id}">
|
||||
<div class="meta">
|
||||
<div class="author">
|
||||
${base.gravatar_with_user(comment.author.email, 16)}
|
||||
|
|
@ -46,24 +53,15 @@
|
|||
## only super-admin, repo admin OR comment owner can delete
|
||||
%if not comment.pull_request or (comment.pull_request and not comment.pull_request.is_closed()):
|
||||
%if h.HasPermissionAny('hg.admin')() or h.HasRepoPermissionAny('repository.admin')(c.repo_name) or comment.author.user_id == c.rhodecode_user.user_id:
|
||||
<div onClick="deleteComment(${comment.comment_id})" class="delete-comment"> ${_('Delete')}</div>
|
||||
%if inline:
|
||||
<div class="comment-links-divider"> | </div>
|
||||
## TODO: dan: add edit comment here
|
||||
<a onclick="return Rhodecode.comments.deleteComment(this);" class="delete-comment"> ${_('Delete')}</a>
|
||||
%if not comment.outdated:
|
||||
<a onclick="return Rhodecode.comments.prevComment(this);" class="prev-comment"> ${_('Prev')}</a>
|
||||
<a onclick="return Rhodecode.comments.nextComment(this);" class="next-comment"> ${_('Next')}</a>
|
||||
%endif
|
||||
%endif
|
||||
%endif
|
||||
|
||||
%if inline:
|
||||
|
||||
<div id="prev_c_${comment.comment_id}" class="comment-previous-link" title="${_('Previous comment')}">
|
||||
<a class="arrow_comment_link disabled"><i class="icon-left"></i></a>
|
||||
</div>
|
||||
|
||||
<div id="next_c_${comment.comment_id}" class="comment-next-link" title="${_('Next comment')}">
|
||||
<a class="arrow_comment_link disabled"><i class="icon-right"></i></a>
|
||||
</div>
|
||||
%endif
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="text">
|
||||
|
|
@ -165,31 +163,8 @@
|
|||
</%def>
|
||||
|
||||
|
||||
## generates inlines taken from c.comments var
|
||||
<%def name="inlines(is_pull_request=False)">
|
||||
%if is_pull_request:
|
||||
<h2 id="comments">${ungettext("%d Pull Request Comment", "%d Pull Request Comments", len(c.comments)) % len(c.comments)}</h2>
|
||||
%else:
|
||||
<h2 id="comments">${ungettext("%d Commit Comment", "%d Commit Comments", len(c.comments)) % len(c.comments)}</h2>
|
||||
%endif
|
||||
%for path, lines_comments in c.inline_comments:
|
||||
% for line, comments in lines_comments.iteritems():
|
||||
<div style="display: none;" class="inline-comment-placeholder" path="${path}" target_id="${h.safeid(h.safe_unicode(path))}">
|
||||
## for each comment in particular line
|
||||
%for comment in comments:
|
||||
${comment_block(comment, inline=True)}
|
||||
%endfor
|
||||
</div>
|
||||
%endfor
|
||||
%endfor
|
||||
|
||||
</%def>
|
||||
|
||||
## generate inline comments and the main ones
|
||||
## generate main comments
|
||||
<%def name="generate_comments(include_pull_request=False, is_pull_request=False)">
|
||||
## generate inlines for this changeset
|
||||
${inlines(is_pull_request)}
|
||||
|
||||
%for comment in c.comments:
|
||||
<div id="comment-tr-${comment.comment_id}">
|
||||
## only render comments that are not from pull request, or from
|
||||
|
|
|
|||
|
|
@ -54,12 +54,12 @@
|
|||
##CS
|
||||
<%include file="../compare/compare_commits.html"/>
|
||||
<div class="cs_files">
|
||||
${cbdiffs.render_diffset_menu()}
|
||||
<%namespace name="cbdiffs" file="/codeblocks/diffs.html"/>
|
||||
<%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
|
||||
<%namespace name="diff_block" file="/changeset/diff_block.html"/>
|
||||
${cbdiffs.render_diffset_menu()}
|
||||
%for commit in c.commit_ranges:
|
||||
${cbdifss.render_diffset(
|
||||
${cbdiffs.render_diffset(
|
||||
diffset=c.changes[commit.raw_id],
|
||||
collapse_when_files_over=5,
|
||||
commit=commit,
|
||||
|
|
|
|||
|
|
@ -34,7 +34,74 @@ return h.url('', **new_args)
|
|||
# add a ruler at to the output
|
||||
ruler_at_chars=0,
|
||||
|
||||
# turn on inline comments
|
||||
use_comments=False,
|
||||
|
||||
)">
|
||||
|
||||
%if use_comments:
|
||||
<div id="cb-comments-inline-container-template" class="js-template">
|
||||
${inline_comments_container([])}
|
||||
</div>
|
||||
<div class="js-template" id="cb-comment-inline-form-template">
|
||||
<div class="comment-inline-form ac">
|
||||
%if c.rhodecode_user.username != h.DEFAULT_USER:
|
||||
${h.form('#', method='get')}
|
||||
<div id="edit-container_{1}" class="clearfix">
|
||||
<div class="comment-title pull-left">
|
||||
${_('Create a comment on line {1}.')}
|
||||
</div>
|
||||
<div class="comment-help pull-right">
|
||||
${(_('Comments parsed using %s syntax with %s support.') % (
|
||||
('<a href="%s">%s</a>' % (h.url('%s_help' % c.visual.default_renderer), c.visual.default_renderer.upper())),
|
||||
('<span class="tooltip" title="%s">@mention</span>' % _('Use @username inside this text to send notification to this RhodeCode user'))
|
||||
)
|
||||
)|n
|
||||
}
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
<textarea id="text_{1}" name="text" class="comment-block-ta ac-input"></textarea>
|
||||
</div>
|
||||
<div id="preview-container_{1}" class="clearfix" style="display: none;">
|
||||
<div class="comment-help">
|
||||
${_('Comment preview')}
|
||||
</div>
|
||||
<div id="preview-box_{1}" class="preview-box"></div>
|
||||
</div>
|
||||
<div class="comment-footer">
|
||||
<div class="action-buttons">
|
||||
<input type="hidden" name="f_path" value="{0}">
|
||||
<input type="hidden" name="line" value="{1}">
|
||||
<button id="preview-btn_{1}" class="btn btn-secondary">${_('Preview')}</button>
|
||||
<button id="edit-btn_{1}" class="btn btn-secondary" style="display: none;">${_('Edit')}</button>
|
||||
${h.submit('save', _('Comment'), class_='btn btn-success save-inline-form')}
|
||||
</div>
|
||||
<div class="comment-button">
|
||||
<button type="button" class="cb-comment-cancel" onclick="return Rhodecode.comments.cancelComment(this);">
|
||||
${_('Cancel')}
|
||||
</button>
|
||||
</div>
|
||||
${h.end_form()}
|
||||
</div>
|
||||
%else:
|
||||
${h.form('', class_='inline-form comment-form-login', method='get')}
|
||||
<div class="pull-left">
|
||||
<div class="comment-help pull-right">
|
||||
${_('You need to be logged in to comment.')} <a href="${h.route_path('login', _query={'came_from': h.url.current()})}">${_('Login now')}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="comment-button pull-right">
|
||||
<button type="button" class="cb-comment-cancel" onclick="return Rhodecode.comments.cancelComment(this);">
|
||||
${_('Cancel')}
|
||||
</button>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
${h.end_form()}
|
||||
%endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
%endif
|
||||
<%
|
||||
collapse_all = len(diffset.files) > collapse_when_files_over
|
||||
%>
|
||||
|
|
@ -101,12 +168,12 @@ collapse_all = len(diffset.files) > collapse_when_files_over
|
|||
<div
|
||||
class="filediff"
|
||||
data-f-path="${filediff['patch']['filename']}"
|
||||
id="a_${h.FID(commit and commit.raw_id or '', filediff['patch']['filename'])}">
|
||||
id="a_${h.FID('', filediff['patch']['filename'])}">
|
||||
<label for="filediff-collapse-${id(filediff)}" class="filediff-heading">
|
||||
<div class="filediff-collapse-indicator"></div>
|
||||
${diff_ops(filediff)}
|
||||
</label>
|
||||
${diff_menu(filediff)}
|
||||
${diff_menu(filediff, use_comments=use_comments)}
|
||||
<table class="cb cb-diff-${c.diffmode} code-highlight ${over_lines_changed_limit and 'cb-collapsed' or ''}">
|
||||
%if not filediff.hunks:
|
||||
%for op_id, op_text in filediff['patch']['stats']['ops'].items():
|
||||
|
|
@ -159,9 +226,9 @@ collapse_all = len(diffset.files) > collapse_when_files_over
|
|||
</td>
|
||||
</tr>
|
||||
%if c.diffmode == 'unified':
|
||||
${render_hunk_lines_unified(hunk)}
|
||||
${render_hunk_lines_unified(hunk, use_comments=use_comments)}
|
||||
%elif c.diffmode == 'sideside':
|
||||
${render_hunk_lines_sideside(hunk)}
|
||||
${render_hunk_lines_sideside(hunk, use_comments=use_comments)}
|
||||
%else:
|
||||
<tr class="cb-line">
|
||||
<td>unknown diff mode</td>
|
||||
|
|
@ -227,7 +294,7 @@ from rhodecode.lib.diffs import NEW_FILENODE, DEL_FILENODE, \
|
|||
%endif
|
||||
</span>
|
||||
|
||||
<a class="pill filediff-anchor" href="#a_${h.FID(commit and commit.raw_id or '', filediff.patch['filename'])}">¶</a>
|
||||
<a class="pill filediff-anchor" href="#a_${h.FID('', filediff.patch['filename'])}">¶</a>
|
||||
|
||||
<span class="pill-group" style="float: right">
|
||||
%if BIN_FILENODE in stats['ops']:
|
||||
|
|
@ -250,7 +317,7 @@ from rhodecode.lib.diffs import NEW_FILENODE, DEL_FILENODE, \
|
|||
${filemode.startswith('100') and filemode[3:] or filemode}
|
||||
</%def>
|
||||
|
||||
<%def name="diff_menu(filediff)">
|
||||
<%def name="diff_menu(filediff, use_comments=False)">
|
||||
<div class="filediff-menu">
|
||||
%if filediff.diffset.source_ref:
|
||||
%if filediff.patch['operation'] in ['D', 'M']:
|
||||
|
|
@ -299,12 +366,41 @@ from rhodecode.lib.diffs import NEW_FILENODE, DEL_FILENODE, \
|
|||
>
|
||||
${_('Download diff')}
|
||||
</a>
|
||||
|
||||
## TODO: dan: refactor ignorews_url and context_url into the diff renderer same as diffmode=unified/sideside. Also use ajax to load more context (by clicking hunks)
|
||||
%if hasattr(c, 'ignorews_url'):
|
||||
${c.ignorews_url(request.GET, h.FID('', filediff['patch']['filename']))}
|
||||
%endif
|
||||
%if hasattr(c, 'context_url'):
|
||||
${c.context_url(request.GET, h.FID('', filediff['patch']['filename']))}
|
||||
%endif
|
||||
|
||||
|
||||
%if use_comments:
|
||||
<a href="#" onclick="$(this).closest('.filediff').toggleClass('hide-comments'); return false;">
|
||||
<span class="show-comment-button">${_('Show comments')}</span><span class="hide-comment-button">${_('Hide comments')}</span>
|
||||
</a>
|
||||
%endif
|
||||
%endif
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
|
||||
<%def name="render_hunk_lines_sideside(hunk)">
|
||||
<%namespace name="commentblock" file="/changeset/changeset_file_comment.html"/>
|
||||
<%def name="inline_comments_container(comments)">
|
||||
<div class="inline-comments">
|
||||
%for comment in comments:
|
||||
${commentblock.comment_block(comment, inline=True)}
|
||||
%endfor
|
||||
<span onclick="return Rhodecode.comments.createComment(this)"
|
||||
class="btn btn-secondary cb-comment-add-button">
|
||||
${_('Add another comment')}
|
||||
</span>
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
|
||||
<%def name="render_hunk_lines_sideside(hunk, use_comments=False)">
|
||||
%for i, line in enumerate(hunk.sideside):
|
||||
<%
|
||||
old_line_anchor, new_line_anchor = None, None
|
||||
|
|
@ -326,7 +422,14 @@ from rhodecode.lib.diffs import NEW_FILENODE, DEL_FILENODE, \
|
|||
</td>
|
||||
<td class="cb-content ${action_class(line.original.action)}"
|
||||
data-line-number="o${line.original.lineno}"
|
||||
><span class="cb-code">${line.original.action} ${line.original.content or '' | n}</span>
|
||||
>
|
||||
%if use_comments and line.original.lineno:
|
||||
${render_add_comment_button()}
|
||||
%endif
|
||||
<span class="cb-code">${line.original.action} ${line.original.content or '' | n}</span>
|
||||
%if use_comments and line.original.lineno and line.original.comments:
|
||||
${inline_comments_container(line.original.comments)}
|
||||
%endif
|
||||
</td>
|
||||
<td class="cb-lineno ${action_class(line.modified.action)}"
|
||||
data-line-number="${line.modified.lineno}"
|
||||
|
|
@ -341,15 +444,21 @@ from rhodecode.lib.diffs import NEW_FILENODE, DEL_FILENODE, \
|
|||
<td class="cb-content ${action_class(line.modified.action)}"
|
||||
data-line-number="n${line.modified.lineno}"
|
||||
>
|
||||
%if use_comments and line.modified.lineno:
|
||||
${render_add_comment_button()}
|
||||
%endif
|
||||
<span class="cb-code">${line.modified.action} ${line.modified.content or '' | n}</span>
|
||||
%if use_comments and line.modified.lineno and line.modified.comments:
|
||||
${inline_comments_container(line.modified.comments)}
|
||||
%endif
|
||||
</td>
|
||||
</tr>
|
||||
%endfor
|
||||
</%def>
|
||||
|
||||
|
||||
<%def name="render_hunk_lines_unified(hunk)">
|
||||
%for old_line_no, new_line_no, action, content in hunk.unified:
|
||||
<%def name="render_hunk_lines_unified(hunk, use_comments=False)">
|
||||
%for old_line_no, new_line_no, action, content, comments in hunk.unified:
|
||||
<%
|
||||
old_line_anchor, new_line_anchor = None, None
|
||||
if old_line_no:
|
||||
|
|
@ -380,12 +489,25 @@ from rhodecode.lib.diffs import NEW_FILENODE, DEL_FILENODE, \
|
|||
</td>
|
||||
<td class="cb-content ${action_class(action)}"
|
||||
data-line-number="${new_line_no and 'n' or 'o'}${new_line_no or old_line_no}"
|
||||
><span class="cb-code">${action} ${content or '' | n}</span>
|
||||
</td>
|
||||
>
|
||||
%if use_comments:
|
||||
${render_add_comment_button()}
|
||||
%endif
|
||||
<span class="cb-code">${action} ${content or '' | n}</span>
|
||||
%if use_comments and comments:
|
||||
${inline_comments_container(comments)}
|
||||
%endif
|
||||
</td>
|
||||
</tr>
|
||||
%endfor
|
||||
</%def>
|
||||
|
||||
<%def name="render_add_comment_button()">
|
||||
<button
|
||||
class="btn btn-small btn-primary cb-comment-box-opener"
|
||||
onclick="return Rhodecode.comments.createComment(this)"
|
||||
>+</button>
|
||||
</%def>
|
||||
|
||||
<%def name="render_diffset_menu()">
|
||||
<div class="diffset-menu clearinner">
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ class TestChangesetController(object):
|
|||
response.mustcontain('new file 100644')
|
||||
response.mustcontain('Changed theme to ADC theme') # commit msg
|
||||
|
||||
self._check_diff_menus(response, right_menu=True)
|
||||
self._check_new_diff_menus(response, right_menu=True)
|
||||
|
||||
def test_commit_range_page_different_ops(self, backend):
|
||||
commit_id_range = {
|
||||
|
|
@ -108,10 +108,13 @@ class TestChangesetController(object):
|
|||
# svn is special
|
||||
if backend.alias == 'svn':
|
||||
response.mustcontain('new file 10644')
|
||||
response.mustcontain('34 files changed: 1184 inserted, 311 deleted')
|
||||
response.mustcontain('1 file changed: 5 inserted, 1 deleted')
|
||||
response.mustcontain('12 files changed: 236 inserted, 22 deleted')
|
||||
response.mustcontain('21 files changed: 943 inserted, 288 deleted')
|
||||
else:
|
||||
response.mustcontain('new file 100644')
|
||||
response.mustcontain('33 files changed: 1165 inserted, 308 deleted')
|
||||
response.mustcontain('12 files changed: 222 inserted, 20 deleted')
|
||||
response.mustcontain('21 files changed: 943 inserted, 288 deleted')
|
||||
|
||||
# files op files
|
||||
response.mustcontain('File no longer present at commit: %s' %
|
||||
|
|
@ -119,7 +122,7 @@ class TestChangesetController(object):
|
|||
response.mustcontain('Added docstrings to vcs.cli') # commit msg
|
||||
response.mustcontain('Changed theme to ADC theme') # commit msg
|
||||
|
||||
self._check_diff_menus(response)
|
||||
self._check_new_diff_menus(response)
|
||||
|
||||
def test_combined_compare_commit_page_different_ops(self, backend):
|
||||
commit_id_range = {
|
||||
|
|
|
|||
|
|
@ -109,11 +109,17 @@ class TestCommitCommentsController(TestController):
|
|||
# test DB
|
||||
assert ChangesetComment.query().count() == 1
|
||||
assert_comment_links(response, 0, ChangesetComment.query().count())
|
||||
response.mustcontain(
|
||||
'''class="inline-comment-placeholder" '''
|
||||
'''path="vcs/web/simplevcs/views/repository.py" '''
|
||||
'''target_id="vcswebsimplevcsviewsrepositorypy"'''
|
||||
)
|
||||
|
||||
if backend.alias == 'svn':
|
||||
response.mustcontain(
|
||||
'''data-f-path="vcs/commands/summary.py" '''
|
||||
'''id="a_c--ad05457a43f8"'''
|
||||
)
|
||||
else:
|
||||
response.mustcontain(
|
||||
'''data-f-path="vcs/backends/hg.py" '''
|
||||
'''id="a_c--9c390eb52cd6"'''
|
||||
)
|
||||
|
||||
assert Notification.query().count() == 1
|
||||
assert ChangesetComment.query().count() == 1
|
||||
|
|
@ -271,7 +277,6 @@ def assert_comment_links(response, comments, inline_comments):
|
|||
inline_comments) % inline_comments
|
||||
if inline_comments:
|
||||
response.mustcontain(
|
||||
'<a href="#inline-comments" '
|
||||
'id="inline-comments-counter">%s</a>' % inline_comments_text)
|
||||
'id="inline-comments-counter">%s</' % inline_comments_text)
|
||||
else:
|
||||
response.mustcontain(inline_comments_text)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue