##// END OF EJS Templates
Load generated revs while switching to other sources of pull-requests....
marcink -
r2720:0f7355d3 beta
parent child Browse files
Show More
@@ -1,382 +1,388 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 c.other_refs = c.org_refs
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
110 c.default_revs = self._get_repo_refs(org_repo.scm_instance)
109 #add orginal repo
111 #add orginal repo
110 other_repos_info[org_repo.repo_name] = {
112 other_repos_info[org_repo.repo_name] = {
111 'gravatar': h.gravatar_url(org_repo.user.email, 24),
113 'gravatar': h.gravatar_url(org_repo.user.email, 24),
112 'description': org_repo.description
114 'description': org_repo.description,
115 'revs': h.select('other_ref', '', c.default_revs, class_='refs')
113 }
116 }
114
117
115 c.default_pull_request = org_repo.repo_name
116 #gather forks and add to this list
118 #gather forks and add to this list
117 for fork in org_repo.forks:
119 for fork in org_repo.forks:
118 c.other_repos.append((fork.repo_name, '%s/%s' % (
120 c.other_repos.append((fork.repo_name, '%s/%s' % (
119 fork.user.username, fork.repo_name))
121 fork.user.username, fork.repo_name))
120 )
122 )
121 other_repos_info[fork.repo_name] = {
123 other_repos_info[fork.repo_name] = {
122 'gravatar': h.gravatar_url(fork.user.email, 24),
124 'gravatar': h.gravatar_url(fork.user.email, 24),
123 'description': fork.description
125 'description': fork.description,
126 'revs': h.select('other_ref', '',
127 self._get_repo_refs(fork.scm_instance),
128 class_='refs')
124 }
129 }
125 #add parents of this fork also
130 #add parents of this fork also
126 if org_repo.parent:
131 if org_repo.parent:
127 c.default_pull_request = org_repo.parent.repo_name
132 c.default_pull_request = org_repo.parent.repo_name
128 c.other_repos.append((org_repo.parent.repo_name, '%s/%s' % (
133 c.other_repos.append((org_repo.parent.repo_name, '%s/%s' % (
129 org_repo.parent.user.username,
134 org_repo.parent.user.username,
130 org_repo.parent.repo_name))
135 org_repo.parent.repo_name))
131 )
136 )
132 other_repos_info[org_repo.parent.repo_name] = {
137 other_repos_info[org_repo.parent.repo_name] = {
133 'gravatar': h.gravatar_url(org_repo.parent.user.email, 24),
138 'gravatar': h.gravatar_url(org_repo.parent.user.email, 24),
134 'description': org_repo.parent.description
139 'description': org_repo.parent.description,
140 'revs': h.select('other_ref', '',
141 self._get_repo_refs(org_repo.parent.scm_instance),
142 class_='refs')
135 }
143 }
136
144
137 c.other_repos_info = json.dumps(other_repos_info)
145 c.other_repos_info = json.dumps(other_repos_info)
138 c.review_members = [org_repo.user]
146 c.review_members = [org_repo.user]
139 return render('/pullrequests/pullrequest.html')
147 return render('/pullrequests/pullrequest.html')
140
148
141 @NotAnonymous()
149 @NotAnonymous()
142 def create(self, repo_name):
150 def create(self, repo_name):
143
144 try:
151 try:
145 _form = PullRequestForm()().to_python(request.POST)
152 _form = PullRequestForm()().to_python(request.POST)
146 except formencode.Invalid, errors:
153 except formencode.Invalid, errors:
147 log.error(traceback.format_exc())
154 log.error(traceback.format_exc())
148 if errors.error_dict.get('revisions'):
155 if errors.error_dict.get('revisions'):
149 msg = _('Cannot open a pull request with '
156 msg = 'Revisions: %s' % errors.error_dict['revisions']
150 'empty list of changesets')
151 elif errors.error_dict.get('pullrequest_title'):
157 elif errors.error_dict.get('pullrequest_title'):
152 msg = _('Pull request requires a title with min. 3 chars')
158 msg = _('Pull request requires a title with min. 3 chars')
153 else:
159 else:
154 msg = _('error during creation of pull request')
160 msg = _('error during creation of pull request')
155
161
156 h.flash(msg, 'error')
162 h.flash(msg, 'error')
157 return redirect(url('pullrequest_home', repo_name=repo_name))
163 return redirect(url('pullrequest_home', repo_name=repo_name))
158
164
159 org_repo = _form['org_repo']
165 org_repo = _form['org_repo']
160 org_ref = _form['org_ref']
166 org_ref = _form['org_ref']
161 other_repo = _form['other_repo']
167 other_repo = _form['other_repo']
162 other_ref = _form['other_ref']
168 other_ref = _form['other_ref']
163 revisions = _form['revisions']
169 revisions = _form['revisions']
164 reviewers = _form['review_members']
170 reviewers = _form['review_members']
165
171
166 title = _form['pullrequest_title']
172 title = _form['pullrequest_title']
167 description = _form['pullrequest_desc']
173 description = _form['pullrequest_desc']
168
174
169 try:
175 try:
170 pull_request = PullRequestModel().create(
176 pull_request = PullRequestModel().create(
171 self.rhodecode_user.user_id, org_repo, org_ref, other_repo,
177 self.rhodecode_user.user_id, org_repo, org_ref, other_repo,
172 other_ref, revisions, reviewers, title, description
178 other_ref, revisions, reviewers, title, description
173 )
179 )
174 Session().commit()
180 Session().commit()
175 h.flash(_('Successfully opened new pull request'),
181 h.flash(_('Successfully opened new pull request'),
176 category='success')
182 category='success')
177 except Exception:
183 except Exception:
178 h.flash(_('Error occurred during sending pull request'),
184 h.flash(_('Error occurred during sending pull request'),
179 category='error')
185 category='error')
180 log.error(traceback.format_exc())
186 log.error(traceback.format_exc())
181 return redirect(url('pullrequest_home', repo_name=repo_name))
187 return redirect(url('pullrequest_home', repo_name=repo_name))
182
188
183 return redirect(url('pullrequest_show', repo_name=other_repo,
189 return redirect(url('pullrequest_show', repo_name=other_repo,
184 pull_request_id=pull_request.pull_request_id))
190 pull_request_id=pull_request.pull_request_id))
185
191
186 @NotAnonymous()
192 @NotAnonymous()
187 @jsonify
193 @jsonify
188 def update(self, repo_name, pull_request_id):
194 def update(self, repo_name, pull_request_id):
189 pull_request = PullRequest.get_or_404(pull_request_id)
195 pull_request = PullRequest.get_or_404(pull_request_id)
190 if pull_request.is_closed():
196 if pull_request.is_closed():
191 raise HTTPForbidden()
197 raise HTTPForbidden()
192
198
193 reviewers_ids = map(int, filter(lambda v: v not in [None, ''],
199 reviewers_ids = map(int, filter(lambda v: v not in [None, ''],
194 request.POST.get('reviewers_ids', '').split(',')))
200 request.POST.get('reviewers_ids', '').split(',')))
195
201
196 PullRequestModel().update_reviewers(pull_request_id, reviewers_ids)
202 PullRequestModel().update_reviewers(pull_request_id, reviewers_ids)
197 Session.commit()
203 Session.commit()
198 return True
204 return True
199
205
200 def _load_compare_data(self, pull_request, enable_comments=True):
206 def _load_compare_data(self, pull_request, enable_comments=True):
201 """
207 """
202 Load context data needed for generating compare diff
208 Load context data needed for generating compare diff
203
209
204 :param pull_request:
210 :param pull_request:
205 :type pull_request:
211 :type pull_request:
206 """
212 """
207
213
208 org_repo = pull_request.org_repo
214 org_repo = pull_request.org_repo
209 (org_ref_type,
215 (org_ref_type,
210 org_ref_name,
216 org_ref_name,
211 org_ref_rev) = pull_request.org_ref.split(':')
217 org_ref_rev) = pull_request.org_ref.split(':')
212
218
213 other_repo = pull_request.other_repo
219 other_repo = pull_request.other_repo
214 (other_ref_type,
220 (other_ref_type,
215 other_ref_name,
221 other_ref_name,
216 other_ref_rev) = pull_request.other_ref.split(':')
222 other_ref_rev) = pull_request.other_ref.split(':')
217
223
218 # dispite opening revisions for bookmarks/branches/tags, we always
224 # despite opening revisions for bookmarks/branches/tags, we always
219 # convert this to rev to prevent changes after book or branch change
225 # convert this to rev to prevent changes after book or branch change
220 org_ref = ('rev', org_ref_rev)
226 org_ref = ('rev', org_ref_rev)
221 other_ref = ('rev', other_ref_rev)
227 other_ref = ('rev', other_ref_rev)
222
228
223 c.org_repo = org_repo
229 c.org_repo = org_repo
224 c.other_repo = other_repo
230 c.other_repo = other_repo
225
231
226 c.cs_ranges, discovery_data = PullRequestModel().get_compare_data(
232 c.cs_ranges, discovery_data = PullRequestModel().get_compare_data(
227 org_repo, org_ref, other_repo, other_ref
233 org_repo, org_ref, other_repo, other_ref
228 )
234 )
229
235
230 c.statuses = c.rhodecode_db_repo.statuses([x.raw_id for x in
236 c.statuses = c.rhodecode_db_repo.statuses([x.raw_id for x in
231 c.cs_ranges])
237 c.cs_ranges])
232 # defines that we need hidden inputs with changesets
238 # defines that we need hidden inputs with changesets
233 c.as_form = request.GET.get('as_form', False)
239 c.as_form = request.GET.get('as_form', False)
234
240
235 c.org_ref = org_ref[1]
241 c.org_ref = org_ref[1]
236 c.other_ref = other_ref[1]
242 c.other_ref = other_ref[1]
237 # diff needs to have swapped org with other to generate proper diff
243 # diff needs to have swapped org with other to generate proper diff
238 _diff = diffs.differ(other_repo, other_ref, org_repo, org_ref,
244 _diff = diffs.differ(other_repo, other_ref, org_repo, org_ref,
239 discovery_data)
245 discovery_data)
240 diff_processor = diffs.DiffProcessor(_diff, format='gitdiff')
246 diff_processor = diffs.DiffProcessor(_diff, format='gitdiff')
241 _parsed = diff_processor.prepare()
247 _parsed = diff_processor.prepare()
242
248
243 c.files = []
249 c.files = []
244 c.changes = {}
250 c.changes = {}
245
251
246 for f in _parsed:
252 for f in _parsed:
247 fid = h.FID('', f['filename'])
253 fid = h.FID('', f['filename'])
248 c.files.append([fid, f['operation'], f['filename'], f['stats']])
254 c.files.append([fid, f['operation'], f['filename'], f['stats']])
249 diff = diff_processor.as_html(enable_comments=enable_comments,
255 diff = diff_processor.as_html(enable_comments=enable_comments,
250 diff_lines=[f])
256 diff_lines=[f])
251 c.changes[fid] = [f['operation'], f['filename'], diff]
257 c.changes[fid] = [f['operation'], f['filename'], diff]
252
258
253 def show(self, repo_name, pull_request_id):
259 def show(self, repo_name, pull_request_id):
254 repo_model = RepoModel()
260 repo_model = RepoModel()
255 c.users_array = repo_model.get_users_js()
261 c.users_array = repo_model.get_users_js()
256 c.users_groups_array = repo_model.get_users_groups_js()
262 c.users_groups_array = repo_model.get_users_groups_js()
257 c.pull_request = PullRequest.get_or_404(pull_request_id)
263 c.pull_request = PullRequest.get_or_404(pull_request_id)
258
264
259 cc_model = ChangesetCommentsModel()
265 cc_model = ChangesetCommentsModel()
260 cs_model = ChangesetStatusModel()
266 cs_model = ChangesetStatusModel()
261 _cs_statuses = cs_model.get_statuses(c.pull_request.org_repo,
267 _cs_statuses = cs_model.get_statuses(c.pull_request.org_repo,
262 pull_request=c.pull_request,
268 pull_request=c.pull_request,
263 with_revisions=True)
269 with_revisions=True)
264
270
265 cs_statuses = defaultdict(list)
271 cs_statuses = defaultdict(list)
266 for st in _cs_statuses:
272 for st in _cs_statuses:
267 cs_statuses[st.author.username] += [st]
273 cs_statuses[st.author.username] += [st]
268
274
269 c.pull_request_reviewers = []
275 c.pull_request_reviewers = []
270 c.pull_request_pending_reviewers = []
276 c.pull_request_pending_reviewers = []
271 for o in c.pull_request.reviewers:
277 for o in c.pull_request.reviewers:
272 st = cs_statuses.get(o.user.username, None)
278 st = cs_statuses.get(o.user.username, None)
273 if st:
279 if st:
274 sorter = lambda k: k.version
280 sorter = lambda k: k.version
275 st = [(x, list(y)[0])
281 st = [(x, list(y)[0])
276 for x, y in (groupby(sorted(st, key=sorter), sorter))]
282 for x, y in (groupby(sorted(st, key=sorter), sorter))]
277 else:
283 else:
278 c.pull_request_pending_reviewers.append(o.user)
284 c.pull_request_pending_reviewers.append(o.user)
279 c.pull_request_reviewers.append([o.user, st])
285 c.pull_request_reviewers.append([o.user, st])
280
286
281 # pull_requests repo_name we opened it against
287 # pull_requests repo_name we opened it against
282 # ie. other_repo must match
288 # ie. other_repo must match
283 if repo_name != c.pull_request.other_repo.repo_name:
289 if repo_name != c.pull_request.other_repo.repo_name:
284 raise HTTPNotFound
290 raise HTTPNotFound
285
291
286 # load compare data into template context
292 # load compare data into template context
287 enable_comments = not c.pull_request.is_closed()
293 enable_comments = not c.pull_request.is_closed()
288 self._load_compare_data(c.pull_request, enable_comments=enable_comments)
294 self._load_compare_data(c.pull_request, enable_comments=enable_comments)
289
295
290 # inline comments
296 # inline comments
291 c.inline_cnt = 0
297 c.inline_cnt = 0
292 c.inline_comments = cc_model.get_inline_comments(
298 c.inline_comments = cc_model.get_inline_comments(
293 c.rhodecode_db_repo.repo_id,
299 c.rhodecode_db_repo.repo_id,
294 pull_request=pull_request_id)
300 pull_request=pull_request_id)
295 # count inline comments
301 # count inline comments
296 for __, lines in c.inline_comments:
302 for __, lines in c.inline_comments:
297 for comments in lines.values():
303 for comments in lines.values():
298 c.inline_cnt += len(comments)
304 c.inline_cnt += len(comments)
299 # comments
305 # comments
300 c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id,
306 c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id,
301 pull_request=pull_request_id)
307 pull_request=pull_request_id)
302
308
303 # changeset(pull-request) status
309 # changeset(pull-request) status
304 c.current_changeset_status = cs_model.calculate_status(
310 c.current_changeset_status = cs_model.calculate_status(
305 c.pull_request_reviewers
311 c.pull_request_reviewers
306 )
312 )
307 c.changeset_statuses = ChangesetStatus.STATUSES
313 c.changeset_statuses = ChangesetStatus.STATUSES
308 c.target_repo = c.pull_request.org_repo.repo_name
314 c.target_repo = c.pull_request.org_repo.repo_name
309 return render('/pullrequests/pullrequest_show.html')
315 return render('/pullrequests/pullrequest_show.html')
310
316
311 @NotAnonymous()
317 @NotAnonymous()
312 @jsonify
318 @jsonify
313 def comment(self, repo_name, pull_request_id):
319 def comment(self, repo_name, pull_request_id):
314 pull_request = PullRequest.get_or_404(pull_request_id)
320 pull_request = PullRequest.get_or_404(pull_request_id)
315 if pull_request.is_closed():
321 if pull_request.is_closed():
316 raise HTTPForbidden()
322 raise HTTPForbidden()
317
323
318 status = request.POST.get('changeset_status')
324 status = request.POST.get('changeset_status')
319 change_status = request.POST.get('change_changeset_status')
325 change_status = request.POST.get('change_changeset_status')
320
326
321 comm = ChangesetCommentsModel().create(
327 comm = ChangesetCommentsModel().create(
322 text=request.POST.get('text'),
328 text=request.POST.get('text'),
323 repo=c.rhodecode_db_repo.repo_id,
329 repo=c.rhodecode_db_repo.repo_id,
324 user=c.rhodecode_user.user_id,
330 user=c.rhodecode_user.user_id,
325 pull_request=pull_request_id,
331 pull_request=pull_request_id,
326 f_path=request.POST.get('f_path'),
332 f_path=request.POST.get('f_path'),
327 line_no=request.POST.get('line'),
333 line_no=request.POST.get('line'),
328 status_change=(ChangesetStatus.get_status_lbl(status)
334 status_change=(ChangesetStatus.get_status_lbl(status)
329 if status and change_status else None)
335 if status and change_status else None)
330 )
336 )
331
337
332 # get status if set !
338 # get status if set !
333 if status and change_status:
339 if status and change_status:
334 ChangesetStatusModel().set_status(
340 ChangesetStatusModel().set_status(
335 c.rhodecode_db_repo.repo_id,
341 c.rhodecode_db_repo.repo_id,
336 status,
342 status,
337 c.rhodecode_user.user_id,
343 c.rhodecode_user.user_id,
338 comm,
344 comm,
339 pull_request=pull_request_id
345 pull_request=pull_request_id
340 )
346 )
341 action_logger(self.rhodecode_user,
347 action_logger(self.rhodecode_user,
342 'user_commented_pull_request:%s' % pull_request_id,
348 'user_commented_pull_request:%s' % pull_request_id,
343 c.rhodecode_db_repo, self.ip_addr, self.sa)
349 c.rhodecode_db_repo, self.ip_addr, self.sa)
344
350
345 if request.POST.get('save_close'):
351 if request.POST.get('save_close'):
346 PullRequestModel().close_pull_request(pull_request_id)
352 PullRequestModel().close_pull_request(pull_request_id)
347 action_logger(self.rhodecode_user,
353 action_logger(self.rhodecode_user,
348 'user_closed_pull_request:%s' % pull_request_id,
354 'user_closed_pull_request:%s' % pull_request_id,
349 c.rhodecode_db_repo, self.ip_addr, self.sa)
355 c.rhodecode_db_repo, self.ip_addr, self.sa)
350
356
351 Session().commit()
357 Session().commit()
352
358
353 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
359 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
354 return redirect(h.url('pullrequest_show', repo_name=repo_name,
360 return redirect(h.url('pullrequest_show', repo_name=repo_name,
355 pull_request_id=pull_request_id))
361 pull_request_id=pull_request_id))
356
362
357 data = {
363 data = {
358 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
364 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
359 }
365 }
360 if comm:
366 if comm:
361 c.co = comm
367 c.co = comm
362 data.update(comm.get_dict())
368 data.update(comm.get_dict())
363 data.update({'rendered_text':
369 data.update({'rendered_text':
364 render('changeset/changeset_comment_block.html')})
370 render('changeset/changeset_comment_block.html')})
365
371
366 return data
372 return data
367
373
368 @NotAnonymous()
374 @NotAnonymous()
369 @jsonify
375 @jsonify
370 def delete_comment(self, repo_name, comment_id):
376 def delete_comment(self, repo_name, comment_id):
371 co = ChangesetComment.get(comment_id)
377 co = ChangesetComment.get(comment_id)
372 if co.pull_request.is_closed():
378 if co.pull_request.is_closed():
373 #don't allow deleting comments on closed pull request
379 #don't allow deleting comments on closed pull request
374 raise HTTPForbidden()
380 raise HTTPForbidden()
375
381
376 owner = lambda: co.author.user_id == c.rhodecode_user.user_id
382 owner = lambda: co.author.user_id == c.rhodecode_user.user_id
377 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
383 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
378 ChangesetCommentsModel().delete(comment=co)
384 ChangesetCommentsModel().delete(comment=co)
379 Session().commit()
385 Session().commit()
380 return True
386 return True
381 else:
387 else:
382 raise HTTPForbidden()
388 raise HTTPForbidden()
@@ -1,188 +1,189 b''
1 <%inherit file="/base/base.html"/>
1 <%inherit file="/base/base.html"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${c.repo_name} ${_('New pull request')}
4 ${c.repo_name} ${_('New pull request')}
5 </%def>
5 </%def>
6
6
7 <%def name="breadcrumbs_links()">
7 <%def name="breadcrumbs_links()">
8 ${h.link_to(_(u'Home'),h.url('/'))}
8 ${h.link_to(_(u'Home'),h.url('/'))}
9 &raquo;
9 &raquo;
10 ${h.link_to(c.repo_name,h.url('changelog_home',repo_name=c.repo_name))}
10 ${h.link_to(c.repo_name,h.url('changelog_home',repo_name=c.repo_name))}
11 &raquo;
11 &raquo;
12 ${_('New pull request')}
12 ${_('New pull request')}
13 </%def>
13 </%def>
14
14
15 <%def name="main()">
15 <%def name="main()">
16
16
17 <div class="box">
17 <div class="box">
18 <!-- box / title -->
18 <!-- box / title -->
19 <div class="title">
19 <div class="title">
20 ${self.breadcrumbs()}
20 ${self.breadcrumbs()}
21 </div>
21 </div>
22 ${h.form(url('pullrequest', repo_name=c.repo_name), method='post', id='pull_request_form')}
22 ${h.form(url('pullrequest', repo_name=c.repo_name), method='post', id='pull_request_form')}
23 <div style="float:left;padding:0px 30px 30px 30px">
23 <div style="float:left;padding:0px 30px 30px 30px">
24 <div style="padding:0px 5px 5px 5px">
24 <div style="padding:0px 5px 5px 5px">
25 <span>
25 <span>
26 <a id="refresh" href="#">
26 <a id="refresh" href="#">
27 <img class="icon" title="${_('Refresh')}" alt="${_('Refresh')}" src="${h.url('/images/icons/arrow_refresh.png')}"/>
27 <img class="icon" title="${_('Refresh')}" alt="${_('Refresh')}" src="${h.url('/images/icons/arrow_refresh.png')}"/>
28 ${_('refresh overview')}
28 ${_('refresh overview')}
29 </a>
29 </a>
30 </span>
30 </span>
31 </div>
31 </div>
32 ##ORG
32 ##ORG
33 <div style="float:left">
33 <div style="float:left">
34 <div class="fork_user">
34 <div class="fork_user">
35 <div class="gravatar">
35 <div class="gravatar">
36 <img alt="gravatar" src="${h.gravatar_url(c.rhodecode_db_repo.user.email,24)}"/>
36 <img alt="gravatar" src="${h.gravatar_url(c.rhodecode_db_repo.user.email,24)}"/>
37 </div>
37 </div>
38 <span style="font-size: 20px">
38 <span style="font-size: 20px">
39 ${h.select('org_repo','',c.org_repos,class_='refs')}:${h.select('org_ref','',c.org_refs,class_='refs')}
39 ${h.select('org_repo','',c.org_repos,class_='refs')}:${h.select('org_ref','',c.org_refs,class_='refs')}
40 </span>
40 </span>
41 <div style="padding:5px 3px 3px 42px;">${c.rhodecode_db_repo.description}</div>
41 <div style="padding:5px 3px 3px 42px;">${c.rhodecode_db_repo.description}</div>
42 </div>
42 </div>
43 <div style="clear:both;padding-top: 10px"></div>
43 <div style="clear:both;padding-top: 10px"></div>
44 </div>
44 </div>
45 <div style="float:left;font-size:24px;padding:0px 20px">
45 <div style="float:left;font-size:24px;padding:0px 20px">
46 <img height=32 width=32 src="${h.url('/images/arrow_right_64.png')}"/>
46 <img height=32 width=32 src="${h.url('/images/arrow_right_64.png')}"/>
47 </div>
47 </div>
48
48
49 ##OTHER, most Probably the PARENT OF THIS FORK
49 ##OTHER, most Probably the PARENT OF THIS FORK
50 <div style="float:left">
50 <div style="float:left">
51 <div class="fork_user">
51 <div class="fork_user">
52 <div class="gravatar">
52 <div class="gravatar">
53 <img id="other_repo_gravatar" alt="gravatar" src=""/>
53 <img id="other_repo_gravatar" alt="gravatar" src=""/>
54 </div>
54 </div>
55 <span style="font-size: 20px">
55 <span style="font-size: 20px">
56 ${h.select('other_repo',c.default_pull_request ,c.other_repos,class_='refs')}:${h.select('other_ref','',c.other_refs,class_='refs')}
56 ${h.select('other_repo',c.default_pull_request ,c.other_repos,class_='refs')}:${h.select('other_ref','',c.default_revs,class_='refs')}
57 </span>
57 </span>
58 <div id="other_repo_desc" style="padding:5px 3px 3px 42px;"></div>
58 <div id="other_repo_desc" style="padding:5px 3px 3px 42px;"></div>
59 </div>
59 </div>
60 <div style="clear:both;padding-top: 10px"></div>
60 <div style="clear:both;padding-top: 10px"></div>
61 </div>
61 </div>
62 <div style="clear:both;padding-top: 10px"></div>
62 <div style="clear:both;padding-top: 10px"></div>
63 ## overview pulled by ajax
63 ## overview pulled by ajax
64 <div style="float:left" id="pull_request_overview"></div>
64 <div style="float:left" id="pull_request_overview"></div>
65 <div style="float:left;clear:both;padding:10px 10px 10px 0px;display:none">
65 <div style="float:left;clear:both;padding:10px 10px 10px 0px;display:none">
66 <a id="pull_request_overview_url" href="#">${_('Detailed compare view')}</a>
66 <a id="pull_request_overview_url" href="#">${_('Detailed compare view')}</a>
67 </div>
67 </div>
68 </div>
68 </div>
69 <div style="float:left; border-left:1px dashed #eee">
69 <div style="float:left; border-left:1px dashed #eee">
70 <h4>${_('Pull request reviewers')}</h4>
70 <h4>${_('Pull request reviewers')}</h4>
71 <div id="reviewers" style="padding:0px 0px 0px 15px">
71 <div id="reviewers" style="padding:0px 0px 0px 15px">
72 ## members goes here !
72 ## members goes here !
73 <div class="group_members_wrap">
73 <div class="group_members_wrap">
74 <ul id="review_members" class="group_members">
74 <ul id="review_members" class="group_members">
75 %for member in c.review_members:
75 %for member in c.review_members:
76 <li id="reviewer_${member.user_id}">
76 <li id="reviewer_${member.user_id}">
77 <div class="reviewers_member">
77 <div class="reviewers_member">
78 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(member.email,14)}"/> </div>
78 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(member.email,14)}"/> </div>
79 <div style="float:left">${member.full_name} (${_('owner')})</div>
79 <div style="float:left">${member.full_name} (${_('owner')})</div>
80 <input type="hidden" value="${member.user_id}" name="review_members" />
80 <input type="hidden" value="${member.user_id}" name="review_members" />
81 <span class="delete_icon action_button" onclick="removeReviewer(${member.user_id})"></span>
81 <span class="delete_icon action_button" onclick="removeReviewer(${member.user_id})"></span>
82 </div>
82 </div>
83 </li>
83 </li>
84 %endfor
84 %endfor
85 </ul>
85 </ul>
86 </div>
86 </div>
87
87
88 <div class='ac'>
88 <div class='ac'>
89 <div class="reviewer_ac">
89 <div class="reviewer_ac">
90 ${h.text('user', class_='yui-ac-input')}
90 ${h.text('user', class_='yui-ac-input')}
91 <span class="help-block">${_('Add reviewer to this pull request.')}</span>
91 <span class="help-block">${_('Add reviewer to this pull request.')}</span>
92 <div id="reviewers_container"></div>
92 <div id="reviewers_container"></div>
93 </div>
93 </div>
94 </div>
94 </div>
95 </div>
95 </div>
96 </div>
96 </div>
97 <h3>${_('Create new pull request')}</h3>
97 <h3>${_('Create new pull request')}</h3>
98
98
99 <div class="form">
99 <div class="form">
100 <!-- fields -->
100 <!-- fields -->
101
101
102 <div class="fields">
102 <div class="fields">
103
103
104 <div class="field">
104 <div class="field">
105 <div class="label">
105 <div class="label">
106 <label for="pullrequest_title">${_('Title')}:</label>
106 <label for="pullrequest_title">${_('Title')}:</label>
107 </div>
107 </div>
108 <div class="input">
108 <div class="input">
109 ${h.text('pullrequest_title',size=30)}
109 ${h.text('pullrequest_title',size=30)}
110 </div>
110 </div>
111 </div>
111 </div>
112
112
113 <div class="field">
113 <div class="field">
114 <div class="label label-textarea">
114 <div class="label label-textarea">
115 <label for="pullrequest_desc">${_('description')}:</label>
115 <label for="pullrequest_desc">${_('description')}:</label>
116 </div>
116 </div>
117 <div class="textarea text-area editor">
117 <div class="textarea text-area editor">
118 ${h.textarea('pullrequest_desc',size=30)}
118 ${h.textarea('pullrequest_desc',size=30)}
119 </div>
119 </div>
120 </div>
120 </div>
121
121
122 <div class="buttons">
122 <div class="buttons">
123 ${h.submit('save',_('Send pull request'),class_="ui-btn large")}
123 ${h.submit('save',_('Send pull request'),class_="ui-btn large")}
124 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
124 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
125 </div>
125 </div>
126 </div>
126 </div>
127 </div>
127 </div>
128 ${h.end_form()}
128 ${h.end_form()}
129
129
130 </div>
130 </div>
131
131
132 <script type="text/javascript">
132 <script type="text/javascript">
133 var _USERS_AC_DATA = ${c.users_array|n};
133 var _USERS_AC_DATA = ${c.users_array|n};
134 var _GROUPS_AC_DATA = ${c.users_groups_array|n};
134 var _GROUPS_AC_DATA = ${c.users_groups_array|n};
135 PullRequestAutoComplete('user', 'reviewers_container', _USERS_AC_DATA, _GROUPS_AC_DATA);
135 PullRequestAutoComplete('user', 'reviewers_container', _USERS_AC_DATA, _GROUPS_AC_DATA);
136
136
137 var other_repos_info = ${c.other_repos_info|n};
137 var other_repos_info = ${c.other_repos_info|n};
138 var loadPreview = function(){
138 var loadPreview = function(){
139 YUD.setStyle(YUD.get('pull_request_overview_url').parentElement,'display','none');
139 YUD.setStyle(YUD.get('pull_request_overview_url').parentElement,'display','none');
140 var url = "${h.url('compare_url',
140 var url = "${h.url('compare_url',
141 repo_name='org_repo',
141 repo_name='org_repo',
142 org_ref_type='org_ref_type', org_ref='org_ref',
142 org_ref_type='org_ref_type', org_ref='org_ref',
143 other_ref_type='other_ref_type', other_ref='other_ref',
143 other_ref_type='other_ref_type', other_ref='other_ref',
144 repo='other_repo',
144 repo='other_repo',
145 as_form=True)}";
145 as_form=True)}";
146
146
147 var select_refs = YUQ('#pull_request_form select.refs')
147 var select_refs = YUQ('#pull_request_form select.refs')
148
148
149 for(var i=0;i<select_refs.length;i++){
149 for(var i=0;i<select_refs.length;i++){
150 var select_ref = select_refs[i];
150 var select_ref = select_refs[i];
151 var select_ref_data = select_ref.value.split(':');
151 var select_ref_data = select_ref.value.split(':');
152 var key = null;
152 var key = null;
153 var val = null;
153 var val = null;
154 if(select_ref_data.length>1){
154 if(select_ref_data.length>1){
155 key = select_ref.name+"_type";
155 key = select_ref.name+"_type";
156 val = select_ref_data[0];
156 val = select_ref_data[0];
157 url = url.replace(key,val);
157 url = url.replace(key,val);
158
158
159 key = select_ref.name;
159 key = select_ref.name;
160 val = select_ref_data[1];
160 val = select_ref_data[1];
161 url = url.replace(key,val);
161 url = url.replace(key,val);
162
162
163 }else{
163 }else{
164 key = select_ref.name;
164 key = select_ref.name;
165 val = select_ref.value;
165 val = select_ref.value;
166 url = url.replace(key,val);
166 url = url.replace(key,val);
167 }
167 }
168 }
168 }
169
169
170 ypjax(url,'pull_request_overview', function(data){
170 ypjax(url,'pull_request_overview', function(data){
171 var sel_box = YUQ('#pull_request_form #other_repo')[0];
171 var sel_box = YUQ('#pull_request_form #other_repo')[0];
172 var repo_name = sel_box.options[sel_box.selectedIndex].value;
172 var repo_name = sel_box.options[sel_box.selectedIndex].value;
173 YUD.get('pull_request_overview_url').href = url;
173 YUD.get('pull_request_overview_url').href = url;
174 YUD.setStyle(YUD.get('pull_request_overview_url').parentElement,'display','');
174 YUD.setStyle(YUD.get('pull_request_overview_url').parentElement,'display','');
175 YUD.get('other_repo_gravatar').src = other_repos_info[repo_name]['gravatar'];
175 YUD.get('other_repo_gravatar').src = other_repos_info[repo_name]['gravatar'];
176 YUD.get('other_repo_desc').innerHTML = other_repos_info[repo_name]['description'];
176 YUD.get('other_repo_desc').innerHTML = other_repos_info[repo_name]['description'];
177 YUD.get('other_ref').innerHTML = other_repos_info[repo_name]['revs'];
177 })
178 })
178 }
179 }
179 YUE.on('refresh','click',function(e){
180 YUE.on('refresh','click',function(e){
180 loadPreview()
181 loadPreview()
181 })
182 })
182
183
183 //lazy load overview after 0.5s
184 //lazy load overview after 0.5s
184 setTimeout(loadPreview, 500)
185 setTimeout(loadPreview, 500)
185
186
186 </script>
187 </script>
187
188
188 </%def>
189 </%def>
General Comments 0
You need to be logged in to leave comments. Login now