##// END OF EJS Templates
code garden for changeset ranges and comments...
marcink -
r1787:d4a7b6c8 beta
parent child Browse files
Show More
@@ -0,0 +1,50 b''
1 ## -*- coding: utf-8 -*-
2 ##usage:
3 ## <%namespace name="diff_block" file="/changeset/diff_block.html"/>
4 ## ${diff_block.diff_block(changes)}
5 ##
6 <%def name="diff_block(changes)">
7
8 %for change,filenode,diff,cs1,cs2,stat in changes:
9 %if change !='removed':
10 <div id="${h.FID(filenode.changeset.raw_id,filenode.path)}" style="clear:both;height:90px;margin-top:-60px"></div>
11 <div class="diffblock margined comm">
12 <div class="code-header">
13 <div class="changeset_header">
14 <div class="changeset_file">
15 ${h.link_to_if(change!='removed',h.safe_unicode(filenode.path),h.url('files_home',repo_name=c.repo_name,
16 revision=filenode.changeset.raw_id,f_path=h.safe_unicode(filenode.path)))}
17 </div>
18 <div class="diff-menu-wrapper">
19 <img class="diff-menu-activate" style="margin-bottom:-6px;cursor: pointer" alt="diff-menu" src="${h.url('/images/icons/script_gear.png')}" />
20 <div class="diff-menu" style="display:none">
21 <ul>
22 <li>${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'))}</li>
23 <li>${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'))}</li>
24 <li>${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'))}</li>
25 <li>${c.ignorews_url(h.FID(filenode.changeset.raw_id,filenode.path))}</li>
26 <li>${c.context_url(h.FID(filenode.changeset.raw_id,filenode.path))}</li>
27 </ul>
28 </div>
29 </div>
30 <span style="float:right;margin-top:-3px">
31 <label>
32 ${_('show inline comments')}
33 ${h.checkbox('',checked="checked",class_="show-inline-comments",id_for=h.FID(filenode.changeset.raw_id,filenode.path))}
34 </label>
35 </span>
36 </div>
37 </div>
38 <div class="code-body">
39 <div class="full_f_path" path="${h.safe_unicode(filenode.path)}"></div>
40 %if diff:
41 ${diff|n}
42 %else:
43 ${_('No changes in this file')}
44 %endif
45 </div>
46 </div>
47 %endif
48 %endfor
49
50 </%def> No newline at end of file
@@ -54,6 +54,7 b' def anchor_url(revision,path):'
54 fid = h.FID(revision, path)
54 fid = h.FID(revision, path)
55 return h.url.current(anchor=fid,**request.GET)
55 return h.url.current(anchor=fid, **request.GET)
56
56
57
57 def get_ignore_ws(fid, GET):
58 def get_ignore_ws(fid, GET):
58 ig_ws_global = request.GET.get('ignorews')
59 ig_ws_global = request.GET.get('ignorews')
59 ig_ws = filter(lambda k:k.startswith('WS'),GET.getall(fid))
60 ig_ws = filter(lambda k: k.startswith('WS'), GET.getall(fid))
@@ -64,6 +65,7 b' def get_ignore_ws(fid, GET):'
64 pass
65 pass
65 return ig_ws_global
66 return ig_ws_global
66
67
68
67 def _ignorews_url(fileid=None):
69 def _ignorews_url(fileid=None):
68
70
69 params = defaultdict(list)
71 params = defaultdict(list)
@@ -92,6 +94,7 b' def _ignorews_url(fileid=None):'
92 params['anchor'] = fileid
94 params['anchor'] = fileid
93 return h.link_to(lbl, h.url.current(**params))
95 return h.link_to(lbl, h.url.current(**params))
94
96
97
95 def get_line_ctx(fid, GET):
98 def get_line_ctx(fid, GET):
96 ln_ctx_global = request.GET.get('context')
99 ln_ctx_global = request.GET.get('context')
97 ln_ctx = filter(lambda k:k.startswith('C'),GET.getall(fid))
100 ln_ctx = filter(lambda k: k.startswith('C'), GET.getall(fid))
@@ -106,6 +109,7 b' def get_line_ctx(fid, GET):'
106 except:
109 except:
107 return
110 return
108
111
112
109 def _context_url(fileid=None):
113 def _context_url(fileid=None):
110 """
114 """
111 Generates url for context lines
115 Generates url for context lines
@@ -140,14 +144,16 b' def _context_url(fileid=None):'
140 params['anchor'] = fileid
144 params['anchor'] = fileid
141 return h.link_to(lbl, h.url.current(**params))
145 return h.link_to(lbl, h.url.current(**params))
142
146
147
143 def wrap_to_table(str_):
148 def wrap_to_table(str_):
144 return '''<table class="code-difftable">
149 return '''<table class="code-difftable">
145 <tr class="line">
150 <tr class="line no-comment">
146 <td class="lineno new"></td>
151 <td class="lineno new"></td>
147 <td class="code"><pre>%s</pre></td>
152 <td class="code no-comment"><pre>%s</pre></td>
148 </tr>
153 </tr>
149 </table>''' % str_
154 </table>''' % str_
150
155
156
151 class ChangesetController(BaseRepoController):
157 class ChangesetController(BaseRepoController):
152
158
153 @LoginRequired()
159 @LoginRequired()
@@ -165,9 +171,10 b' class ChangesetController(BaseRepoContro'
165
171
166 #get ranges of revisions if preset
172 #get ranges of revisions if preset
167 rev_range = revision.split('...')[:2]
173 rev_range = revision.split('...')[:2]
168
174 enable_comments = True
169 try:
175 try:
170 if len(rev_range) == 2:
176 if len(rev_range) == 2:
177 enable_comments = False
171 rev_start = rev_range[0]
178 rev_start = rev_range[0]
172 rev_end = rev_range[1]
179 rev_end = rev_range[1]
173 rev_ranges = c.rhodecode_repo.get_changesets(start=rev_start,
180 rev_ranges = c.rhodecode_repo.get_changesets(start=rev_start,
@@ -233,7 +240,7 b' class ChangesetController(BaseRepoContro'
233 d = diffs.DiffProcessor(f_gitdiff, format='gitdiff')
240 d = diffs.DiffProcessor(f_gitdiff, format='gitdiff')
234
241
235 st = d.stat()
242 st = d.stat()
236 diff = d.as_html()
243 diff = d.as_html(enable_comments=enable_comments)
237
244
238 else:
245 else:
239 diff = wrap_to_table(_('Changeset is to big and '
246 diff = wrap_to_table(_('Changeset is to big and '
@@ -281,7 +288,7 b' class ChangesetController(BaseRepoContro'
281 'and was cut off, see '
288 'and was cut off, see '
282 'raw diff instead'))
289 'raw diff instead'))
283 else:
290 else:
284 diff = d.as_html()
291 diff = d.as_html(enable_comments=enable_comments)
285
292
286 if diff:
293 if diff:
287 c.sum_removed += len(diff)
294 c.sum_removed += len(diff)
@@ -404,4 +411,3 b' class ChangesetController(BaseRepoContro'
404 return True
411 return True
405 else:
412 else:
406 raise HTTPForbidden()
413 raise HTTPForbidden()
407
@@ -114,15 +114,17 b' class DiffProcessor(object):'
114 try:
114 try:
115 if line1.startswith('--- ') and line2.startswith('+++ '):
115 if line1.startswith('--- ') and line2.startswith('+++ '):
116 l1 = line1[4:].split(None, 1)
116 l1 = line1[4:].split(None, 1)
117 old_filename = l1[0].lstrip('a/') if len(l1) >= 1 else None
117 old_filename = (l1[0].replace('a/', '', 1)
118 if len(l1) >= 1 else None)
118 old_rev = l1[1] if len(l1) == 2 else 'old'
119 old_rev = l1[1] if len(l1) == 2 else 'old'
119
120
120 l2 = line2[4:].split(None, 1)
121 l2 = line2[4:].split(None, 1)
121 new_filename = l2[0].lstrip('b/') if len(l1) >= 1 else None
122 new_filename = (l2[0].replace('b/', '', 1)
123 if len(l1) >= 1 else None)
122 new_rev = l2[1] if len(l2) == 2 else 'new'
124 new_rev = l2[1] if len(l2) == 2 else 'new'
123
125
124 filename = old_filename if (old_filename !=
126 filename = (old_filename
125 'dev/null') else new_filename
127 if old_filename != '/dev/null' else new_filename)
126
128
127 return filename, new_rev, old_rev
129 return filename, new_rev, old_rev
128 except (ValueError, IndexError):
130 except (ValueError, IndexError):
@@ -359,7 +361,7 b' class DiffProcessor(object):'
359
361
360 def as_html(self, table_class='code-difftable', line_class='line',
362 def as_html(self, table_class='code-difftable', line_class='line',
361 new_lineno_class='lineno old', old_lineno_class='lineno new',
363 new_lineno_class='lineno old', old_lineno_class='lineno new',
362 code_class='code'):
364 code_class='code', enable_comments=False):
363 """
365 """
364 Return udiff as html table with customized css classes
366 Return udiff as html table with customized css classes
365 """
367 """
@@ -429,8 +431,10 b' class DiffProcessor(object):'
429 ###########################################################
431 ###########################################################
430 # CODE
432 # CODE
431 ###########################################################
433 ###########################################################
432 _html.append('''\t<td class="%(code_class)s">''' \
434 comments = '' if enable_comments else 'no-comment'
433 % {'code_class': code_class})
435 _html.append('''\t<td class="%(code_class)s %(in-comments)s">''' \
436 % {'code_class': code_class,
437 'in-comments': comments})
434 _html.append('''\n\t\t<pre>%(code)s</pre>\n''' \
438 _html.append('''\n\t\t<pre>%(code)s</pre>\n''' \
435 % {'code': change['line']})
439 % {'code': change['line']})
436 _html.append('''\t</td>''')
440 _html.append('''\t</td>''')
@@ -3898,17 +3898,19 b' form.comment-inline-form {'
3898 text-decoration: none !important;
3898 text-decoration: none !important;
3899 }
3899 }
3900 .notification-header{
3900 .notification-header{
3901
3901 padding-top:6px;
3902 }
3902 }
3903 .notification-header .desc{
3903 .notification-header .desc{
3904 font-size: 16px;
3904 font-size: 16px;
3905 height: 24px;
3905 height: 24px;
3906 padding-top: 6px;
3907 float: left
3906 float: left
3908 }
3907 }
3909 .notification-list .container.unread{
3908 .notification-list .container.unread{
3910
3909
3911 }
3910 }
3911 .notification-header .gravatar{
3912
3913 }
3912 .notification-header .desc.unread{
3914 .notification-header .desc.unread{
3913 font-weight: bold;
3915 font-weight: bold;
3914 font-size: 17px;
3916 font-size: 17px;
@@ -4069,4 +4071,10 b' table.code-difftable .code pre{'
4069 background-repeat:no-repeat !important;
4071 background-repeat:no-repeat !important;
4070 background-position: right !important;
4072 background-position: right !important;
4071 background-position: 0% 50% !important;
4073 background-position: 0% 50% !important;
4074 }
4075 .diffblock.margined.comm .line .code.no-comment:hover{
4076 background-image: none !important;
4077 cursor: auto !important;
4078 background-color: inherit !important;
4079
4072 } No newline at end of file
4080 }
@@ -343,7 +343,7 b' var createInlineForm = function(parent_t'
343 return form
343 return form
344 };
344 };
345 var injectInlineForm = function(tr){
345 var injectInlineForm = function(tr){
346 if(YUD.hasClass(tr,'form-open') || YUD.hasClass(tr,'context')){
346 if(YUD.hasClass(tr,'form-open') || YUD.hasClass(tr,'context') || YUD.hasClass(tr,'no-comment')){
347 return
347 return
348 }
348 }
349 YUD.addClass(tr,'form-open');
349 YUD.addClass(tr,'form-open');
@@ -115,88 +115,16 b''
115
115
116 </div>
116 </div>
117
117
118 %for change,filenode,diff,cs1,cs2,stat in c.changes:
118 ## diff block
119 %if change !='removed':
119 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
120 <div id="${h.FID(filenode.changeset.raw_id,filenode.path)}" style="clear:both;height:90px;margin-top:-60px"></div>
120 ${diff_block.diff_block(c.changes)}
121 <div class="diffblock margined comm">
122 <div class="code-header">
123 <div class="changeset_header">
124 <div class="changeset_file">
125 ${h.link_to_if(change!='removed',h.safe_unicode(filenode.path),h.url('files_home',repo_name=c.repo_name,
126 revision=filenode.changeset.raw_id,f_path=h.safe_unicode(filenode.path)))}
127 </div>
128 <div class="diff-menu-wrapper">
129 <img class="diff-menu-activate" style="margin-bottom:-6px;cursor: pointer" alt="diff-menu" src="${h.url('/images/icons/script_gear.png')}" />
130 <div class="diff-menu" style="display:none">
131 <ul>
132 <li>${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'))}</li>
133 <li>${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'))}</li>
134 <li>${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'))}</li>
135 <li>${c.ignorews_url(h.FID(filenode.changeset.raw_id,filenode.path))}</li>
136 <li>${c.context_url(h.FID(filenode.changeset.raw_id,filenode.path))}</li>
137 </ul>
138 </div>
139 </div>
140 <span style="float:right;margin-top:-3px">
141 <label>
142 ${_('show inline comments')}
143 ${h.checkbox('',checked="checked",class_="show-inline-comments",id_for=h.FID(filenode.changeset.raw_id,filenode.path))}
144 </label>
145 </span>
146 </div>
147 </div>
148 <div class="code-body">
149 <div class="full_f_path" path="${filenode.path}"></div>
150 %if diff:
151 ${diff|n}
152 %else:
153 ${_('No changes in this file')}
154 %endif
155 </div>
156 </div>
157 %endif
158 %endfor
159
121
122 ## template for inline comment form
160 <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
123 <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
161 ## template for inline comment form
124 ${comment.comment_inline_form(c.changeset)}
162 ${comment.comment_inline_form()}
163
164 <div class="comments">
165 <div class="comments-number">${len(c.comments)} comment(s) (${c.inline_cnt} ${_('inline')})</div>
166
167 %for path, lines in c.inline_comments:
168 <div style="display:none" class="inline-comment-placeholder" path="${path}" target_id="${h.FID(c.changeset.raw_id,path)}">
169 % for line,comments in lines.iteritems():
170 <div class="inline-comment-placeholder-line" line="${line}" target_id="${h.safeid(h.safe_unicode(path))}">
171 %for co in comments:
172 ${comment.comment_block(co)}
173 %endfor
174 </div>
175 %endfor
176 </div>
177 %endfor
178
125
179 %for co in c.comments:
126 ${comment.comments(c.changeset)}
180 ${comment.comment_block(co)}
127
181 %endfor
182 %if c.rhodecode_user.username != 'default':
183 <div class="comment-form">
184 ${h.form(h.url('changeset_comment', repo_name=c.repo_name, revision=c.changeset.raw_id))}
185 <strong>${_('Leave a comment')}</strong>
186 <div class="clearfix">
187 <div class="comment-help">
188 ${_('Comments parsed using')} <a href="${h.url('rst_help')}">RST</a> ${_('syntax')}
189 ${_('with')} <span style="color:#003367" class="tooltip" title="${_('Use @username inside this text to send notification to this RhodeCode user')}">@mention</span> ${_('support')}
190 </div>
191 ${h.textarea('text')}
192 </div>
193 <div class="comment-button">
194 ${h.submit('save', _('Comment'), class_='ui-button')}
195 </div>
196 ${h.end_form()}
197 </div>
198 %endif
199 </div>
200 <script type="text/javascript">
128 <script type="text/javascript">
201 var deleteComment = function(comment_id){
129 var deleteComment = function(comment_id){
202
130
@@ -264,11 +192,14 b''
264
192
265 var comment = new YAHOO.util.Element(tableTr('inline-comments',inline.innerHTML))
193 var comment = new YAHOO.util.Element(tableTr('inline-comments',inline.innerHTML))
266 YUD.insertAfter(comment,target_line.parentNode);
194 YUD.insertAfter(comment,target_line.parentNode);
267 }catch(e){}
195 }catch(e){
196 console.log(e);
197 }
268 }
198 }
269 }
199 }
270 })
200 })
271
201
272 </script>
202 </script>
203
273 </div>
204 </div>
274 </%def>
205 </%def>
@@ -1,3 +1,4 b''
1 ## -*- coding: utf-8 -*-
1 ##usage:
2 ## usage:
2 ## <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
3 ## <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
3 ## ${comment.comment_block(co)}
4 ## ${comment.comment_block(co)}
@@ -33,12 +34,11 b''
33 </%def>
34 </%def>
34
35
35
36
36
37 <%def name="comment_inline_form(changeset)">
37 <%def name="comment_inline_form()">
38 <div id='comment-inline-form-template' style="display:none">
38 <div id='comment-inline-form-template' style="display:none">
39 <div class="comment-inline-form">
39 <div class="comment-inline-form">
40 %if c.rhodecode_user.username != 'default':
40 %if c.rhodecode_user.username != 'default':
41 ${h.form(h.url('changeset_comment', repo_name=c.repo_name, revision=c.changeset.raw_id))}
41 ${h.form(h.url('changeset_comment', repo_name=c.repo_name, revision=changeset.raw_id))}
42 <div class="clearfix">
42 <div class="clearfix">
43 <div class="comment-help">${_('Commenting on line')} {1}. ${_('Comments parsed using')}
43 <div class="comment-help">${_('Commenting on line')} {1}. ${_('Comments parsed using')}
44 <a href="${h.url('rst_help')}">RST</a> ${_('syntax')} ${_('with')}
44 <a href="${h.url('rst_help')}">RST</a> ${_('syntax')} ${_('with')}
@@ -67,4 +67,45 b''
67 %endif
67 %endif
68 </div>
68 </div>
69 </div>
69 </div>
70 </%def> No newline at end of file
70 </%def>
71
72
73 <%def name="comments(changeset)">
74
75 <div class="comments">
76 <div class="comments-number">${len(c.comments)} comment(s) (${c.inline_cnt} ${_('inline')})</div>
77
78 %for path, lines in c.inline_comments:
79 <div style="display:none" class="inline-comment-placeholder" path="${path}" target_id="${h.FID(changeset.raw_id,path)}">
80 % for line,comments in lines.iteritems():
81 <div class="inline-comment-placeholder-line" line="${line}" target_id="${h.safeid(h.safe_unicode(path))}">
82 %for co in comments:
83 ${comment_block(co)}
84 %endfor
85 </div>
86 %endfor
87 </div>
88 %endfor
89
90 %for co in c.comments:
91 ${comment.comment_block(co)}
92 %endfor
93 %if c.rhodecode_user.username != 'default':
94 <div class="comment-form">
95 ${h.form(h.url('changeset_comment', repo_name=c.repo_name, revision=changeset.raw_id))}
96 <strong>${_('Leave a comment')}</strong>
97 <div class="clearfix">
98 <div class="comment-help">
99 ${_('Comments parsed using')} <a href="${h.url('rst_help')}">RST</a> ${_('syntax')}
100 ${_('with')} <span style="color:#003367" class="tooltip" title="${_('Use @username inside this text to send notification to this RhodeCode user')}">@mention</span> ${_('support')}
101 </div>
102 ${h.textarea('text')}
103 </div>
104 <div class="comment-button">
105 ${h.submit('save', _('Comment'), class_='ui-button')}
106 </div>
107 ${h.end_form()}
108 </div>
109 %endif
110 </div>
111 </%def>
@@ -1,3 +1,4 b''
1 ## -*- coding: utf-8 -*-
1 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
2
3
3 <%def name="title()">
4 <%def name="title()">
@@ -24,8 +25,8 b''
24 </div>
25 </div>
25 <div class="table">
26 <div class="table">
26 <div id="body" class="diffblock">
27 <div id="body" class="diffblock">
27 <div class="code-header">
28 <div class="code-header cv">
28 <h3>${_('Compare View')}</h3>
29 <h3 class="code-header-title">${_('Compare View')}</h3>
29 <div>
30 <div>
30 ${_('Changesets')} - r${c.cs_ranges[0].revision}:${h.short_id(c.cs_ranges[0].raw_id)} -> r${c.cs_ranges[-1].revision}:${h.short_id(c.cs_ranges[-1].raw_id)}
31 ${_('Changesets')} - r${c.cs_ranges[0].revision}:${h.short_id(c.cs_ranges[0].raw_id)} -> r${c.cs_ranges[-1].revision}:${h.short_id(c.cs_ranges[-1].raw_id)}
31 </div>
32 </div>
@@ -57,37 +58,32 b''
57 </div>
58 </div>
58
59
59 </div>
60 </div>
61 <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
62 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
60 %for cs in c.cs_ranges:
63 %for cs in c.cs_ranges:
61 %for change,filenode,diff,cs1,cs2,st in c.changes[cs.raw_id]:
64 ##${comment.comment_inline_form(cs)}
62 %if change !='removed':
65 ## diff block
63 <div style="clear:both;height:10px"></div>
66 <h3 style="border:none;padding-top:8px;">${'r%s:%s' % (cs.revision,h.short_id(cs.raw_id))}</h3>
64 <div class="diffblock margined">
67 ${diff_block.diff_block(c.changes[cs.raw_id])}
65 <div id="${h.FID(cs.raw_id,filenode.path)}" class="code-header">
68 ##${comment.comments(cs)}
66 <div class="changeset_header">
69
67 <span class="changeset_file">
68 ${h.link_to_if(change!='removed',h.safe_unicode(filenode.path),h.url('files_home',repo_name=c.repo_name,
69 revision=filenode.changeset.raw_id,f_path=h.safe_unicode(filenode.path)))}
70 </span>
71 %if 1:
72 &raquo; <span>${h.link_to(_('diff'),
73 h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(filenode.path),diff2=cs2,diff1=cs1,diff='diff'))}</span>
74 &raquo; <span>${h.link_to(_('raw diff'),
75 h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(filenode.path),diff2=cs2,diff1=cs1,diff='raw'))}</span>
76 &raquo; <span>${h.link_to(_('download diff'),
77 h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(filenode.path),diff2=cs2,diff1=cs1,diff='download'))}</span>
78 %endif
79 </div>
80 </div>
81 <div class="code-body">
82 %if diff:
83 ${diff|n}
84 %else:
85 ${_('No changes in this file')}
86 %endif
87 </div>
88 </div>
89 %endif
90 %endfor
70 %endfor
91 %endfor
71 <script type="text/javascript">
72
73 YUE.onDOMReady(function(){
74
75 YUE.on(YUQ('.diff-menu-activate'),'click',function(e){
76 var act = e.currentTarget.nextElementSibling;
77
78 if(YUD.hasClass(act,'active')){
79 YUD.removeClass(act,'active');
80 YUD.setStyle(act,'display','none');
81 }else{
82 YUD.addClass(act,'active');
83 YUD.setStyle(act,'display','');
84 }
85 });
86 })
87 </script>
92 </div>
88 </div>
93 </%def> No newline at end of file
89 </%def>
General Comments 0
You need to be logged in to leave comments. Login now