Show More
@@ -98,13 +98,17 b' class ChangesetController(BaseRepoContro' | |||||
98 | c.cut_off = False # defines if cut off limit is reached |
|
98 | c.cut_off = False # defines if cut off limit is reached | |
99 |
|
99 | |||
100 | c.comments = [] |
|
100 | c.comments = [] | |
101 |
|
101 | c.inline_comments = [] | ||
|
102 | c.inline_cnt = 0 | |||
102 | # Iterate over ranges (default changeset view is always one changeset) |
|
103 | # Iterate over ranges (default changeset view is always one changeset) | |
103 | for changeset in c.cs_ranges: |
|
104 | for changeset in c.cs_ranges: | |
104 | c.comments.extend(ChangesetCommentsModel()\ |
|
105 | c.comments.extend(ChangesetCommentsModel()\ | |
105 | .get_comments(c.rhodecode_db_repo.repo_id, |
|
106 | .get_comments(c.rhodecode_db_repo.repo_id, | |
106 | changeset.raw_id)) |
|
107 | changeset.raw_id)) | |
107 |
|
108 | inlines = ChangesetCommentsModel()\ | ||
|
109 | .get_inline_comments(c.rhodecode_db_repo.repo_id, | |||
|
110 | changeset.raw_id) | |||
|
111 | c.inline_comments.extend(inlines) | |||
108 | c.changes[changeset.raw_id] = [] |
|
112 | c.changes[changeset.raw_id] = [] | |
109 | try: |
|
113 | try: | |
110 | changeset_parent = changeset.parents[0] |
|
114 | changeset_parent = changeset.parents[0] | |
@@ -199,6 +203,11 b' class ChangesetController(BaseRepoContro' | |||||
199 | c.changes[changeset.raw_id].append(('removed', node, None, |
|
203 | c.changes[changeset.raw_id].append(('removed', node, None, | |
200 | None, None, (0, 0))) |
|
204 | None, None, (0, 0))) | |
201 |
|
205 | |||
|
206 | # count inline comments | |||
|
207 | for path, lines in c.inline_comments: | |||
|
208 | for comments in lines.values(): | |||
|
209 | c.inline_cnt += len(comments) | |||
|
210 | ||||
202 | if len(c.cs_ranges) == 1: |
|
211 | if len(c.cs_ranges) == 1: | |
203 | c.changeset = c.cs_ranges[0] |
|
212 | c.changeset = c.cs_ranges[0] | |
204 | c.changes = c.changes[c.changeset.raw_id] |
|
213 | c.changes = c.changes[c.changeset.raw_id] |
@@ -49,18 +49,18 b' class ChangesetCommentsModel(BaseModel):' | |||||
49 | :param f_path: |
|
49 | :param f_path: | |
50 | :param line_no: |
|
50 | :param line_no: | |
51 | """ |
|
51 | """ | |
|
52 | if text: | |||
|
53 | comment = ChangesetComment() | |||
|
54 | comment.repo_id = repo_id | |||
|
55 | comment.user_id = user_id | |||
|
56 | comment.revision = revision | |||
|
57 | comment.text = text | |||
|
58 | comment.f_path = f_path | |||
|
59 | comment.line_no = line_no | |||
52 |
|
60 | |||
53 | comment = ChangesetComment() |
|
61 | self.sa.add(comment) | |
54 | comment.repo_id = repo_id |
|
62 | self.sa.commit() | |
55 | comment.user_id = user_id |
|
63 | return comment | |
56 | comment.revision = revision |
|
|||
57 | comment.text = text |
|
|||
58 | comment.f_path = f_path |
|
|||
59 | comment.line_no = line_no |
|
|||
60 |
|
||||
61 | self.sa.add(comment) |
|
|||
62 | self.sa.commit() |
|
|||
63 | return comment |
|
|||
64 |
|
64 | |||
65 | def delete(self, comment_id): |
|
65 | def delete(self, comment_id): | |
66 | """ |
|
66 | """ | |
@@ -81,13 +81,13 b' class ChangesetCommentsModel(BaseModel):' | |||||
81 | .filter(ChangesetComment.line_no == None)\ |
|
81 | .filter(ChangesetComment.line_no == None)\ | |
82 | .filter(ChangesetComment.f_path == None).all() |
|
82 | .filter(ChangesetComment.f_path == None).all() | |
83 |
|
83 | |||
84 |
def get_comments |
|
84 | def get_inline_comments(self, repo_id, revision): | |
85 | comments = self.sa.query(ChangesetComment)\ |
|
85 | comments = self.sa.query(ChangesetComment)\ | |
86 | .filter(ChangesetComment.repo_id == repo_id)\ |
|
86 | .filter(ChangesetComment.repo_id == repo_id)\ | |
87 |
.filter(ChangesetComment. |
|
87 | .filter(ChangesetComment.revision == revision).all() | |
88 | .filter(ChangesetComment.f_path == f_path).all() |
|
88 | ||
|
89 | paths = defaultdict(lambda:defaultdict(list)) | |||
89 |
|
90 | |||
90 | d = defaultdict(list) |
|
|||
91 | for co in comments: |
|
91 | for co in comments: | |
92 |
|
|
92 | paths[co.f_path][co.line_no].append(co) | |
93 |
return |
|
93 | return paths.items() |
@@ -1101,11 +1101,11 b' class ChangesetComment(Base, BaseModel):' | |||||
1101 | comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True) |
|
1101 | comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True) | |
1102 | repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False) |
|
1102 | repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False) | |
1103 | revision = Column('revision', String(40), nullable=False) |
|
1103 | revision = Column('revision', String(40), nullable=False) | |
1104 |
line_no = Column('line_no', |
|
1104 | line_no = Column('line_no', Unicode(10), nullable=True) | |
1105 |
f_path = Column('f_path', |
|
1105 | f_path = Column('f_path', Unicode(1000), nullable=True) | |
1106 | user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False) |
|
1106 | user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False) | |
1107 |
text = Column('text', |
|
1107 | text = Column('text', Unicode(25000), nullable=False) | |
1108 |
modified_at = Column('modified_at', DateTime( |
|
1108 | modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now) | |
1109 |
|
1109 | |||
1110 | author = relationship('User') |
|
1110 | author = relationship('User') | |
1111 | repo = relationship('Repository') |
|
1111 | repo = relationship('Repository') |
@@ -117,4 +117,13 b' cursor: pointer;' | |||||
117 | .line{ |
|
117 | .line{ | |
118 | padding:0; |
|
118 | padding:0; | |
119 | margin:0; |
|
119 | margin:0; | |
120 | } No newline at end of file |
|
120 | } | |
|
121 | ||||
|
122 | .line.highlight{ | |||
|
123 | background-color:#FFFFCC; | |||
|
124 | cursor: pointer; | |||
|
125 | background-image:url("../images/icons/comment_add.png"); | |||
|
126 | background-repeat:no-repeat; | |||
|
127 | background-position: right; | |||
|
128 | background-position: 100% 50%; | |||
|
129 | } |
@@ -1853,20 +1853,24 b' h3.files_location {' | |||||
1853 |
|
1853 | |||
1854 | .cs_files .changes { |
|
1854 | .cs_files .changes { | |
1855 | float: right; |
|
1855 | float: right; | |
|
1856 | color:#003367; | |||
|
1857 | ||||
1856 | } |
|
1858 | } | |
1857 |
|
1859 | |||
1858 | .cs_files .changes .added { |
|
1860 | .cs_files .changes .added { | |
1859 | background-color: #BBFFBB; |
|
1861 | background-color: #BBFFBB; | |
1860 | float: left; |
|
1862 | float: left; | |
1861 | text-align: center; |
|
1863 | text-align: center; | |
1862 |
font-size: 9 |
|
1864 | font-size: 9px; | |
|
1865 | padding: 2px 0px 2px 0px; | |||
1863 | } |
|
1866 | } | |
1864 |
|
1867 | |||
1865 | .cs_files .changes .deleted { |
|
1868 | .cs_files .changes .deleted { | |
1866 | background-color: #FF8888; |
|
1869 | background-color: #FF8888; | |
1867 | float: left; |
|
1870 | float: left; | |
1868 | text-align: center; |
|
1871 | text-align: center; | |
1869 |
font-size: 9 |
|
1872 | font-size: 9px; | |
|
1873 | padding: 2px 0px 2px 0px; | |||
1870 | } |
|
1874 | } | |
1871 |
|
1875 | |||
1872 | .cs_files .cs_added { |
|
1876 | .cs_files .cs_added { | |
@@ -3270,7 +3274,10 b' div.rst-block pre {' | |||||
3270 | .comments .comments-number{ |
|
3274 | .comments .comments-number{ | |
3271 | padding:0px 0px 10px 0px; |
|
3275 | padding:0px 0px 10px 0px; | |
3272 | font-weight: bold; |
|
3276 | font-weight: bold; | |
3273 | } |
|
3277 | color: #666; | |
|
3278 | font-size: 16px; | |||
|
3279 | } | |||
|
3280 | /** comment form **/ | |||
3274 |
|
3281 | |||
3275 | .comment-form .clearfix{ |
|
3282 | .comment-form .clearfix{ | |
3276 | background: #EEE; |
|
3283 | background: #EEE; | |
@@ -3332,3 +3339,59 b' form.comment-form {' | |||||
3332 | position: absolute; |
|
3339 | position: absolute; | |
3333 | right:40px; |
|
3340 | right:40px; | |
3334 | } |
|
3341 | } | |
|
3342 | ||||
|
3343 | ||||
|
3344 | ||||
|
3345 | /** comment inline form **/ | |||
|
3346 | ||||
|
3347 | .comment-inline-form .clearfix{ | |||
|
3348 | background: #EEE; | |||
|
3349 | -webkit-border-radius: 4px; | |||
|
3350 | -moz-border-radius: 4px; | |||
|
3351 | border-radius: 4px; | |||
|
3352 | padding: 5px; | |||
|
3353 | } | |||
|
3354 | ||||
|
3355 | div.comment-inline-form { | |||
|
3356 | margin-top: 5px; | |||
|
3357 | padding:2px 6px 8px 6px; | |||
|
3358 | } | |||
|
3359 | ||||
|
3360 | .comment-inline-form strong { | |||
|
3361 | display: block; | |||
|
3362 | margin-bottom: 15px; | |||
|
3363 | } | |||
|
3364 | ||||
|
3365 | .comment-inline-form textarea { | |||
|
3366 | width: 100%; | |||
|
3367 | height: 100px; | |||
|
3368 | font-family: 'Monaco', 'Courier', 'Courier New', monospace; | |||
|
3369 | } | |||
|
3370 | ||||
|
3371 | form.comment-inline-form { | |||
|
3372 | margin-top: 10px; | |||
|
3373 | margin-left: 10px; | |||
|
3374 | } | |||
|
3375 | ||||
|
3376 | .comment-inline-form-submit { | |||
|
3377 | margin-top: 5px; | |||
|
3378 | margin-left: 525px; | |||
|
3379 | } | |||
|
3380 | ||||
|
3381 | .file-comments { | |||
|
3382 | display: none; | |||
|
3383 | } | |||
|
3384 | ||||
|
3385 | .comment-inline-form .comment { | |||
|
3386 | margin-left: 10px; | |||
|
3387 | } | |||
|
3388 | ||||
|
3389 | .comment-inline-form .comment-help{ | |||
|
3390 | padding: 0px 0px 2px 0px; | |||
|
3391 | color: #666666; | |||
|
3392 | font-size: 10px; | |||
|
3393 | } | |||
|
3394 | ||||
|
3395 | .comment-inline-form .comment-button{ | |||
|
3396 | padding-top:5px; | |||
|
3397 | } No newline at end of file |
@@ -298,3 +298,41 b' var ajaxPOST = function(url,postData,suc' | |||||
298 | var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData); |
|
298 | var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData); | |
299 | }; |
|
299 | }; | |
300 |
|
300 | |||
|
301 | ||||
|
302 | /** comments **/ | |||
|
303 | var removeInlineForm = function(form) { | |||
|
304 | form.parentNode.removeChild(form); | |||
|
305 | } | |||
|
306 | ||||
|
307 | var createInlineForm = function(parent_tr, f_path, line) { | |||
|
308 | var form = document.createElement('tr'); | |||
|
309 | YUD.addClass(form, 'comment-form-inline'); | |||
|
310 | var tmpl = YUD.get('comment-inline-form-template').innerHTML; | |||
|
311 | tmpl = tmpl.format(f_path, line); | |||
|
312 | form.innerHTML = '<td class="lineno new"></td>'+ | |||
|
313 | '<td class="lineno old"></td>'+ | |||
|
314 | '<td>{0}</td>'.format(tmpl); | |||
|
315 | ||||
|
316 | // create event for hide button | |||
|
317 | form = new YAHOO.util.Element(form); | |||
|
318 | var form_hide_button = new YAHOO.util.Element(form.getElementsByClassName('hide-inline-form')[0]); | |||
|
319 | form_hide_button.on('click', function(e) { | |||
|
320 | var newtr = e.currentTarget.parentNode.parentNode.parentNode.parentNode.parentNode; | |||
|
321 | removeInlineForm(newtr); | |||
|
322 | YUD.removeClass(parent_tr, 'form-open'); | |||
|
323 | }); | |||
|
324 | return form | |||
|
325 | } | |||
|
326 | var getLineNo = function(tr) { | |||
|
327 | var line; | |||
|
328 | var o = tr.children[0].id.split('_'); | |||
|
329 | var n = tr.children[1].id.split('_'); | |||
|
330 | ||||
|
331 | if (n.length == 2) { | |||
|
332 | line = n[1]; | |||
|
333 | } else if (o.length == 2) { | |||
|
334 | line = o[1]; | |||
|
335 | } | |||
|
336 | ||||
|
337 | return line | |||
|
338 | } No newline at end of file |
@@ -114,38 +114,52 b'' | |||||
114 | ${h.link_to_if(change!='removed',h.safe_unicode(filenode.path),h.url('files_home',repo_name=c.repo_name, |
|
114 | ${h.link_to_if(change!='removed',h.safe_unicode(filenode.path),h.url('files_home',repo_name=c.repo_name, | |
115 | revision=filenode.changeset.raw_id,f_path=h.safe_unicode(filenode.path)))} |
|
115 | revision=filenode.changeset.raw_id,f_path=h.safe_unicode(filenode.path)))} | |
116 | </span> |
|
116 | </span> | |
117 | %if 1: |
|
|||
118 | » <span>${h.link_to(_('diff'), |
|
117 | » <span>${h.link_to(_('diff'), | |
119 | h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(filenode.path),diff2=cs2,diff1=cs1,diff='diff'))}</span> |
|
118 | h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(filenode.path),diff2=cs2,diff1=cs1,diff='diff'))}</span> | |
120 | » <span>${h.link_to(_('raw diff'), |
|
119 | » <span>${h.link_to(_('raw diff'), | |
121 | h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(filenode.path),diff2=cs2,diff1=cs1,diff='raw'))}</span> |
|
120 | h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(filenode.path),diff2=cs2,diff1=cs1,diff='raw'))}</span> | |
122 | » <span>${h.link_to(_('download diff'), |
|
121 | » <span>${h.link_to(_('download diff'), | |
123 | h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(filenode.path),diff2=cs2,diff1=cs1,diff='download'))}</span> |
|
122 | h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(filenode.path),diff2=cs2,diff1=cs1,diff='download'))}</span> | |
124 | %endif |
|
|||
125 | </div> |
|
123 | </div> | |
126 | </div> |
|
124 | </div> | |
127 |
<div class="code-body"> |
|
125 | <div class="code-body"> | |
128 | %if diff: |
|
126 | <div class="full_f_path" path="${filenode.path}"></div> | |
129 | ${diff|n} |
|
127 | %if diff: | |
130 | %else: |
|
128 | ${diff|n} | |
131 | ${_('No changes in this file')} |
|
129 | %else: | |
132 | %endif |
|
130 | ${_('No changes in this file')} | |
|
131 | %endif | |||
133 | </div> |
|
132 | </div> | |
134 | </div> |
|
133 | </div> | |
135 | %endif |
|
134 | %endif | |
136 | %endfor |
|
135 | %endfor | |
137 |
|
136 | |||
138 | <%namespace name="comment" file="/changeset/changeset_file_comment.html"/> |
|
137 | <%namespace name="comment" file="/changeset/changeset_file_comment.html"/> | |
|
138 | ## template for inline comment form | |||
|
139 | ${comment.comment_inline_form()} | |||
139 |
|
140 | |||
140 | <div class="comments"> |
|
141 | <div class="comments"> | |
141 | <div class="comments-number">${len(c.comments)} comment(s)</div> |
|
142 | <div class="comments-number">${len(c.comments)} comment(s) (${c.inline_cnt} ${_('inline')})</div> | |
|
143 | ||||
|
144 | %for path, lines in c.inline_comments: | |||
|
145 | <div class="inline-comment-placeholder" path="${path} "> | |||
|
146 | % for line,comments in lines.iteritems(): | |||
|
147 | <div class="inline-comment-placeholder-line" line="${line}"> | |||
|
148 | %for co in comments: | |||
|
149 | ${comment.comment_block(co)} | |||
|
150 | %endfor | |||
|
151 | </div> | |||
|
152 | %endfor | |||
|
153 | </div> | |||
|
154 | %endfor | |||
|
155 | ||||
142 | %for co in c.comments: |
|
156 | %for co in c.comments: | |
143 | ${comment.comment_block(co)} |
|
157 | ${comment.comment_block(co)} | |
144 | %endfor |
|
158 | %endfor | |
145 | %if c.rhodecode_user.username != 'default': |
|
159 | %if c.rhodecode_user.username != 'default': | |
146 | <div class="comment-form"> |
|
160 | <div class="comment-form"> | |
147 | ${h.form(h.url('changeset_comment', repo_name=c.repo_name, revision=c.changeset.raw_id))} |
|
161 | ${h.form(h.url('changeset_comment', repo_name=c.repo_name, revision=c.changeset.raw_id))} | |
148 | <strong>Leave a comment</strong> |
|
162 | <strong>${_('Leave a comment')}</strong> | |
149 | <div class="clearfix"> |
|
163 | <div class="clearfix"> | |
150 | <div class="comment-help">${_('Comments parsed using RST syntax')}</div> |
|
164 | <div class="comment-help">${_('Comments parsed using RST syntax')}</div> | |
151 | ${h.textarea('text')} |
|
165 | ${h.textarea('text')} | |
@@ -167,7 +181,34 b'' | |||||
167 | n.parentNode.removeChild(n); |
|
181 | n.parentNode.removeChild(n); | |
168 | } |
|
182 | } | |
169 | ajaxPOST(url,postData,success); |
|
183 | ajaxPOST(url,postData,success); | |
170 |
} |
|
184 | } | |
|
185 | ||||
|
186 | YUE.onDOMReady(function(){ | |||
|
187 | YUE.on(YUQ('.line'),'mouseenter',function(e){ | |||
|
188 | var tr = e.currentTarget; | |||
|
189 | if(YUD.hasClass(tr,'form-open') || YUD.hasClass(tr,'context')){ | |||
|
190 | return | |||
|
191 | } | |||
|
192 | YUD.addClass(tr,'highlight'); | |||
|
193 | }); | |||
|
194 | YUE.on(YUQ('.line'),'mouseleave',function(e){ | |||
|
195 | YUD.removeClass(e.currentTarget,'highlight'); | |||
|
196 | }); | |||
|
197 | ||||
|
198 | YUE.on(YUQ('.line'),'click',function(e){ | |||
|
199 | var tr = e.currentTarget; | |||
|
200 | if(YUD.hasClass(tr,'form-open') || YUD.hasClass(tr,'context')){ | |||
|
201 | return | |||
|
202 | } | |||
|
203 | YUD.addClass(tr,'form-open'); | |||
|
204 | var node = tr.parentNode.parentNode.parentNode.getElementsByClassName('full_f_path')[0]; | |||
|
205 | var f_path = YUD.getAttribute(node,'path'); | |||
|
206 | var lineno = getLineNo(tr); | |||
|
207 | var form = createInlineForm(tr, f_path, lineno); | |||
|
208 | YUD.insertAfter(form,tr); | |||
|
209 | }) | |||
|
210 | }) | |||
|
211 | ||||
171 | </script> |
|
212 | </script> | |
172 | </div> |
|
213 | </div> | |
173 | </%def> |
|
214 | </%def> |
@@ -13,7 +13,7 b'' | |||||
13 | ${h.short_id(co.revision)} |
|
13 | ${h.short_id(co.revision)} | |
14 | %if co.f_path: |
|
14 | %if co.f_path: | |
15 | ${_(' in file ')} |
|
15 | ${_(' in file ')} | |
16 | ${co.f_path}:L${co.line_no} |
|
16 | ${co.f_path}:L ${co.line_no} | |
17 | %endif |
|
17 | %endif | |
18 | <span class="date"> |
|
18 | <span class="date"> | |
19 | ${h.age(co.modified_at)} |
|
19 | ${h.age(co.modified_at)} | |
@@ -28,4 +28,25 b'' | |||||
28 | ${h.rst(co.text)|n} |
|
28 | ${h.rst(co.text)|n} | |
29 | </div> |
|
29 | </div> | |
30 | </div> |
|
30 | </div> | |
|
31 | </%def> | |||
|
32 | ||||
|
33 | ||||
|
34 | ||||
|
35 | <%def name="comment_inline_form()"> | |||
|
36 | <div id='comment-inline-form-template' style="display:none"> | |||
|
37 | <div class="comment-inline-form"> | |||
|
38 | ${h.form(h.url('changeset_comment', repo_name=c.repo_name, revision=c.changeset.raw_id))} | |||
|
39 | <div class="clearfix"> | |||
|
40 | <div class="comment-help">${_('Commenting on line')} {1} ${_('comments parsed using RST syntax')}</div> | |||
|
41 | ${h.textarea('text')} | |||
|
42 | </div> | |||
|
43 | <div class="comment-button"> | |||
|
44 | <input type="hidden" name="f_path" value="{0}"> | |||
|
45 | <input type="hidden" name="line" value="{1}"> | |||
|
46 | ${h.submit('save', _('Comment'), class_='ui-button-small')} | |||
|
47 | ${h.reset('hide-inline-form', _('Hide'), class_='ui-button-small hide-inline-form')} | |||
|
48 | </div> | |||
|
49 | ${h.end_form()} | |||
|
50 | </div> | |||
|
51 | </div> | |||
31 | </%def> No newline at end of file |
|
52 | </%def> |
General Comments 0
You need to be logged in to leave comments.
Login now