@@ -98,13 +98,17 @@ class ChangesetController(BaseRepoContro
c.cut_off = False # defines if cut off limit is reached
c.comments = []
c.inline_comments = []
c.inline_cnt = 0
# Iterate over ranges (default changeset view is always one changeset)
for changeset in c.cs_ranges:
c.comments.extend(ChangesetCommentsModel()\
.get_comments(c.rhodecode_db_repo.repo_id,
changeset.raw_id))
inlines = ChangesetCommentsModel()\
.get_inline_comments(c.rhodecode_db_repo.repo_id,
changeset.raw_id)
c.inline_comments.extend(inlines)
c.changes[changeset.raw_id] = []
try:
changeset_parent = changeset.parents[0]
@@ -199,6 +203,11 @@ class ChangesetController(BaseRepoContro
c.changes[changeset.raw_id].append(('removed', node, None,
None, None, (0, 0)))
# count inline comments
for path, lines in c.inline_comments:
for comments in lines.values():
c.inline_cnt += len(comments)
if len(c.cs_ranges) == 1:
c.changeset = c.cs_ranges[0]
c.changes = c.changes[c.changeset.raw_id]
@@ -49,18 +49,18 @@ class ChangesetCommentsModel(BaseModel):
:param f_path:
:param line_no:
"""
if text:
comment = ChangesetComment()
comment.repo_id = repo_id
comment.user_id = user_id
comment.revision = revision
comment.text = text
comment.f_path = f_path
comment.line_no = line_no
self.sa.add(comment)
self.sa.commit()
return comment
def delete(self, comment_id):
@@ -81,13 +81,13 @@ class ChangesetCommentsModel(BaseModel):
.filter(ChangesetComment.line_no == None)\
.filter(ChangesetComment.f_path == None).all()
def get_comments_for_file(self, repo_id, f_path, raw_id):
def get_inline_comments(self, repo_id, revision):
comments = self.sa.query(ChangesetComment)\
.filter(ChangesetComment.repo_id == repo_id)\
.filter(ChangesetComment.commit_id == raw_id)\
.filter(ChangesetComment.f_path == f_path).all()
.filter(ChangesetComment.revision == revision).all()
paths = defaultdict(lambda:defaultdict(list))
d = defaultdict(list)
for co in comments:
d[co.line_no].append(co)
return d.items()
paths[co.f_path][co.line_no].append(co)
return paths.items()
@@ -1101,11 +1101,11 @@ class ChangesetComment(Base, BaseModel):
comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
revision = Column('revision', String(40), nullable=False)
line_no = Column('line_no', Integer(), nullable=True)
f_path = Column('f_path', String(1000), nullable=True)
line_no = Column('line_no', Unicode(10), nullable=True)
f_path = Column('f_path', Unicode(1000), nullable=True)
user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
text = Column('text', String(25000), nullable=False)
modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
text = Column('text', Unicode(25000), nullable=False)
modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
author = relationship('User')
repo = relationship('Repository')
@@ -117,4 +117,13 @@ cursor: pointer;
.line{
padding:0;
margin:0;
}
\ No newline at end of file
.line.highlight{
background-color:#FFFFCC;
cursor: pointer;
background-image:url("../images/icons/comment_add.png");
background-repeat:no-repeat;
background-position: right;
background-position: 100% 50%;
@@ -1853,20 +1853,24 @@ h3.files_location {
.cs_files .changes {
float: right;
color:#003367;
.cs_files .changes .added {
background-color: #BBFFBB;
float: left;
text-align: center;
font-size: 90%;
font-size: 9px;
padding: 2px 0px 2px 0px;
.cs_files .changes .deleted {
background-color: #FF8888;
.cs_files .cs_added {
@@ -3270,7 +3274,10 @@ div.rst-block pre {
.comments .comments-number{
padding:0px 0px 10px 0px;
font-weight: bold;
color: #666;
font-size: 16px;
/** comment form **/
.comment-form .clearfix{
background: #EEE;
@@ -3332,3 +3339,59 @@ form.comment-form {
position: absolute;
right:40px;
/** comment inline form **/
.comment-inline-form .clearfix{
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
padding: 5px;
div.comment-inline-form {
margin-top: 5px;
padding:2px 6px 8px 6px;
.comment-inline-form strong {
display: block;
margin-bottom: 15px;
.comment-inline-form textarea {
width: 100%;
height: 100px;
font-family: 'Monaco', 'Courier', 'Courier New', monospace;
form.comment-inline-form {
margin-top: 10px;
margin-left: 10px;
.comment-inline-form-submit {
margin-left: 525px;
.file-comments {
display: none;
.comment-inline-form .comment {
.comment-inline-form .comment-help{
padding: 0px 0px 2px 0px;
color: #666666;
font-size: 10px;
.comment-inline-form .comment-button{
padding-top:5px;
@@ -298,3 +298,41 @@ var ajaxPOST = function(url,postData,suc
var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
};
/** comments **/
var removeInlineForm = function(form) {
form.parentNode.removeChild(form);
var createInlineForm = function(parent_tr, f_path, line) {
var form = document.createElement('tr');
YUD.addClass(form, 'comment-form-inline');
var tmpl = YUD.get('comment-inline-form-template').innerHTML;
tmpl = tmpl.format(f_path, line);
form.innerHTML = '<td class="lineno new"></td>'+
'<td class="lineno old"></td>'+
'<td>{0}</td>'.format(tmpl);
// create event for hide button
form = new YAHOO.util.Element(form);
var form_hide_button = new YAHOO.util.Element(form.getElementsByClassName('hide-inline-form')[0]);
form_hide_button.on('click', function(e) {
var newtr = e.currentTarget.parentNode.parentNode.parentNode.parentNode.parentNode;
removeInlineForm(newtr);
YUD.removeClass(parent_tr, 'form-open');
});
return form
var getLineNo = function(tr) {
var line;
var o = tr.children[0].id.split('_');
var n = tr.children[1].id.split('_');
if (n.length == 2) {
line = n[1];
} else if (o.length == 2) {
line = o[1];
return line
@@ -114,38 +114,52 @@
${h.link_to_if(change!='removed',h.safe_unicode(filenode.path),h.url('files_home',repo_name=c.repo_name,
revision=filenode.changeset.raw_id,f_path=h.safe_unicode(filenode.path)))}
</span>
%if 1:
» <span>${h.link_to(_('diff'),
h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(filenode.path),diff2=cs2,diff1=cs1,diff='diff'))}</span>
» <span>${h.link_to(_('raw diff'),
h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(filenode.path),diff2=cs2,diff1=cs1,diff='raw'))}</span>
» <span>${h.link_to(_('download diff'),
h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(filenode.path),diff2=cs2,diff1=cs1,diff='download'))}</span>
%endif
</div>
<div class="code-body">
%if diff:
${diff|n}
%else:
${_('No changes in this file')}
<div class="full_f_path" path="${filenode.path}"></div>
%endfor
<%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
## template for inline comment form
${comment.comment_inline_form()}
<div class="comments">
<div class="comments-number">${len(c.comments)} comment(s)</div>
<div class="comments-number">${len(c.comments)} comment(s) (${c.inline_cnt} ${_('inline')})</div>
%for path, lines in c.inline_comments:
<div class="inline-comment-placeholder" path="${path} ">
% for line,comments in lines.iteritems():
<div class="inline-comment-placeholder-line" line="${line}">
%for co in comments:
${comment.comment_block(co)}
%for co in c.comments:
%if c.rhodecode_user.username != 'default':
<div class="comment-form">
${h.form(h.url('changeset_comment', repo_name=c.repo_name, revision=c.changeset.raw_id))}
<strong>Leave a comment</strong>
<strong>${_('Leave a comment')}</strong>
<div class="clearfix">
<div class="comment-help">${_('Comments parsed using RST syntax')}</div>
${h.textarea('text')}
@@ -167,7 +181,34 @@
n.parentNode.removeChild(n);
ajaxPOST(url,postData,success);
YUE.onDOMReady(function(){
YUE.on(YUQ('.line'),'mouseenter',function(e){
var tr = e.currentTarget;
if(YUD.hasClass(tr,'form-open') || YUD.hasClass(tr,'context')){
return
YUD.addClass(tr,'highlight');
YUE.on(YUQ('.line'),'mouseleave',function(e){
YUD.removeClass(e.currentTarget,'highlight');
YUE.on(YUQ('.line'),'click',function(e){
YUD.addClass(tr,'form-open');
var node = tr.parentNode.parentNode.parentNode.getElementsByClassName('full_f_path')[0];
var f_path = YUD.getAttribute(node,'path');
var lineno = getLineNo(tr);
var form = createInlineForm(tr, f_path, lineno);
YUD.insertAfter(form,tr);
})
</script>
</%def>
@@ -13,7 +13,7 @@
${h.short_id(co.revision)}
%if co.f_path:
${_(' in file ')}
${co.f_path}:L${co.line_no}
${co.f_path}:L ${co.line_no}
<span class="date">
${h.age(co.modified_at)}
@@ -28,4 +28,25 @@
${h.rst(co.text)|n}
<%def name="comment_inline_form()">
<div id='comment-inline-form-template' style="display:none">
<div class="comment-inline-form">
<div class="comment-help">${_('Commenting on line')} {1} ${_('comments parsed using RST syntax')}</div>
<div class="comment-button">
<input type="hidden" name="f_path" value="{0}">
<input type="hidden" name="line" value="{1}">
${h.submit('save', _('Comment'), class_='ui-button-small')}
${h.reset('hide-inline-form', _('Hide'), class_='ui-button-small hide-inline-form')}
${h.end_form()}
Status change: