##// END OF EJS Templates
moved around some code in changeset controllers to properly log which function was decorated....
marcink -
r3750:244f184f beta
parent child Browse files
Show More
@@ -1,437 +1,440 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.changeset
3 rhodecode.controllers.changeset
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 changeset controller for pylons showoing changes beetween
6 changeset controller for pylons showoing changes beetween
7 revisions
7 revisions
8
8
9 :created_on: Apr 25, 2010
9 :created_on: Apr 25, 2010
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software: you can redistribute it and/or modify
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
17 # (at your option) any later version.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 import logging
26 import logging
27 import traceback
27 import traceback
28 from collections import defaultdict
28 from collections import defaultdict
29 from webob.exc import HTTPForbidden, HTTPBadRequest, HTTPNotFound
29 from webob.exc import HTTPForbidden, HTTPBadRequest, HTTPNotFound
30
30
31 from pylons import tmpl_context as c, url, request, response
31 from pylons import tmpl_context as c, url, request, response
32 from pylons.i18n.translation import _
32 from pylons.i18n.translation import _
33 from pylons.controllers.util import redirect
33 from pylons.controllers.util import redirect
34 from rhodecode.lib.utils import jsonify
34 from rhodecode.lib.utils import jsonify
35
35
36 from rhodecode.lib.vcs.exceptions import RepositoryError, \
36 from rhodecode.lib.vcs.exceptions import RepositoryError, \
37 ChangesetDoesNotExistError
37 ChangesetDoesNotExistError
38
38
39 import rhodecode.lib.helpers as h
39 import rhodecode.lib.helpers as h
40 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator,\
40 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator,\
41 NotAnonymous
41 NotAnonymous
42 from rhodecode.lib.base import BaseRepoController, render
42 from rhodecode.lib.base import BaseRepoController, render
43 from rhodecode.lib.utils import action_logger
43 from rhodecode.lib.utils import action_logger
44 from rhodecode.lib.compat import OrderedDict
44 from rhodecode.lib.compat import OrderedDict
45 from rhodecode.lib import diffs
45 from rhodecode.lib import diffs
46 from rhodecode.model.db import ChangesetComment, ChangesetStatus
46 from rhodecode.model.db import ChangesetComment, ChangesetStatus
47 from rhodecode.model.comment import ChangesetCommentsModel
47 from rhodecode.model.comment import ChangesetCommentsModel
48 from rhodecode.model.changeset_status import ChangesetStatusModel
48 from rhodecode.model.changeset_status import ChangesetStatusModel
49 from rhodecode.model.meta import Session
49 from rhodecode.model.meta import Session
50 from rhodecode.model.repo import RepoModel
50 from rhodecode.model.repo import RepoModel
51 from rhodecode.lib.diffs import LimitedDiffContainer
51 from rhodecode.lib.diffs import LimitedDiffContainer
52 from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
52 from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
53 from rhodecode.lib.vcs.backends.base import EmptyChangeset
53 from rhodecode.lib.vcs.backends.base import EmptyChangeset
54 from rhodecode.lib.utils2 import safe_unicode
54 from rhodecode.lib.utils2 import safe_unicode
55
55
56 log = logging.getLogger(__name__)
56 log = logging.getLogger(__name__)
57
57
58
58
59 def _update_with_GET(params, GET):
59 def _update_with_GET(params, GET):
60 for k in ['diff1', 'diff2', 'diff']:
60 for k in ['diff1', 'diff2', 'diff']:
61 params[k] += GET.getall(k)
61 params[k] += GET.getall(k)
62
62
63
63
64 def anchor_url(revision, path, GET):
64 def anchor_url(revision, path, GET):
65 fid = h.FID(revision, path)
65 fid = h.FID(revision, path)
66 return h.url.current(anchor=fid, **dict(GET))
66 return h.url.current(anchor=fid, **dict(GET))
67
67
68
68
69 def get_ignore_ws(fid, GET):
69 def get_ignore_ws(fid, GET):
70 ig_ws_global = GET.get('ignorews')
70 ig_ws_global = GET.get('ignorews')
71 ig_ws = filter(lambda k: k.startswith('WS'), GET.getall(fid))
71 ig_ws = filter(lambda k: k.startswith('WS'), GET.getall(fid))
72 if ig_ws:
72 if ig_ws:
73 try:
73 try:
74 return int(ig_ws[0].split(':')[-1])
74 return int(ig_ws[0].split(':')[-1])
75 except Exception:
75 except Exception:
76 pass
76 pass
77 return ig_ws_global
77 return ig_ws_global
78
78
79
79
80 def _ignorews_url(GET, fileid=None):
80 def _ignorews_url(GET, fileid=None):
81 fileid = str(fileid) if fileid else None
81 fileid = str(fileid) if fileid else None
82 params = defaultdict(list)
82 params = defaultdict(list)
83 _update_with_GET(params, GET)
83 _update_with_GET(params, GET)
84 lbl = _('Show white space')
84 lbl = _('Show white space')
85 ig_ws = get_ignore_ws(fileid, GET)
85 ig_ws = get_ignore_ws(fileid, GET)
86 ln_ctx = get_line_ctx(fileid, GET)
86 ln_ctx = get_line_ctx(fileid, GET)
87 # global option
87 # global option
88 if fileid is None:
88 if fileid is None:
89 if ig_ws is None:
89 if ig_ws is None:
90 params['ignorews'] += [1]
90 params['ignorews'] += [1]
91 lbl = _('Ignore white space')
91 lbl = _('Ignore white space')
92 ctx_key = 'context'
92 ctx_key = 'context'
93 ctx_val = ln_ctx
93 ctx_val = ln_ctx
94 # per file options
94 # per file options
95 else:
95 else:
96 if ig_ws is None:
96 if ig_ws is None:
97 params[fileid] += ['WS:1']
97 params[fileid] += ['WS:1']
98 lbl = _('Ignore white space')
98 lbl = _('Ignore white space')
99
99
100 ctx_key = fileid
100 ctx_key = fileid
101 ctx_val = 'C:%s' % ln_ctx
101 ctx_val = 'C:%s' % ln_ctx
102 # if we have passed in ln_ctx pass it along to our params
102 # if we have passed in ln_ctx pass it along to our params
103 if ln_ctx:
103 if ln_ctx:
104 params[ctx_key] += [ctx_val]
104 params[ctx_key] += [ctx_val]
105
105
106 params['anchor'] = fileid
106 params['anchor'] = fileid
107 img = h.image(h.url('/images/icons/text_strikethrough.png'), lbl, class_='icon')
107 img = h.image(h.url('/images/icons/text_strikethrough.png'), lbl, class_='icon')
108 return h.link_to(img, h.url.current(**params), title=lbl, class_='tooltip')
108 return h.link_to(img, h.url.current(**params), title=lbl, class_='tooltip')
109
109
110
110
111 def get_line_ctx(fid, GET):
111 def get_line_ctx(fid, GET):
112 ln_ctx_global = GET.get('context')
112 ln_ctx_global = GET.get('context')
113 if fid:
113 if fid:
114 ln_ctx = filter(lambda k: k.startswith('C'), GET.getall(fid))
114 ln_ctx = filter(lambda k: k.startswith('C'), GET.getall(fid))
115 else:
115 else:
116 _ln_ctx = filter(lambda k: k.startswith('C'), GET)
116 _ln_ctx = filter(lambda k: k.startswith('C'), GET)
117 ln_ctx = GET.get(_ln_ctx[0]) if _ln_ctx else ln_ctx_global
117 ln_ctx = GET.get(_ln_ctx[0]) if _ln_ctx else ln_ctx_global
118 if ln_ctx:
118 if ln_ctx:
119 ln_ctx = [ln_ctx]
119 ln_ctx = [ln_ctx]
120
120
121 if ln_ctx:
121 if ln_ctx:
122 retval = ln_ctx[0].split(':')[-1]
122 retval = ln_ctx[0].split(':')[-1]
123 else:
123 else:
124 retval = ln_ctx_global
124 retval = ln_ctx_global
125
125
126 try:
126 try:
127 return int(retval)
127 return int(retval)
128 except Exception:
128 except Exception:
129 return 3
129 return 3
130
130
131
131
132 def _context_url(GET, fileid=None):
132 def _context_url(GET, fileid=None):
133 """
133 """
134 Generates url for context lines
134 Generates url for context lines
135
135
136 :param fileid:
136 :param fileid:
137 """
137 """
138
138
139 fileid = str(fileid) if fileid else None
139 fileid = str(fileid) if fileid else None
140 ig_ws = get_ignore_ws(fileid, GET)
140 ig_ws = get_ignore_ws(fileid, GET)
141 ln_ctx = (get_line_ctx(fileid, GET) or 3) * 2
141 ln_ctx = (get_line_ctx(fileid, GET) or 3) * 2
142
142
143 params = defaultdict(list)
143 params = defaultdict(list)
144 _update_with_GET(params, GET)
144 _update_with_GET(params, GET)
145
145
146 # global option
146 # global option
147 if fileid is None:
147 if fileid is None:
148 if ln_ctx > 0:
148 if ln_ctx > 0:
149 params['context'] += [ln_ctx]
149 params['context'] += [ln_ctx]
150
150
151 if ig_ws:
151 if ig_ws:
152 ig_ws_key = 'ignorews'
152 ig_ws_key = 'ignorews'
153 ig_ws_val = 1
153 ig_ws_val = 1
154
154
155 # per file option
155 # per file option
156 else:
156 else:
157 params[fileid] += ['C:%s' % ln_ctx]
157 params[fileid] += ['C:%s' % ln_ctx]
158 ig_ws_key = fileid
158 ig_ws_key = fileid
159 ig_ws_val = 'WS:%s' % 1
159 ig_ws_val = 'WS:%s' % 1
160
160
161 if ig_ws:
161 if ig_ws:
162 params[ig_ws_key] += [ig_ws_val]
162 params[ig_ws_key] += [ig_ws_val]
163
163
164 lbl = _('%s line context') % ln_ctx
164 lbl = _('%s line context') % ln_ctx
165
165
166 params['anchor'] = fileid
166 params['anchor'] = fileid
167 img = h.image(h.url('/images/icons/table_add.png'), lbl, class_='icon')
167 img = h.image(h.url('/images/icons/table_add.png'), lbl, class_='icon')
168 return h.link_to(img, h.url.current(**params), title=lbl, class_='tooltip')
168 return h.link_to(img, h.url.current(**params), title=lbl, class_='tooltip')
169
169
170
170
171 class ChangesetController(BaseRepoController):
171 class ChangesetController(BaseRepoController):
172
172
173 def __before__(self):
173 def __before__(self):
174 super(ChangesetController, self).__before__()
174 super(ChangesetController, self).__before__()
175 c.affected_files_cut_off = 60
175 c.affected_files_cut_off = 60
176 repo_model = RepoModel()
176 repo_model = RepoModel()
177 c.users_array = repo_model.get_users_js()
177 c.users_array = repo_model.get_users_js()
178 c.users_groups_array = repo_model.get_users_groups_js()
178 c.users_groups_array = repo_model.get_users_groups_js()
179
179
180 @LoginRequired()
180 def _index(self, revision, method):
181 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
182 'repository.admin')
183 def index(self, revision, method='show'):
184 c.anchor_url = anchor_url
181 c.anchor_url = anchor_url
185 c.ignorews_url = _ignorews_url
182 c.ignorews_url = _ignorews_url
186 c.context_url = _context_url
183 c.context_url = _context_url
187 c.fulldiff = fulldiff = request.GET.get('fulldiff')
184 c.fulldiff = fulldiff = request.GET.get('fulldiff')
188 #get ranges of revisions if preset
185 #get ranges of revisions if preset
189 rev_range = revision.split('...')[:2]
186 rev_range = revision.split('...')[:2]
190 enable_comments = True
187 enable_comments = True
191 try:
188 try:
192 if len(rev_range) == 2:
189 if len(rev_range) == 2:
193 enable_comments = False
190 enable_comments = False
194 rev_start = rev_range[0]
191 rev_start = rev_range[0]
195 rev_end = rev_range[1]
192 rev_end = rev_range[1]
196 rev_ranges = c.rhodecode_repo.get_changesets(start=rev_start,
193 rev_ranges = c.rhodecode_repo.get_changesets(start=rev_start,
197 end=rev_end)
194 end=rev_end)
198 else:
195 else:
199 rev_ranges = [c.rhodecode_repo.get_changeset(revision)]
196 rev_ranges = [c.rhodecode_repo.get_changeset(revision)]
200
197
201 c.cs_ranges = list(rev_ranges)
198 c.cs_ranges = list(rev_ranges)
202 if not c.cs_ranges:
199 if not c.cs_ranges:
203 raise RepositoryError('Changeset range returned empty result')
200 raise RepositoryError('Changeset range returned empty result')
204
201
205 except (RepositoryError, ChangesetDoesNotExistError, Exception), e:
202 except (RepositoryError, ChangesetDoesNotExistError, Exception), e:
206 log.error(traceback.format_exc())
203 log.error(traceback.format_exc())
207 h.flash(str(e), category='error')
204 h.flash(str(e), category='error')
208 raise HTTPNotFound()
205 raise HTTPNotFound()
209
206
210 c.changes = OrderedDict()
207 c.changes = OrderedDict()
211
208
212 c.lines_added = 0 # count of lines added
209 c.lines_added = 0 # count of lines added
213 c.lines_deleted = 0 # count of lines removes
210 c.lines_deleted = 0 # count of lines removes
214
211
215 c.changeset_statuses = ChangesetStatus.STATUSES
212 c.changeset_statuses = ChangesetStatus.STATUSES
216 c.comments = []
213 c.comments = []
217 c.statuses = []
214 c.statuses = []
218 c.inline_comments = []
215 c.inline_comments = []
219 c.inline_cnt = 0
216 c.inline_cnt = 0
220
217
221 # Iterate over ranges (default changeset view is always one changeset)
218 # Iterate over ranges (default changeset view is always one changeset)
222 for changeset in c.cs_ranges:
219 for changeset in c.cs_ranges:
223 inlines = []
220 inlines = []
224 if method == 'show':
221 if method == 'show':
225 c.statuses.extend([ChangesetStatusModel().get_status(
222 c.statuses.extend([ChangesetStatusModel().get_status(
226 c.rhodecode_db_repo.repo_id, changeset.raw_id)])
223 c.rhodecode_db_repo.repo_id, changeset.raw_id)])
227
224
228 c.comments.extend(ChangesetCommentsModel()\
225 c.comments.extend(ChangesetCommentsModel()\
229 .get_comments(c.rhodecode_db_repo.repo_id,
226 .get_comments(c.rhodecode_db_repo.repo_id,
230 revision=changeset.raw_id))
227 revision=changeset.raw_id))
231
228
232 #comments from PR
229 #comments from PR
233 st = ChangesetStatusModel().get_statuses(
230 st = ChangesetStatusModel().get_statuses(
234 c.rhodecode_db_repo.repo_id, changeset.raw_id,
231 c.rhodecode_db_repo.repo_id, changeset.raw_id,
235 with_revisions=True)
232 with_revisions=True)
236 # from associated statuses, check the pull requests, and
233 # from associated statuses, check the pull requests, and
237 # show comments from them
234 # show comments from them
238
235
239 prs = set([x.pull_request for x in
236 prs = set([x.pull_request for x in
240 filter(lambda x: x.pull_request != None, st)])
237 filter(lambda x: x.pull_request != None, st)])
241
238
242 for pr in prs:
239 for pr in prs:
243 c.comments.extend(pr.comments)
240 c.comments.extend(pr.comments)
244 inlines = ChangesetCommentsModel()\
241 inlines = ChangesetCommentsModel()\
245 .get_inline_comments(c.rhodecode_db_repo.repo_id,
242 .get_inline_comments(c.rhodecode_db_repo.repo_id,
246 revision=changeset.raw_id)
243 revision=changeset.raw_id)
247 c.inline_comments.extend(inlines)
244 c.inline_comments.extend(inlines)
248
245
249 c.changes[changeset.raw_id] = []
246 c.changes[changeset.raw_id] = []
250
247
251 cs2 = changeset.raw_id
248 cs2 = changeset.raw_id
252 cs1 = changeset.parents[0].raw_id if changeset.parents else EmptyChangeset()
249 cs1 = changeset.parents[0].raw_id if changeset.parents else EmptyChangeset()
253 context_lcl = get_line_ctx('', request.GET)
250 context_lcl = get_line_ctx('', request.GET)
254 ign_whitespace_lcl = ign_whitespace_lcl = get_ignore_ws('', request.GET)
251 ign_whitespace_lcl = ign_whitespace_lcl = get_ignore_ws('', request.GET)
255
252
256 _diff = c.rhodecode_repo.get_diff(cs1, cs2,
253 _diff = c.rhodecode_repo.get_diff(cs1, cs2,
257 ignore_whitespace=ign_whitespace_lcl, context=context_lcl)
254 ignore_whitespace=ign_whitespace_lcl, context=context_lcl)
258 diff_limit = self.cut_off_limit if not fulldiff else None
255 diff_limit = self.cut_off_limit if not fulldiff else None
259 diff_processor = diffs.DiffProcessor(_diff,
256 diff_processor = diffs.DiffProcessor(_diff,
260 vcs=c.rhodecode_repo.alias,
257 vcs=c.rhodecode_repo.alias,
261 format='gitdiff',
258 format='gitdiff',
262 diff_limit=diff_limit)
259 diff_limit=diff_limit)
263 cs_changes = OrderedDict()
260 cs_changes = OrderedDict()
264 if method == 'show':
261 if method == 'show':
265 _parsed = diff_processor.prepare()
262 _parsed = diff_processor.prepare()
266 c.limited_diff = False
263 c.limited_diff = False
267 if isinstance(_parsed, LimitedDiffContainer):
264 if isinstance(_parsed, LimitedDiffContainer):
268 c.limited_diff = True
265 c.limited_diff = True
269 for f in _parsed:
266 for f in _parsed:
270 st = f['stats']
267 st = f['stats']
271 if st[0] != 'b':
268 if st[0] != 'b':
272 c.lines_added += st[0]
269 c.lines_added += st[0]
273 c.lines_deleted += st[1]
270 c.lines_deleted += st[1]
274 fid = h.FID(changeset.raw_id, f['filename'])
271 fid = h.FID(changeset.raw_id, f['filename'])
275 diff = diff_processor.as_html(enable_comments=enable_comments,
272 diff = diff_processor.as_html(enable_comments=enable_comments,
276 parsed_lines=[f])
273 parsed_lines=[f])
277 cs_changes[fid] = [cs1, cs2, f['operation'], f['filename'],
274 cs_changes[fid] = [cs1, cs2, f['operation'], f['filename'],
278 diff, st]
275 diff, st]
279 else:
276 else:
280 # downloads/raw we only need RAW diff nothing else
277 # downloads/raw we only need RAW diff nothing else
281 diff = diff_processor.as_raw()
278 diff = diff_processor.as_raw()
282 cs_changes[''] = [None, None, None, None, diff, None]
279 cs_changes[''] = [None, None, None, None, diff, None]
283 c.changes[changeset.raw_id] = cs_changes
280 c.changes[changeset.raw_id] = cs_changes
284
281
285 #sort comments by how they were generated
282 #sort comments by how they were generated
286 c.comments = sorted(c.comments, key=lambda x: x.comment_id)
283 c.comments = sorted(c.comments, key=lambda x: x.comment_id)
287
284
288 # count inline comments
285 # count inline comments
289 for __, lines in c.inline_comments:
286 for __, lines in c.inline_comments:
290 for comments in lines.values():
287 for comments in lines.values():
291 c.inline_cnt += len(comments)
288 c.inline_cnt += len(comments)
292
289
293 if len(c.cs_ranges) == 1:
290 if len(c.cs_ranges) == 1:
294 c.changeset = c.cs_ranges[0]
291 c.changeset = c.cs_ranges[0]
295 c.parent_tmpl = ''.join(['# Parent %s\n' % x.raw_id
292 c.parent_tmpl = ''.join(['# Parent %s\n' % x.raw_id
296 for x in c.changeset.parents])
293 for x in c.changeset.parents])
297 if method == 'download':
294 if method == 'download':
298 response.content_type = 'text/plain'
295 response.content_type = 'text/plain'
299 response.content_disposition = 'attachment; filename=%s.diff' \
296 response.content_disposition = 'attachment; filename=%s.diff' \
300 % revision[:12]
297 % revision[:12]
301 return diff
298 return diff
302 elif method == 'patch':
299 elif method == 'patch':
303 response.content_type = 'text/plain'
300 response.content_type = 'text/plain'
304 c.diff = safe_unicode(diff)
301 c.diff = safe_unicode(diff)
305 return render('changeset/patch_changeset.html')
302 return render('changeset/patch_changeset.html')
306 elif method == 'raw':
303 elif method == 'raw':
307 response.content_type = 'text/plain'
304 response.content_type = 'text/plain'
308 return diff
305 return diff
309 elif method == 'show':
306 elif method == 'show':
310 if len(c.cs_ranges) == 1:
307 if len(c.cs_ranges) == 1:
311 return render('changeset/changeset.html')
308 return render('changeset/changeset.html')
312 else:
309 else:
313 return render('changeset/changeset_range.html')
310 return render('changeset/changeset_range.html')
314
311
315 @LoginRequired()
312 @LoginRequired()
316 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
313 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
317 'repository.admin')
314 'repository.admin')
315 def index(self, revision, method='show'):
316 return self._index(revision, method=method)
317
318 @LoginRequired()
319 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
320 'repository.admin')
318 def changeset_raw(self, revision):
321 def changeset_raw(self, revision):
319 return self.index(revision, method='raw')
322 return self._index(revision, method='raw')
320
323
321 @LoginRequired()
324 @LoginRequired()
322 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
325 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
323 'repository.admin')
326 'repository.admin')
324 def changeset_patch(self, revision):
327 def changeset_patch(self, revision):
325 return self.index(revision, method='patch')
328 return self._index(revision, method='patch')
326
329
327 @LoginRequired()
330 @LoginRequired()
328 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
331 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
329 'repository.admin')
332 'repository.admin')
330 def changeset_download(self, revision):
333 def changeset_download(self, revision):
331 return self.index(revision, method='download')
334 return self._index(revision, method='download')
332
335
333 @LoginRequired()
336 @LoginRequired()
334 @NotAnonymous()
337 @NotAnonymous()
335 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
338 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
336 'repository.admin')
339 'repository.admin')
337 @jsonify
340 @jsonify
338 def comment(self, repo_name, revision):
341 def comment(self, repo_name, revision):
339 status = request.POST.get('changeset_status')
342 status = request.POST.get('changeset_status')
340 change_status = request.POST.get('change_changeset_status')
343 change_status = request.POST.get('change_changeset_status')
341 text = request.POST.get('text')
344 text = request.POST.get('text')
342 if status and change_status:
345 if status and change_status:
343 text = text or (_('Status change -> %s')
346 text = text or (_('Status change -> %s')
344 % ChangesetStatus.get_status_lbl(status))
347 % ChangesetStatus.get_status_lbl(status))
345
348
346 c.co = comm = ChangesetCommentsModel().create(
349 c.co = comm = ChangesetCommentsModel().create(
347 text=text,
350 text=text,
348 repo=c.rhodecode_db_repo.repo_id,
351 repo=c.rhodecode_db_repo.repo_id,
349 user=c.rhodecode_user.user_id,
352 user=c.rhodecode_user.user_id,
350 revision=revision,
353 revision=revision,
351 f_path=request.POST.get('f_path'),
354 f_path=request.POST.get('f_path'),
352 line_no=request.POST.get('line'),
355 line_no=request.POST.get('line'),
353 status_change=(ChangesetStatus.get_status_lbl(status)
356 status_change=(ChangesetStatus.get_status_lbl(status)
354 if status and change_status else None)
357 if status and change_status else None)
355 )
358 )
356
359
357 # get status if set !
360 # get status if set !
358 if status and change_status:
361 if status and change_status:
359 # if latest status was from pull request and it's closed
362 # if latest status was from pull request and it's closed
360 # disallow changing status !
363 # disallow changing status !
361 # dont_allow_on_closed_pull_request = True !
364 # dont_allow_on_closed_pull_request = True !
362
365
363 try:
366 try:
364 ChangesetStatusModel().set_status(
367 ChangesetStatusModel().set_status(
365 c.rhodecode_db_repo.repo_id,
368 c.rhodecode_db_repo.repo_id,
366 status,
369 status,
367 c.rhodecode_user.user_id,
370 c.rhodecode_user.user_id,
368 comm,
371 comm,
369 revision=revision,
372 revision=revision,
370 dont_allow_on_closed_pull_request=True
373 dont_allow_on_closed_pull_request=True
371 )
374 )
372 except StatusChangeOnClosedPullRequestError:
375 except StatusChangeOnClosedPullRequestError:
373 log.error(traceback.format_exc())
376 log.error(traceback.format_exc())
374 msg = _('Changing status on a changeset associated with '
377 msg = _('Changing status on a changeset associated with '
375 'a closed pull request is not allowed')
378 'a closed pull request is not allowed')
376 h.flash(msg, category='warning')
379 h.flash(msg, category='warning')
377 return redirect(h.url('changeset_home', repo_name=repo_name,
380 return redirect(h.url('changeset_home', repo_name=repo_name,
378 revision=revision))
381 revision=revision))
379 action_logger(self.rhodecode_user,
382 action_logger(self.rhodecode_user,
380 'user_commented_revision:%s' % revision,
383 'user_commented_revision:%s' % revision,
381 c.rhodecode_db_repo, self.ip_addr, self.sa)
384 c.rhodecode_db_repo, self.ip_addr, self.sa)
382
385
383 Session().commit()
386 Session().commit()
384
387
385 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
388 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
386 return redirect(h.url('changeset_home', repo_name=repo_name,
389 return redirect(h.url('changeset_home', repo_name=repo_name,
387 revision=revision))
390 revision=revision))
388 #only ajax below
391 #only ajax below
389 data = {
392 data = {
390 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
393 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
391 }
394 }
392 if comm:
395 if comm:
393 data.update(comm.get_dict())
396 data.update(comm.get_dict())
394 data.update({'rendered_text':
397 data.update({'rendered_text':
395 render('changeset/changeset_comment_block.html')})
398 render('changeset/changeset_comment_block.html')})
396
399
397 return data
400 return data
398
401
399 @LoginRequired()
402 @LoginRequired()
400 @NotAnonymous()
403 @NotAnonymous()
401 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
404 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
402 'repository.admin')
405 'repository.admin')
403 def preview_comment(self):
406 def preview_comment(self):
404 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
407 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
405 raise HTTPBadRequest()
408 raise HTTPBadRequest()
406 text = request.POST.get('text')
409 text = request.POST.get('text')
407 if text:
410 if text:
408 return h.rst_w_mentions(text)
411 return h.rst_w_mentions(text)
409 return ''
412 return ''
410
413
411 @LoginRequired()
414 @LoginRequired()
412 @NotAnonymous()
415 @NotAnonymous()
413 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
416 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
414 'repository.admin')
417 'repository.admin')
415 @jsonify
418 @jsonify
416 def delete_comment(self, repo_name, comment_id):
419 def delete_comment(self, repo_name, comment_id):
417 co = ChangesetComment.get(comment_id)
420 co = ChangesetComment.get(comment_id)
418 owner = co.author.user_id == c.rhodecode_user.user_id
421 owner = co.author.user_id == c.rhodecode_user.user_id
419 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
422 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
420 ChangesetCommentsModel().delete(comment=co)
423 ChangesetCommentsModel().delete(comment=co)
421 Session().commit()
424 Session().commit()
422 return True
425 return True
423 else:
426 else:
424 raise HTTPForbidden()
427 raise HTTPForbidden()
425
428
426 @LoginRequired()
429 @LoginRequired()
427 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
430 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
428 'repository.admin')
431 'repository.admin')
429 @jsonify
432 @jsonify
430 def changeset_info(self, repo_name, revision):
433 def changeset_info(self, repo_name, revision):
431 if request.is_xhr:
434 if request.is_xhr:
432 try:
435 try:
433 return c.rhodecode_repo.get_changeset(revision)
436 return c.rhodecode_repo.get_changeset(revision)
434 except ChangesetDoesNotExistError, e:
437 except ChangesetDoesNotExistError, e:
435 return EmptyChangeset(message=str(e))
438 return EmptyChangeset(message=str(e))
436 else:
439 else:
437 raise HTTPBadRequest()
440 raise HTTPBadRequest()
General Comments 0
You need to be logged in to leave comments. Login now