##// END OF EJS Templates
typos+docs.
marcink -
r2769:52617fb7 beta
parent child Browse files
Show More
@@ -1,402 +1,404 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.pullrequests
3 rhodecode.controllers.pullrequests
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 pull requests controller for rhodecode for initializing pull requests
6 pull requests controller for rhodecode for initializing pull requests
7
7
8 :created_on: May 7, 2012
8 :created_on: May 7, 2012
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 import logging
25 import logging
26 import traceback
26 import traceback
27 import formencode
27 import formencode
28
28
29 from webob.exc import HTTPNotFound, HTTPForbidden
29 from webob.exc import HTTPNotFound, HTTPForbidden
30 from collections import defaultdict
30 from collections import defaultdict
31 from itertools import groupby
31 from itertools import groupby
32
32
33 from pylons import request, response, session, tmpl_context as c, url
33 from pylons import request, response, session, tmpl_context as c, url
34 from pylons.controllers.util import abort, redirect
34 from pylons.controllers.util import abort, redirect
35 from pylons.i18n.translation import _
35 from pylons.i18n.translation import _
36 from pylons.decorators import jsonify
36 from pylons.decorators import jsonify
37
37
38 from rhodecode.lib.compat import json
38 from rhodecode.lib.compat import json
39 from rhodecode.lib.base import BaseRepoController, render
39 from rhodecode.lib.base import BaseRepoController, render
40 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator,\
40 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator,\
41 NotAnonymous
41 NotAnonymous
42 from rhodecode.lib import helpers as h
42 from rhodecode.lib import helpers as h
43 from rhodecode.lib import diffs
43 from rhodecode.lib import diffs
44 from rhodecode.lib.utils import action_logger
44 from rhodecode.lib.utils import action_logger
45 from rhodecode.model.db import User, PullRequest, ChangesetStatus,\
45 from rhodecode.model.db import User, PullRequest, ChangesetStatus,\
46 ChangesetComment
46 ChangesetComment
47 from rhodecode.model.pull_request import PullRequestModel
47 from rhodecode.model.pull_request import PullRequestModel
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.model.comment import ChangesetCommentsModel
50 from rhodecode.model.comment import ChangesetCommentsModel
51 from rhodecode.model.changeset_status import ChangesetStatusModel
51 from rhodecode.model.changeset_status import ChangesetStatusModel
52 from rhodecode.model.forms import PullRequestForm
52 from rhodecode.model.forms import PullRequestForm
53
53
54 log = logging.getLogger(__name__)
54 log = logging.getLogger(__name__)
55
55
56
56
57 class PullrequestsController(BaseRepoController):
57 class PullrequestsController(BaseRepoController):
58
58
59 @LoginRequired()
59 @LoginRequired()
60 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
60 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
61 'repository.admin')
61 'repository.admin')
62 def __before__(self):
62 def __before__(self):
63 super(PullrequestsController, self).__before__()
63 super(PullrequestsController, self).__before__()
64 repo_model = RepoModel()
64 repo_model = RepoModel()
65 c.users_array = repo_model.get_users_js()
65 c.users_array = repo_model.get_users_js()
66 c.users_groups_array = repo_model.get_users_groups_js()
66 c.users_groups_array = repo_model.get_users_groups_js()
67
67
68 def _get_repo_refs(self, repo):
68 def _get_repo_refs(self, repo):
69 hist_l = []
69 hist_l = []
70
70
71 branches_group = ([('branch:%s:%s' % (k, v), k) for
71 branches_group = ([('branch:%s:%s' % (k, v), k) for
72 k, v in repo.branches.iteritems()], _("Branches"))
72 k, v in repo.branches.iteritems()], _("Branches"))
73 bookmarks_group = ([('book:%s:%s' % (k, v), k) for
73 bookmarks_group = ([('book:%s:%s' % (k, v), k) for
74 k, v in repo.bookmarks.iteritems()], _("Bookmarks"))
74 k, v in repo.bookmarks.iteritems()], _("Bookmarks"))
75 tags_group = ([('tag:%s:%s' % (k, v), k) for
75 tags_group = ([('tag:%s:%s' % (k, v), k) for
76 k, v in repo.tags.iteritems()], _("Tags"))
76 k, v in repo.tags.iteritems()], _("Tags"))
77
77
78 hist_l.append(bookmarks_group)
78 hist_l.append(bookmarks_group)
79 hist_l.append(branches_group)
79 hist_l.append(branches_group)
80 hist_l.append(tags_group)
80 hist_l.append(tags_group)
81
81
82 return hist_l
82 return hist_l
83
83
84 def show_all(self, repo_name):
84 def show_all(self, repo_name):
85 c.pull_requests = PullRequestModel().get_all(repo_name)
85 c.pull_requests = PullRequestModel().get_all(repo_name)
86 c.repo_name = repo_name
86 c.repo_name = repo_name
87 return render('/pullrequests/pullrequest_show_all.html')
87 return render('/pullrequests/pullrequest_show_all.html')
88
88
89 @NotAnonymous()
89 @NotAnonymous()
90 def index(self):
90 def index(self):
91 org_repo = c.rhodecode_db_repo
91 org_repo = c.rhodecode_db_repo
92
92
93 if org_repo.scm_instance.alias != 'hg':
93 if org_repo.scm_instance.alias != 'hg':
94 log.error('Review not available for GIT REPOS')
94 log.error('Review not available for GIT REPOS')
95 raise HTTPNotFound
95 raise HTTPNotFound
96
96
97 other_repos_info = {}
97 other_repos_info = {}
98
98
99 c.org_refs = self._get_repo_refs(c.rhodecode_repo)
99 c.org_refs = self._get_repo_refs(c.rhodecode_repo)
100 c.org_repos = []
100 c.org_repos = []
101 c.other_repos = []
101 c.other_repos = []
102 c.org_repos.append((org_repo.repo_name, '%s/%s' % (
102 c.org_repos.append((org_repo.repo_name, '%s/%s' % (
103 org_repo.user.username, c.repo_name))
103 org_repo.user.username, c.repo_name))
104 )
104 )
105
105
106 # add org repo to other so we can open pull request agains itself
106 # add org repo to other so we can open pull request agains itself
107 c.other_repos.extend(c.org_repos)
107 c.other_repos.extend(c.org_repos)
108
108
109 c.default_pull_request = org_repo.repo_name
109 c.default_pull_request = org_repo.repo_name
110 c.default_revs = self._get_repo_refs(org_repo.scm_instance)
110 c.default_revs = self._get_repo_refs(org_repo.scm_instance)
111 #add orginal repo
111 #add orginal repo
112 other_repos_info[org_repo.repo_name] = {
112 other_repos_info[org_repo.repo_name] = {
113 'gravatar': h.gravatar_url(org_repo.user.email, 24),
113 'gravatar': h.gravatar_url(org_repo.user.email, 24),
114 'description': org_repo.description,
114 'description': org_repo.description,
115 'revs': h.select('other_ref', '', c.default_revs, class_='refs')
115 'revs': h.select('other_ref', '', c.default_revs, class_='refs')
116 }
116 }
117
117
118 #gather forks and add to this list
118 #gather forks and add to this list
119 for fork in org_repo.forks:
119 for fork in org_repo.forks:
120 c.other_repos.append((fork.repo_name, '%s/%s' % (
120 c.other_repos.append((fork.repo_name, '%s/%s' % (
121 fork.user.username, fork.repo_name))
121 fork.user.username, fork.repo_name))
122 )
122 )
123 other_repos_info[fork.repo_name] = {
123 other_repos_info[fork.repo_name] = {
124 'gravatar': h.gravatar_url(fork.user.email, 24),
124 'gravatar': h.gravatar_url(fork.user.email, 24),
125 'description': fork.description,
125 'description': fork.description,
126 'revs': h.select('other_ref', '',
126 'revs': h.select('other_ref', '',
127 self._get_repo_refs(fork.scm_instance),
127 self._get_repo_refs(fork.scm_instance),
128 class_='refs')
128 class_='refs')
129 }
129 }
130 #add parents of this fork also
130 #add parents of this fork also
131 if org_repo.parent:
131 if org_repo.parent:
132 c.default_pull_request = org_repo.parent.repo_name
132 c.default_pull_request = org_repo.parent.repo_name
133 c.other_repos.append((org_repo.parent.repo_name, '%s/%s' % (
133 c.other_repos.append((org_repo.parent.repo_name, '%s/%s' % (
134 org_repo.parent.user.username,
134 org_repo.parent.user.username,
135 org_repo.parent.repo_name))
135 org_repo.parent.repo_name))
136 )
136 )
137 other_repos_info[org_repo.parent.repo_name] = {
137 other_repos_info[org_repo.parent.repo_name] = {
138 'gravatar': h.gravatar_url(org_repo.parent.user.email, 24),
138 'gravatar': h.gravatar_url(org_repo.parent.user.email, 24),
139 'description': org_repo.parent.description,
139 'description': org_repo.parent.description,
140 'revs': h.select('other_ref', '',
140 'revs': h.select('other_ref', '',
141 self._get_repo_refs(org_repo.parent.scm_instance),
141 self._get_repo_refs(org_repo.parent.scm_instance),
142 class_='refs')
142 class_='refs')
143 }
143 }
144
144
145 c.other_repos_info = json.dumps(other_repos_info)
145 c.other_repos_info = json.dumps(other_repos_info)
146 c.review_members = [org_repo.user]
146 c.review_members = [org_repo.user]
147 return render('/pullrequests/pullrequest.html')
147 return render('/pullrequests/pullrequest.html')
148
148
149 @NotAnonymous()
149 @NotAnonymous()
150 def create(self, repo_name):
150 def create(self, repo_name):
151 try:
151 try:
152 _form = PullRequestForm()().to_python(request.POST)
152 _form = PullRequestForm()().to_python(request.POST)
153 except formencode.Invalid, errors:
153 except formencode.Invalid, errors:
154 log.error(traceback.format_exc())
154 log.error(traceback.format_exc())
155 if errors.error_dict.get('revisions'):
155 if errors.error_dict.get('revisions'):
156 msg = 'Revisions: %s' % errors.error_dict['revisions']
156 msg = 'Revisions: %s' % errors.error_dict['revisions']
157 elif errors.error_dict.get('pullrequest_title'):
157 elif errors.error_dict.get('pullrequest_title'):
158 msg = _('Pull request requires a title with min. 3 chars')
158 msg = _('Pull request requires a title with min. 3 chars')
159 else:
159 else:
160 msg = _('error during creation of pull request')
160 msg = _('error during creation of pull request')
161
161
162 h.flash(msg, 'error')
162 h.flash(msg, 'error')
163 return redirect(url('pullrequest_home', repo_name=repo_name))
163 return redirect(url('pullrequest_home', repo_name=repo_name))
164
164
165 org_repo = _form['org_repo']
165 org_repo = _form['org_repo']
166 org_ref = _form['org_ref']
166 org_ref = _form['org_ref']
167 other_repo = _form['other_repo']
167 other_repo = _form['other_repo']
168 other_ref = _form['other_ref']
168 other_ref = _form['other_ref']
169 revisions = _form['revisions']
169 revisions = _form['revisions']
170 reviewers = _form['review_members']
170 reviewers = _form['review_members']
171
171
172 title = _form['pullrequest_title']
172 title = _form['pullrequest_title']
173 description = _form['pullrequest_desc']
173 description = _form['pullrequest_desc']
174
174
175 try:
175 try:
176 pull_request = PullRequestModel().create(
176 pull_request = PullRequestModel().create(
177 self.rhodecode_user.user_id, org_repo, org_ref, other_repo,
177 self.rhodecode_user.user_id, org_repo, org_ref, other_repo,
178 other_ref, revisions, reviewers, title, description
178 other_ref, revisions, reviewers, title, description
179 )
179 )
180 Session().commit()
180 Session().commit()
181 h.flash(_('Successfully opened new pull request'),
181 h.flash(_('Successfully opened new pull request'),
182 category='success')
182 category='success')
183 except Exception:
183 except Exception:
184 h.flash(_('Error occurred during sending pull request'),
184 h.flash(_('Error occurred during sending pull request'),
185 category='error')
185 category='error')
186 log.error(traceback.format_exc())
186 log.error(traceback.format_exc())
187 return redirect(url('pullrequest_home', repo_name=repo_name))
187 return redirect(url('pullrequest_home', repo_name=repo_name))
188
188
189 return redirect(url('pullrequest_show', repo_name=other_repo,
189 return redirect(url('pullrequest_show', repo_name=other_repo,
190 pull_request_id=pull_request.pull_request_id))
190 pull_request_id=pull_request.pull_request_id))
191
191
192 @NotAnonymous()
192 @NotAnonymous()
193 @jsonify
193 @jsonify
194 def update(self, repo_name, pull_request_id):
194 def update(self, repo_name, pull_request_id):
195 pull_request = PullRequest.get_or_404(pull_request_id)
195 pull_request = PullRequest.get_or_404(pull_request_id)
196 if pull_request.is_closed():
196 if pull_request.is_closed():
197 raise HTTPForbidden()
197 raise HTTPForbidden()
198
198 #only owner or admin can update it
199 reviewers_ids = map(int, filter(lambda v: v not in [None, ''],
199 owner = pull_request.author.user_id == c.rhodecode_user.user_id
200 request.POST.get('reviewers_ids', '').split(',')))
200 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
201 reviewers_ids = map(int, filter(lambda v: v not in [None, ''],
202 request.POST.get('reviewers_ids', '').split(',')))
201
203
202 PullRequestModel().update_reviewers(pull_request_id, reviewers_ids)
204 PullRequestModel().update_reviewers(pull_request_id, reviewers_ids)
203 Session.commit()
205 Session.commit()
204 return True
206 return True
207 raise HTTPForbidden()
205
208
206 @NotAnonymous()
209 @NotAnonymous()
207 @jsonify
210 @jsonify
208 def delete(self, repo_name, pull_request_id):
211 def delete(self, repo_name, pull_request_id):
209 pull_request = PullRequest.get_or_404(pull_request_id)
212 pull_request = PullRequest.get_or_404(pull_request_id)
210 #only owner can delete it !
213 #only owner can delete it !
211 if pull_request.author.user_id == c.rhodecode_user.user_id:
214 if pull_request.author.user_id == c.rhodecode_user.user_id:
212 PullRequestModel().delete(pull_request)
215 PullRequestModel().delete(pull_request)
213 Session().commit()
216 Session().commit()
214 h.flash(_('Successfully deleted pull request'),
217 h.flash(_('Successfully deleted pull request'),
215 category='success')
218 category='success')
216 return redirect(url('admin_settings_my_account'))
219 return redirect(url('admin_settings_my_account'))
217 else:
220 raise HTTPForbidden()
218 raise HTTPForbidden()
219
221
220 def _load_compare_data(self, pull_request, enable_comments=True):
222 def _load_compare_data(self, pull_request, enable_comments=True):
221 """
223 """
222 Load context data needed for generating compare diff
224 Load context data needed for generating compare diff
223
225
224 :param pull_request:
226 :param pull_request:
225 :type pull_request:
227 :type pull_request:
226 """
228 """
227
229
228 org_repo = pull_request.org_repo
230 org_repo = pull_request.org_repo
229 (org_ref_type,
231 (org_ref_type,
230 org_ref_name,
232 org_ref_name,
231 org_ref_rev) = pull_request.org_ref.split(':')
233 org_ref_rev) = pull_request.org_ref.split(':')
232
234
233 other_repo = pull_request.other_repo
235 other_repo = pull_request.other_repo
234 (other_ref_type,
236 (other_ref_type,
235 other_ref_name,
237 other_ref_name,
236 other_ref_rev) = pull_request.other_ref.split(':')
238 other_ref_rev) = pull_request.other_ref.split(':')
237
239
238 # despite opening revisions for bookmarks/branches/tags, we always
240 # despite opening revisions for bookmarks/branches/tags, we always
239 # convert this to rev to prevent changes after book or branch change
241 # convert this to rev to prevent changes after book or branch change
240 org_ref = ('rev', org_ref_rev)
242 org_ref = ('rev', org_ref_rev)
241 other_ref = ('rev', other_ref_rev)
243 other_ref = ('rev', other_ref_rev)
242
244
243 c.org_repo = org_repo
245 c.org_repo = org_repo
244 c.other_repo = other_repo
246 c.other_repo = other_repo
245
247
246 c.cs_ranges, discovery_data = PullRequestModel().get_compare_data(
248 c.cs_ranges, discovery_data = PullRequestModel().get_compare_data(
247 org_repo, org_ref, other_repo, other_ref
249 org_repo, org_ref, other_repo, other_ref
248 )
250 )
249
251
250 c.statuses = c.rhodecode_db_repo.statuses([x.raw_id for x in
252 c.statuses = c.rhodecode_db_repo.statuses([x.raw_id for x in
251 c.cs_ranges])
253 c.cs_ranges])
252 # defines that we need hidden inputs with changesets
254 # defines that we need hidden inputs with changesets
253 c.as_form = request.GET.get('as_form', False)
255 c.as_form = request.GET.get('as_form', False)
254
256
255 c.org_ref = org_ref[1]
257 c.org_ref = org_ref[1]
256 c.other_ref = other_ref[1]
258 c.other_ref = other_ref[1]
257 # diff needs to have swapped org with other to generate proper diff
259 # diff needs to have swapped org with other to generate proper diff
258 _diff = diffs.differ(other_repo, other_ref, org_repo, org_ref,
260 _diff = diffs.differ(other_repo, other_ref, org_repo, org_ref,
259 discovery_data)
261 discovery_data)
260 diff_processor = diffs.DiffProcessor(_diff, format='gitdiff')
262 diff_processor = diffs.DiffProcessor(_diff, format='gitdiff')
261 _parsed = diff_processor.prepare()
263 _parsed = diff_processor.prepare()
262
264
263 c.files = []
265 c.files = []
264 c.changes = {}
266 c.changes = {}
265
267
266 for f in _parsed:
268 for f in _parsed:
267 fid = h.FID('', f['filename'])
269 fid = h.FID('', f['filename'])
268 c.files.append([fid, f['operation'], f['filename'], f['stats']])
270 c.files.append([fid, f['operation'], f['filename'], f['stats']])
269 diff = diff_processor.as_html(enable_comments=enable_comments,
271 diff = diff_processor.as_html(enable_comments=enable_comments,
270 diff_lines=[f])
272 diff_lines=[f])
271 c.changes[fid] = [f['operation'], f['filename'], diff]
273 c.changes[fid] = [f['operation'], f['filename'], diff]
272
274
273 def show(self, repo_name, pull_request_id):
275 def show(self, repo_name, pull_request_id):
274 repo_model = RepoModel()
276 repo_model = RepoModel()
275 c.users_array = repo_model.get_users_js()
277 c.users_array = repo_model.get_users_js()
276 c.users_groups_array = repo_model.get_users_groups_js()
278 c.users_groups_array = repo_model.get_users_groups_js()
277 c.pull_request = PullRequest.get_or_404(pull_request_id)
279 c.pull_request = PullRequest.get_or_404(pull_request_id)
278
280
279 cc_model = ChangesetCommentsModel()
281 cc_model = ChangesetCommentsModel()
280 cs_model = ChangesetStatusModel()
282 cs_model = ChangesetStatusModel()
281 _cs_statuses = cs_model.get_statuses(c.pull_request.org_repo,
283 _cs_statuses = cs_model.get_statuses(c.pull_request.org_repo,
282 pull_request=c.pull_request,
284 pull_request=c.pull_request,
283 with_revisions=True)
285 with_revisions=True)
284
286
285 cs_statuses = defaultdict(list)
287 cs_statuses = defaultdict(list)
286 for st in _cs_statuses:
288 for st in _cs_statuses:
287 cs_statuses[st.author.username] += [st]
289 cs_statuses[st.author.username] += [st]
288
290
289 c.pull_request_reviewers = []
291 c.pull_request_reviewers = []
290 c.pull_request_pending_reviewers = []
292 c.pull_request_pending_reviewers = []
291 for o in c.pull_request.reviewers:
293 for o in c.pull_request.reviewers:
292 st = cs_statuses.get(o.user.username, None)
294 st = cs_statuses.get(o.user.username, None)
293 if st:
295 if st:
294 sorter = lambda k: k.version
296 sorter = lambda k: k.version
295 st = [(x, list(y)[0])
297 st = [(x, list(y)[0])
296 for x, y in (groupby(sorted(st, key=sorter), sorter))]
298 for x, y in (groupby(sorted(st, key=sorter), sorter))]
297 else:
299 else:
298 c.pull_request_pending_reviewers.append(o.user)
300 c.pull_request_pending_reviewers.append(o.user)
299 c.pull_request_reviewers.append([o.user, st])
301 c.pull_request_reviewers.append([o.user, st])
300
302
301 # pull_requests repo_name we opened it against
303 # pull_requests repo_name we opened it against
302 # ie. other_repo must match
304 # ie. other_repo must match
303 if repo_name != c.pull_request.other_repo.repo_name:
305 if repo_name != c.pull_request.other_repo.repo_name:
304 raise HTTPNotFound
306 raise HTTPNotFound
305
307
306 # load compare data into template context
308 # load compare data into template context
307 enable_comments = not c.pull_request.is_closed()
309 enable_comments = not c.pull_request.is_closed()
308 self._load_compare_data(c.pull_request, enable_comments=enable_comments)
310 self._load_compare_data(c.pull_request, enable_comments=enable_comments)
309
311
310 # inline comments
312 # inline comments
311 c.inline_cnt = 0
313 c.inline_cnt = 0
312 c.inline_comments = cc_model.get_inline_comments(
314 c.inline_comments = cc_model.get_inline_comments(
313 c.rhodecode_db_repo.repo_id,
315 c.rhodecode_db_repo.repo_id,
314 pull_request=pull_request_id)
316 pull_request=pull_request_id)
315 # count inline comments
317 # count inline comments
316 for __, lines in c.inline_comments:
318 for __, lines in c.inline_comments:
317 for comments in lines.values():
319 for comments in lines.values():
318 c.inline_cnt += len(comments)
320 c.inline_cnt += len(comments)
319 # comments
321 # comments
320 c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id,
322 c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id,
321 pull_request=pull_request_id)
323 pull_request=pull_request_id)
322
324
323 # changeset(pull-request) status
325 # changeset(pull-request) status
324 c.current_changeset_status = cs_model.calculate_status(
326 c.current_changeset_status = cs_model.calculate_status(
325 c.pull_request_reviewers
327 c.pull_request_reviewers
326 )
328 )
327 c.changeset_statuses = ChangesetStatus.STATUSES
329 c.changeset_statuses = ChangesetStatus.STATUSES
328 c.target_repo = c.pull_request.org_repo.repo_name
330 c.target_repo = c.pull_request.org_repo.repo_name
329 return render('/pullrequests/pullrequest_show.html')
331 return render('/pullrequests/pullrequest_show.html')
330
332
331 @NotAnonymous()
333 @NotAnonymous()
332 @jsonify
334 @jsonify
333 def comment(self, repo_name, pull_request_id):
335 def comment(self, repo_name, pull_request_id):
334 pull_request = PullRequest.get_or_404(pull_request_id)
336 pull_request = PullRequest.get_or_404(pull_request_id)
335 if pull_request.is_closed():
337 if pull_request.is_closed():
336 raise HTTPForbidden()
338 raise HTTPForbidden()
337
339
338 status = request.POST.get('changeset_status')
340 status = request.POST.get('changeset_status')
339 change_status = request.POST.get('change_changeset_status')
341 change_status = request.POST.get('change_changeset_status')
340
342
341 comm = ChangesetCommentsModel().create(
343 comm = ChangesetCommentsModel().create(
342 text=request.POST.get('text'),
344 text=request.POST.get('text'),
343 repo=c.rhodecode_db_repo.repo_id,
345 repo=c.rhodecode_db_repo.repo_id,
344 user=c.rhodecode_user.user_id,
346 user=c.rhodecode_user.user_id,
345 pull_request=pull_request_id,
347 pull_request=pull_request_id,
346 f_path=request.POST.get('f_path'),
348 f_path=request.POST.get('f_path'),
347 line_no=request.POST.get('line'),
349 line_no=request.POST.get('line'),
348 status_change=(ChangesetStatus.get_status_lbl(status)
350 status_change=(ChangesetStatus.get_status_lbl(status)
349 if status and change_status else None)
351 if status and change_status else None)
350 )
352 )
351
353
352 # get status if set !
354 # get status if set !
353 if status and change_status:
355 if status and change_status:
354 ChangesetStatusModel().set_status(
356 ChangesetStatusModel().set_status(
355 c.rhodecode_db_repo.repo_id,
357 c.rhodecode_db_repo.repo_id,
356 status,
358 status,
357 c.rhodecode_user.user_id,
359 c.rhodecode_user.user_id,
358 comm,
360 comm,
359 pull_request=pull_request_id
361 pull_request=pull_request_id
360 )
362 )
361 action_logger(self.rhodecode_user,
363 action_logger(self.rhodecode_user,
362 'user_commented_pull_request:%s' % pull_request_id,
364 'user_commented_pull_request:%s' % pull_request_id,
363 c.rhodecode_db_repo, self.ip_addr, self.sa)
365 c.rhodecode_db_repo, self.ip_addr, self.sa)
364
366
365 if request.POST.get('save_close'):
367 if request.POST.get('save_close'):
366 PullRequestModel().close_pull_request(pull_request_id)
368 PullRequestModel().close_pull_request(pull_request_id)
367 action_logger(self.rhodecode_user,
369 action_logger(self.rhodecode_user,
368 'user_closed_pull_request:%s' % pull_request_id,
370 'user_closed_pull_request:%s' % pull_request_id,
369 c.rhodecode_db_repo, self.ip_addr, self.sa)
371 c.rhodecode_db_repo, self.ip_addr, self.sa)
370
372
371 Session().commit()
373 Session().commit()
372
374
373 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
375 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
374 return redirect(h.url('pullrequest_show', repo_name=repo_name,
376 return redirect(h.url('pullrequest_show', repo_name=repo_name,
375 pull_request_id=pull_request_id))
377 pull_request_id=pull_request_id))
376
378
377 data = {
379 data = {
378 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
380 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
379 }
381 }
380 if comm:
382 if comm:
381 c.co = comm
383 c.co = comm
382 data.update(comm.get_dict())
384 data.update(comm.get_dict())
383 data.update({'rendered_text':
385 data.update({'rendered_text':
384 render('changeset/changeset_comment_block.html')})
386 render('changeset/changeset_comment_block.html')})
385
387
386 return data
388 return data
387
389
388 @NotAnonymous()
390 @NotAnonymous()
389 @jsonify
391 @jsonify
390 def delete_comment(self, repo_name, comment_id):
392 def delete_comment(self, repo_name, comment_id):
391 co = ChangesetComment.get(comment_id)
393 co = ChangesetComment.get(comment_id)
392 if co.pull_request.is_closed():
394 if co.pull_request.is_closed():
393 #don't allow deleting comments on closed pull request
395 #don't allow deleting comments on closed pull request
394 raise HTTPForbidden()
396 raise HTTPForbidden()
395
397
396 owner = lambda: co.author.user_id == c.rhodecode_user.user_id
398 owner = lambda: co.author.user_id == c.rhodecode_user.user_id
397 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
399 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
398 ChangesetCommentsModel().delete(comment=co)
400 ChangesetCommentsModel().delete(comment=co)
399 Session().commit()
401 Session().commit()
400 return True
402 return True
401 else:
403 else:
402 raise HTTPForbidden()
404 raise HTTPForbidden()
@@ -1,173 +1,186 b''
1 import logging
1 import logging
2 import datetime
2 import datetime
3
3
4 from sqlalchemy import *
4 from sqlalchemy import *
5 from sqlalchemy.exc import DatabaseError
5 from sqlalchemy.exc import DatabaseError
6 from sqlalchemy.orm import relation, backref, class_mapper
6 from sqlalchemy.orm import relation, backref, class_mapper
7 from sqlalchemy.orm.session import Session
7 from sqlalchemy.orm.session import Session
8 from sqlalchemy.ext.declarative import declarative_base
8 from sqlalchemy.ext.declarative import declarative_base
9
9
10 from rhodecode.lib.dbmigrate.migrate import *
10 from rhodecode.lib.dbmigrate.migrate import *
11 from rhodecode.lib.dbmigrate.migrate.changeset import *
11 from rhodecode.lib.dbmigrate.migrate.changeset import *
12
12
13 from rhodecode.model.meta import Base
13 from rhodecode.model.meta import Base
14 from rhodecode.model import meta
14 from rhodecode.model import meta
15
15
16 log = logging.getLogger(__name__)
16 log = logging.getLogger(__name__)
17
17
18
18
19 def upgrade(migrate_engine):
19 def upgrade(migrate_engine):
20 """
20 """
21 Upgrade operations go here.
21 Upgrade operations go here.
22 Don't create your own engine; bind migrate_engine to your metadata
22 Don't create your own engine; bind migrate_engine to your metadata
23 """
23 """
24
24
25 #==========================================================================
25 #==========================================================================
26 # USEREMAILMAP
26 # USEREMAILMAP
27 #==========================================================================
27 #==========================================================================
28 from rhodecode.lib.dbmigrate.schema.db_1_4_0 import UserEmailMap
28 from rhodecode.lib.dbmigrate.schema.db_1_4_0 import UserEmailMap
29 tbl = UserEmailMap.__table__
29 tbl = UserEmailMap.__table__
30 tbl.create()
30 tbl.create()
31 #==========================================================================
31 #==========================================================================
32 # PULL REQUEST
32 # PULL REQUEST
33 #==========================================================================
33 #==========================================================================
34 from rhodecode.lib.dbmigrate.schema.db_1_4_0 import PullRequest
34 from rhodecode.lib.dbmigrate.schema.db_1_4_0 import PullRequest
35 tbl = PullRequest.__table__
35 tbl = PullRequest.__table__
36 tbl.create()
36 tbl.create()
37
37
38 #==========================================================================
38 #==========================================================================
39 # PULL REQUEST REVIEWERS
39 # PULL REQUEST REVIEWERS
40 #==========================================================================
40 #==========================================================================
41 from rhodecode.lib.dbmigrate.schema.db_1_4_0 import PullRequestReviewers
41 from rhodecode.lib.dbmigrate.schema.db_1_4_0 import PullRequestReviewers
42 tbl = PullRequestReviewers.__table__
42 tbl = PullRequestReviewers.__table__
43 tbl.create()
43 tbl.create()
44
44
45 #==========================================================================
45 #==========================================================================
46 # CHANGESET STATUS
46 # CHANGESET STATUS
47 #==========================================================================
47 #==========================================================================
48 from rhodecode.lib.dbmigrate.schema.db_1_4_0 import ChangesetStatus
48 from rhodecode.lib.dbmigrate.schema.db_1_4_0 import ChangesetStatus
49 tbl = ChangesetStatus.__table__
49 tbl = ChangesetStatus.__table__
50 tbl.create()
50 tbl.create()
51
51
52 ## RESET COMPLETLY THE metadata for sqlalchemy to use the 1_3_0 Base
52 ## RESET COMPLETLY THE metadata for sqlalchemy to use the 1_3_0 Base
53 Base = declarative_base()
53 Base = declarative_base()
54 Base.metadata.clear()
54 Base.metadata.clear()
55 Base.metadata = MetaData()
55 Base.metadata = MetaData()
56 Base.metadata.bind = migrate_engine
56 Base.metadata.bind = migrate_engine
57 meta.Base = Base
57 meta.Base = Base
58
58
59 #==========================================================================
59 #==========================================================================
60 # USERS TABLE
60 # USERS TABLE
61 #==========================================================================
61 #==========================================================================
62 from rhodecode.lib.dbmigrate.schema.db_1_3_0 import User
62 from rhodecode.lib.dbmigrate.schema.db_1_3_0 import User
63 tbl = User.__table__
63 tbl = User.__table__
64
64
65 # change column name -> firstname
65 # change column name -> firstname
66 col = User.__table__.columns.name
66 col = User.__table__.columns.name
67 col.alter(index=Index('u_username_idx', 'username'))
67 col.alter(index=Index('u_username_idx', 'username'))
68 col.alter(index=Index('u_email_idx', 'email'))
68 col.alter(index=Index('u_email_idx', 'email'))
69 col.alter(name="firstname", table=tbl)
69 col.alter(name="firstname", table=tbl)
70
70
71 inherit_default_permissions = Column("users_group_inherit_default_permission",
71 # add inherit_default_permission column
72 Boolean(), nullable=True, unique=None,
73 default=True)
74 inherit_default_permissions.create(table=tbl)
75 inherit_default_permissions.alter(nullable=False, default=True, table=tbl)
76
77 #==========================================================================
78 # GROUPS TABLE
79 #==========================================================================
80 from rhodecode.lib.dbmigrate.schema.db_1_3_0 import RepoGroup
81 tbl = RepoGroup.__table__
82 inherit_default_permissions = Column("inherit_default_permissions",
72 inherit_default_permissions = Column("inherit_default_permissions",
83 Boolean(), nullable=True, unique=None,
73 Boolean(), nullable=True, unique=None,
84 default=True)
74 default=True)
85 inherit_default_permissions.create(table=tbl)
75 inherit_default_permissions.create(table=tbl)
86 inherit_default_permissions.alter(nullable=False, default=True, table=tbl)
76 inherit_default_permissions.alter(nullable=False, default=True, table=tbl)
87
77
88 #==========================================================================
78 #==========================================================================
79 # USERS GROUP TABLE
80 #==========================================================================
81 from rhodecode.lib.dbmigrate.schema.db_1_3_0 import UsersGroup
82 tbl = UsersGroup.__table__
83 # add inherit_default_permission column
84 gr_inherit_default_permissions = Column(
85 "users_group_inherit_default_permissions",
86 Boolean(), nullable=True, unique=None,
87 default=True)
88 gr_inherit_default_permissions.create(table=tbl)
89 gr_inherit_default_permissions.alter(nullable=False, default=True, table=tbl)
90
91 #==========================================================================
89 # REPOSITORIES
92 # REPOSITORIES
90 #==========================================================================
93 #==========================================================================
91 from rhodecode.lib.dbmigrate.schema.db_1_3_0 import Repository
94 from rhodecode.lib.dbmigrate.schema.db_1_3_0 import Repository
92 tbl = Repository.__table__
95 tbl = Repository.__table__
93
96
97 # add enable locking column
94 enable_locking = Column("enable_locking", Boolean(), nullable=True,
98 enable_locking = Column("enable_locking", Boolean(), nullable=True,
95 unique=None, default=False)
99 unique=None, default=False)
96 enable_locking.create(table=tbl)
100 enable_locking.create(table=tbl)
97 enable_locking.alter(nullable=False, default=False, table=tbl)
101 enable_locking.alter(nullable=False, default=False, table=tbl)
98
102
103 # add locked column
99 _locked = Column("locked", String(255), nullable=True, unique=False,
104 _locked = Column("locked", String(255), nullable=True, unique=False,
100 default=None)
105 default=None)
101 _locked.create(table=tbl)
106 _locked.create(table=tbl)
102
107
108 #add langing revision column
103 landing_rev = Column("landing_revision", String(255), nullable=True,
109 landing_rev = Column("landing_revision", String(255), nullable=True,
104 unique=False, default='tip')
110 unique=False, default='tip')
105 landing_rev.create(table=tbl)
111 landing_rev.create(table=tbl)
106 landing_rev.alter(nullable=False, default='tip', table=tbl)
112 landing_rev.alter(nullable=False, default='tip', table=tbl)
107
113
108 #==========================================================================
114 #==========================================================================
109 # GROUPS
115 # GROUPS
110 #==========================================================================
116 #==========================================================================
111 from rhodecode.lib.dbmigrate.schema.db_1_3_0 import RepoGroup
117 from rhodecode.lib.dbmigrate.schema.db_1_3_0 import RepoGroup
112 tbl = RepoGroup.__table__
118 tbl = RepoGroup.__table__
119
120 # add enable locking column
113 enable_locking = Column("enable_locking", Boolean(), nullable=True,
121 enable_locking = Column("enable_locking", Boolean(), nullable=True,
114 unique=None, default=False)
122 unique=None, default=False)
115 enable_locking.create(table=tbl)
123 enable_locking.create(table=tbl)
116 enable_locking.alter(nullable=False, default=False)
124 enable_locking.alter(nullable=False, default=False)
117
125
118 #==========================================================================
126 #==========================================================================
119 # CACHE INVALIDATION
127 # CACHE INVALIDATION
120 #==========================================================================
128 #==========================================================================
121 from rhodecode.lib.dbmigrate.schema.db_1_3_0 import CacheInvalidation
129 from rhodecode.lib.dbmigrate.schema.db_1_3_0 import CacheInvalidation
122 tbl = CacheInvalidation.__table__
130 tbl = CacheInvalidation.__table__
123
131
124 # change column name -> firstname
132 # add INDEX for cache keys
125 col = CacheInvalidation.__table__.columns.cache_key
133 col = CacheInvalidation.__table__.columns.cache_key
126 col.alter(index=Index('key_idx', 'cache_key'))
134 col.alter(index=Index('key_idx', 'cache_key'))
127
135
128 #==========================================================================
136 #==========================================================================
129 # NOTIFICATION
137 # NOTIFICATION
130 #==========================================================================
138 #==========================================================================
131 from rhodecode.lib.dbmigrate.schema.db_1_3_0 import Notification
139 from rhodecode.lib.dbmigrate.schema.db_1_3_0 import Notification
132 tbl = Notification.__table__
140 tbl = Notification.__table__
133
141
134 # change column name -> firstname
142 # add index for notification type
135 col = Notification.__table__.columns.type
143 col = Notification.__table__.columns.type
136 col.alter(index=Index('notification_type_idx', 'type'),)
144 col.alter(index=Index('notification_type_idx', 'type'),)
137
145
138 #==========================================================================
146 #==========================================================================
139 # CHANGESET_COMMENTS
147 # CHANGESET_COMMENTS
140 #==========================================================================
148 #==========================================================================
141 from rhodecode.lib.dbmigrate.schema.db_1_3_0 import ChangesetComment
149 from rhodecode.lib.dbmigrate.schema.db_1_3_0 import ChangesetComment
142
150
143 tbl = ChangesetComment.__table__
151 tbl = ChangesetComment.__table__
152 col = ChangesetComment.__table__.columns.revision
144
153
145 col = ChangesetComment.__table__.columns.revision
154 # add index for revisions
146 col.alter(index=Index('cc_revision_idx', 'revision'),)
155 col.alter(index=Index('cc_revision_idx', 'revision'),)
147
156
157 # add hl_lines column
148 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
158 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
149 hl_lines.create(table=tbl)
159 hl_lines.create(table=tbl)
150
160
161 # add created_on column
151 created_on = Column('created_on', DateTime(timezone=False), nullable=True,
162 created_on = Column('created_on', DateTime(timezone=False), nullable=True,
152 default=datetime.datetime.now)
163 default=datetime.datetime.now)
153 created_on.create(table=tbl)
164 created_on.create(table=tbl)
154 created_on.alter(nullable=False, default=datetime.datetime.now)
165 created_on.alter(nullable=False, default=datetime.datetime.now)
166
155 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False,
167 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False,
156 default=datetime.datetime.now)
168 default=datetime.datetime.now)
157 modified_at.alter(type=DateTime(timezone=False), table=tbl)
169 modified_at.alter(type=DateTime(timezone=False), table=tbl)
158
170
171 # add FK to pull_request
159 pull_request_id = Column("pull_request_id", Integer(),
172 pull_request_id = Column("pull_request_id", Integer(),
160 ForeignKey('pull_requests.pull_request_id'),
173 ForeignKey('pull_requests.pull_request_id'),
161 nullable=True)
174 nullable=True)
162 pull_request_id.create(table=tbl)
175 pull_request_id.create(table=tbl)
163 ## RESET COMPLETLY THE metadata for sqlalchemy back after using 1_3_0
176 ## RESET COMPLETLY THE metadata for sqlalchemy back after using 1_3_0
164 Base = declarative_base()
177 Base = declarative_base()
165 Base.metadata.clear()
178 Base.metadata.clear()
166 Base.metadata = MetaData()
179 Base.metadata = MetaData()
167 Base.metadata.bind = migrate_engine
180 Base.metadata.bind = migrate_engine
168 meta.Base = Base
181 meta.Base = Base
169
182
170
183
171 def downgrade(migrate_engine):
184 def downgrade(migrate_engine):
172 meta = MetaData()
185 meta = MetaData()
173 meta.bind = migrate_engine
186 meta.bind = migrate_engine
General Comments 0
You need to be logged in to leave comments. Login now