Show More
@@ -1,93 +1,95 | |||
|
1 | 1 | # -*- coding: utf-8 -*- |
|
2 | 2 | """ |
|
3 | 3 | rhodecode.model.comment |
|
4 | 4 | ~~~~~~~~~~~~~~~~~~~~~~~ |
|
5 | 5 | |
|
6 | 6 | comments model for RhodeCode |
|
7 | 7 | |
|
8 | 8 | :created_on: Nov 11, 2011 |
|
9 | 9 | :author: marcink |
|
10 | 10 | :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> |
|
11 | 11 | :license: GPLv3, see COPYING for more details. |
|
12 | 12 | """ |
|
13 | 13 | # This program is free software: you can redistribute it and/or modify |
|
14 | 14 | # it under the terms of the GNU General Public License as published by |
|
15 | 15 | # the Free Software Foundation, either version 3 of the License, or |
|
16 | 16 | # (at your option) any later version. |
|
17 | 17 | # |
|
18 | 18 | # This program is distributed in the hope that it will be useful, |
|
19 | 19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
20 | 20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
21 | 21 | # GNU General Public License for more details. |
|
22 | 22 | # |
|
23 | 23 | # You should have received a copy of the GNU General Public License |
|
24 | 24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
25 | 25 | |
|
26 | 26 | |
|
27 | 27 | import logging |
|
28 | 28 | import traceback |
|
29 | 29 | |
|
30 | 30 | from rhodecode.model import BaseModel |
|
31 | 31 | from rhodecode.model.db import ChangesetComment |
|
32 | 32 | from sqlalchemy.util.compat import defaultdict |
|
33 | 33 | |
|
34 | 34 | log = logging.getLogger(__name__) |
|
35 | 35 | |
|
36 | 36 | |
|
37 | 37 | class ChangesetCommentsModel(BaseModel): |
|
38 | 38 | |
|
39 | 39 | |
|
40 | 40 | def create(self, text, repo_id, user_id, revision, f_path=None, |
|
41 | 41 | line_no=None): |
|
42 | 42 | """ |
|
43 | 43 | Creates new comment for changeset |
|
44 | 44 | |
|
45 | 45 | :param text: |
|
46 | 46 | :param repo_id: |
|
47 | 47 | :param user_id: |
|
48 | 48 | :param revision: |
|
49 | 49 | :param f_path: |
|
50 | 50 | :param line_no: |
|
51 | 51 | """ |
|
52 | 52 | if text: |
|
53 | 53 | comment = ChangesetComment() |
|
54 | 54 | comment.repo_id = repo_id |
|
55 | 55 | comment.user_id = user_id |
|
56 | 56 | comment.revision = revision |
|
57 | 57 | comment.text = text |
|
58 | 58 | comment.f_path = f_path |
|
59 | 59 | comment.line_no = line_no |
|
60 | 60 | |
|
61 | 61 | self.sa.add(comment) |
|
62 | 62 | self.sa.commit() |
|
63 | 63 | return comment |
|
64 | 64 | |
|
65 | 65 | def delete(self, comment_id): |
|
66 | 66 | """ |
|
67 | 67 | Deletes given comment |
|
68 | 68 | |
|
69 | 69 | :param comment_id: |
|
70 | 70 | """ |
|
71 | 71 | comment = ChangesetComment.get(comment_id) |
|
72 | 72 | self.sa.delete(comment) |
|
73 | 73 | self.sa.commit() |
|
74 | 74 | return comment |
|
75 | 75 | |
|
76 | 76 | |
|
77 | 77 | def get_comments(self, repo_id, revision): |
|
78 | 78 | return ChangesetComment.query()\ |
|
79 | 79 | .filter(ChangesetComment.repo_id == repo_id)\ |
|
80 | 80 | .filter(ChangesetComment.revision == revision)\ |
|
81 | 81 | .filter(ChangesetComment.line_no == None)\ |
|
82 | 82 | .filter(ChangesetComment.f_path == None).all() |
|
83 | 83 | |
|
84 | 84 | def get_inline_comments(self, repo_id, revision): |
|
85 | 85 | comments = self.sa.query(ChangesetComment)\ |
|
86 | 86 | .filter(ChangesetComment.repo_id == repo_id)\ |
|
87 |
.filter(ChangesetComment.revision == revision) |
|
|
87 | .filter(ChangesetComment.revision == revision)\ | |
|
88 | .filter(ChangesetComment.line_no != None)\ | |
|
89 | .filter(ChangesetComment.f_path != None).all() | |
|
88 | 90 | |
|
89 | 91 | paths = defaultdict(lambda:defaultdict(list)) |
|
90 | 92 | |
|
91 | 93 | for co in comments: |
|
92 | 94 | paths[co.f_path][co.line_no].append(co) |
|
93 | 95 | return paths.items() |
@@ -1,214 +1,216 | |||
|
1 | 1 | ## -*- coding: utf-8 -*- |
|
2 | 2 | |
|
3 | 3 | <%inherit file="/base/base.html"/> |
|
4 | 4 | |
|
5 | 5 | <%def name="title()"> |
|
6 | 6 | ${c.repo_name} ${_('Changeset')} - r${c.changeset.revision}:${h.short_id(c.changeset.raw_id)} - ${c.rhodecode_name} |
|
7 | 7 | </%def> |
|
8 | 8 | |
|
9 | 9 | <%def name="breadcrumbs_links()"> |
|
10 | 10 | ${h.link_to(u'Home',h.url('/'))} |
|
11 | 11 | » |
|
12 | 12 | ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))} |
|
13 | 13 | » |
|
14 | 14 | ${_('Changeset')} - r${c.changeset.revision}:${h.short_id(c.changeset.raw_id)} |
|
15 | 15 | </%def> |
|
16 | 16 | |
|
17 | 17 | <%def name="page_nav()"> |
|
18 | 18 | ${self.menu('changelog')} |
|
19 | 19 | </%def> |
|
20 | 20 | |
|
21 | 21 | <%def name="main()"> |
|
22 | 22 | <div class="box"> |
|
23 | 23 | <!-- box / title --> |
|
24 | 24 | <div class="title"> |
|
25 | 25 | ${self.breadcrumbs()} |
|
26 | 26 | </div> |
|
27 | 27 | <div class="table"> |
|
28 | 28 | <div class="diffblock"> |
|
29 | 29 | <div class="code-header"> |
|
30 | 30 | <div> |
|
31 | 31 | ${_('Changeset')} - r${c.changeset.revision}:${h.short_id(c.changeset.raw_id)} |
|
32 | 32 | » <span>${h.link_to(_('raw diff'), |
|
33 | 33 | h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id,diff='show'))}</span> |
|
34 | 34 | » <span>${h.link_to(_('download diff'), |
|
35 | 35 | h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id,diff='download'))}</span> |
|
36 | 36 | </div> |
|
37 | 37 | </div> |
|
38 | 38 | </div> |
|
39 | 39 | <div id="changeset_content"> |
|
40 | 40 | <div class="container"> |
|
41 | 41 | <div class="left"> |
|
42 | 42 | <div class="date">${_('commit')} ${c.changeset.revision}: ${h.short_id(c.changeset.raw_id)}@${c.changeset.date}</div> |
|
43 | 43 | <div class="author"> |
|
44 | 44 | <div class="gravatar"> |
|
45 | 45 | <img alt="gravatar" src="${h.gravatar_url(h.email(c.changeset.author),20)}"/> |
|
46 | 46 | </div> |
|
47 | 47 | <span>${h.person(c.changeset.author)}</span><br/> |
|
48 | 48 | <span><a href="mailto:${h.email_or_none(c.changeset.author)}">${h.email_or_none(c.changeset.author)}</a></span><br/> |
|
49 | 49 | </div> |
|
50 | 50 | <div class="message">${h.link_to(h.wrap_paragraphs(c.changeset.message),h.url('changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id))}</div> |
|
51 | 51 | </div> |
|
52 | 52 | <div class="right"> |
|
53 | 53 | <div class="changes"> |
|
54 | 54 | % if len(c.changeset.affected_files) <= c.affected_files_cut_off: |
|
55 | 55 | <span class="removed" title="${_('removed')}">${len(c.changeset.removed)}</span> |
|
56 | 56 | <span class="changed" title="${_('changed')}">${len(c.changeset.changed)}</span> |
|
57 | 57 | <span class="added" title="${_('added')}">${len(c.changeset.added)}</span> |
|
58 | 58 | % else: |
|
59 | 59 | <span class="removed" title="${_('affected %s files') % len(c.changeset.affected_files)}">!</span> |
|
60 | 60 | <span class="changed" title="${_('affected %s files') % len(c.changeset.affected_files)}">!</span> |
|
61 | 61 | <span class="added" title="${_('affected %s files') % len(c.changeset.affected_files)}">!</span> |
|
62 | 62 | % endif |
|
63 | 63 | </div> |
|
64 | 64 | %if len(c.changeset.parents)>1: |
|
65 | 65 | <div class="merge"> |
|
66 | 66 | ${_('merge')}<img alt="merge" src="${h.url('/images/icons/arrow_join.png')}"/> |
|
67 | 67 | </div> |
|
68 | 68 | %endif |
|
69 | 69 | |
|
70 | 70 | %if c.changeset.parents: |
|
71 | 71 | %for p_cs in reversed(c.changeset.parents): |
|
72 | 72 | <div class="parent">${_('Parent')} ${p_cs.revision}: ${h.link_to(h.short_id(p_cs.raw_id), |
|
73 | 73 | h.url('changeset_home',repo_name=c.repo_name,revision=p_cs.raw_id),title=p_cs.message)} |
|
74 | 74 | </div> |
|
75 | 75 | %endfor |
|
76 | 76 | %else: |
|
77 | 77 | <div class="parent">${_('No parents')}</div> |
|
78 | 78 | %endif |
|
79 | 79 | <span class="logtags"> |
|
80 | 80 | <span class="branchtag" title="${'%s %s' % (_('branch'),c.changeset.branch)}"> |
|
81 | 81 | ${h.link_to(c.changeset.branch,h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id))}</span> |
|
82 | 82 | %for tag in c.changeset.tags: |
|
83 | 83 | <span class="tagtag" title="${'%s %s' % (_('tag'),tag)}"> |
|
84 | 84 | ${h.link_to(tag,h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id))}</span> |
|
85 | 85 | %endfor |
|
86 | 86 | </span> |
|
87 | 87 | </div> |
|
88 | 88 | </div> |
|
89 | 89 | <span style="font-size:1.1em;font-weight: bold"> |
|
90 | 90 | ${_('%s files affected with %s additions and %s deletions.') % (len(c.changeset.affected_files),c.lines_added,c.lines_deleted)} |
|
91 | 91 | </span> |
|
92 | 92 | <div class="cs_files"> |
|
93 | 93 | %for change,filenode,diff,cs1,cs2,stat in c.changes: |
|
94 | 94 | <div class="cs_${change}"> |
|
95 | 95 | <div class="node">${h.link_to(h.safe_unicode(filenode.path),h.url.current(anchor='C-%s-%s' % (h.short_id(filenode.changeset.raw_id),h.safeid(h.safe_unicode(filenode.path)))))}</div> |
|
96 | 96 | <div class="changes">${h.fancy_file_stats(stat)}</div> |
|
97 | 97 | </div> |
|
98 | 98 | %endfor |
|
99 | 99 | % if c.cut_off: |
|
100 | 100 | ${_('Changeset was too big and was cut off...')} |
|
101 | 101 | % endif |
|
102 | 102 | </div> |
|
103 | 103 | </div> |
|
104 | 104 | |
|
105 | 105 | </div> |
|
106 | 106 | |
|
107 | 107 | %for change,filenode,diff,cs1,cs2,stat in c.changes: |
|
108 | 108 | %if change !='removed': |
|
109 | 109 | <div style="clear:both;height:10px"></div> |
|
110 | 110 | <div class="diffblock margined"> |
|
111 | 111 | <div id="${'C-%s-%s' % (h.short_id(filenode.changeset.raw_id),h.safeid(h.safe_unicode(filenode.path)))}" class="code-header"> |
|
112 | 112 | <div class="changeset_header"> |
|
113 | 113 | <span class="changeset_file"> |
|
114 | 114 | ${h.link_to_if(change!='removed',h.safe_unicode(filenode.path),h.url('files_home',repo_name=c.repo_name, |
|
115 | 115 | revision=filenode.changeset.raw_id,f_path=h.safe_unicode(filenode.path)))} |
|
116 | 116 | </span> |
|
117 | 117 | » <span>${h.link_to(_('diff'), |
|
118 | 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> |
|
119 | 119 | » <span>${h.link_to(_('raw diff'), |
|
120 | 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> |
|
121 | 121 | » <span>${h.link_to(_('download diff'), |
|
122 | 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> |
|
123 | 123 | </div> |
|
124 | 124 | </div> |
|
125 | 125 | <div class="code-body"> |
|
126 | 126 | <div class="full_f_path" path="${filenode.path}"></div> |
|
127 | 127 | %if diff: |
|
128 | 128 | ${diff|n} |
|
129 | 129 | %else: |
|
130 | 130 | ${_('No changes in this file')} |
|
131 | 131 | %endif |
|
132 | 132 | </div> |
|
133 | 133 | </div> |
|
134 | 134 | %endif |
|
135 | 135 | %endfor |
|
136 | 136 | |
|
137 | 137 | <%namespace name="comment" file="/changeset/changeset_file_comment.html"/> |
|
138 | 138 | ## template for inline comment form |
|
139 | 139 | ${comment.comment_inline_form()} |
|
140 | 140 | |
|
141 | 141 | <div class="comments"> |
|
142 | 142 | <div class="comments-number">${len(c.comments)} comment(s) (${c.inline_cnt} ${_('inline')})</div> |
|
143 | 143 | |
|
144 | 144 | %for path, lines in c.inline_comments: |
|
145 | 145 | <div class="inline-comment-placeholder" path="${path} "> |
|
146 | 146 | % for line,comments in lines.iteritems(): |
|
147 | 147 | <div class="inline-comment-placeholder-line" line="${line}"> |
|
148 | 148 | %for co in comments: |
|
149 | 149 | ${comment.comment_block(co)} |
|
150 | 150 | %endfor |
|
151 | 151 | </div> |
|
152 | 152 | %endfor |
|
153 | 153 | </div> |
|
154 | 154 | %endfor |
|
155 | 155 | |
|
156 | 156 | %for co in c.comments: |
|
157 | 157 | ${comment.comment_block(co)} |
|
158 | 158 | %endfor |
|
159 | 159 | %if c.rhodecode_user.username != 'default': |
|
160 | 160 | <div class="comment-form"> |
|
161 | 161 | ${h.form(h.url('changeset_comment', repo_name=c.repo_name, revision=c.changeset.raw_id))} |
|
162 | 162 | <strong>${_('Leave a comment')}</strong> |
|
163 | 163 | <div class="clearfix"> |
|
164 |
<div class="comment-help"> |
|
|
164 | <div class="comment-help"> | |
|
165 | ${_('Comments parsed using')} <a href="${h.url('rst_help')}">RST</a> ${_('syntax')} | |
|
166 | </div> | |
|
165 | 167 | ${h.textarea('text')} |
|
166 | 168 | </div> |
|
167 | 169 | <div class="comment-button"> |
|
168 | 170 | ${h.submit('save', _('Comment'), class_='ui-button')} |
|
169 | 171 | </div> |
|
170 | 172 | ${h.end_form()} |
|
171 | 173 | </div> |
|
172 | 174 | %endif |
|
173 | 175 | </div> |
|
174 | 176 | <script type="text/javascript"> |
|
175 | 177 | var deleteComment = function(comment_id){ |
|
176 | 178 | |
|
177 | 179 | var url = "${url('changeset_comment_delete',repo_name=c.repo_name,comment_id='__COMMENT_ID__')}".replace('__COMMENT_ID__',comment_id); |
|
178 | 180 | var postData = '_method=delete'; |
|
179 | 181 | var success = function(o){ |
|
180 | 182 | var n = YUD.get('comment-'+comment_id); |
|
181 | 183 | n.parentNode.removeChild(n); |
|
182 | 184 | } |
|
183 | 185 | ajaxPOST(url,postData,success); |
|
184 | 186 | } |
|
185 | 187 | |
|
186 | 188 | YUE.onDOMReady(function(){ |
|
187 | 189 | YUE.on(YUQ('.line'),'mouseenter',function(e){ |
|
188 | 190 | var tr = e.currentTarget; |
|
189 | 191 | if(YUD.hasClass(tr,'form-open') || YUD.hasClass(tr,'context')){ |
|
190 | 192 | return |
|
191 | 193 | } |
|
192 | 194 | YUD.addClass(tr,'highlight'); |
|
193 | 195 | }); |
|
194 | 196 | YUE.on(YUQ('.line'),'mouseleave',function(e){ |
|
195 | 197 | YUD.removeClass(e.currentTarget,'highlight'); |
|
196 | 198 | }); |
|
197 | 199 | |
|
198 | 200 | YUE.on(YUQ('.line'),'click',function(e){ |
|
199 | 201 | var tr = e.currentTarget; |
|
200 | 202 | if(YUD.hasClass(tr,'form-open') || YUD.hasClass(tr,'context')){ |
|
201 | 203 | return |
|
202 | 204 | } |
|
203 | 205 | YUD.addClass(tr,'form-open'); |
|
204 | 206 | var node = tr.parentNode.parentNode.parentNode.getElementsByClassName('full_f_path')[0]; |
|
205 | 207 | var f_path = YUD.getAttribute(node,'path'); |
|
206 | 208 | var lineno = getLineNo(tr); |
|
207 | 209 | var form = createInlineForm(tr, f_path, lineno); |
|
208 | 210 | YUD.insertAfter(form,tr); |
|
209 | 211 | }) |
|
210 | 212 | }) |
|
211 | 213 | |
|
212 | 214 | </script> |
|
213 | 215 | </div> |
|
214 | 216 | </%def> |
General Comments 0
You need to be logged in to leave comments.
Login now