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