##// END OF EJS Templates
Added dynamic data loading for other repo we open pull request against...
marcink -
r2541:1c2ba03c beta
parent child Browse files
Show More
@@ -1,428 +1,428 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.changeset
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 changeset controller for pylons showoing changes beetween
7 7 revisions
8 8
9 9 :created_on: Apr 25, 2010
10 10 :author: marcink
11 11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 12 :license: GPLv3, see COPYING for more details.
13 13 """
14 14 # This program is free software: you can redistribute it and/or modify
15 15 # it under the terms of the GNU General Public License as published by
16 16 # the Free Software Foundation, either version 3 of the License, or
17 17 # (at your option) any later version.
18 18 #
19 19 # This program is distributed in the hope that it will be useful,
20 20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 22 # GNU General Public License for more details.
23 23 #
24 24 # You should have received a copy of the GNU General Public License
25 25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 26 import logging
27 27 import traceback
28 28 from collections import defaultdict
29 29 from webob.exc import HTTPForbidden
30 30
31 31 from pylons import tmpl_context as c, url, request, response
32 32 from pylons.i18n.translation import _
33 33 from pylons.controllers.util import redirect
34 34 from pylons.decorators import jsonify
35 35
36 36 from rhodecode.lib.vcs.exceptions import RepositoryError, ChangesetError, \
37 37 ChangesetDoesNotExistError
38 38 from rhodecode.lib.vcs.nodes import FileNode
39 39
40 40 import rhodecode.lib.helpers as h
41 41 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
42 42 from rhodecode.lib.base import BaseRepoController, render
43 43 from rhodecode.lib.utils import EmptyChangeset, action_logger
44 44 from rhodecode.lib.compat import OrderedDict
45 45 from rhodecode.lib import diffs
46 46 from rhodecode.model.db import ChangesetComment, ChangesetStatus
47 47 from rhodecode.model.comment import ChangesetCommentsModel
48 48 from rhodecode.model.changeset_status import ChangesetStatusModel
49 49 from rhodecode.model.meta import Session
50 50 from rhodecode.lib.diffs import wrapped_diff
51 51 from rhodecode.model.repo import RepoModel
52 52
53 53 log = logging.getLogger(__name__)
54 54
55 55
56 56 def _update_with_GET(params, GET):
57 57 for k in ['diff1', 'diff2', 'diff']:
58 58 params[k] += GET.getall(k)
59 59
60 60
61 61 def anchor_url(revision, path, GET):
62 62 fid = h.FID(revision, path)
63 63 return h.url.current(anchor=fid, **dict(GET))
64 64
65 65
66 66 def get_ignore_ws(fid, GET):
67 67 ig_ws_global = GET.get('ignorews')
68 68 ig_ws = filter(lambda k: k.startswith('WS'), GET.getall(fid))
69 69 if ig_ws:
70 70 try:
71 71 return int(ig_ws[0].split(':')[-1])
72 72 except:
73 73 pass
74 74 return ig_ws_global
75 75
76 76
77 77 def _ignorews_url(GET, fileid=None):
78 78 fileid = str(fileid) if fileid else None
79 79 params = defaultdict(list)
80 80 _update_with_GET(params, GET)
81 81 lbl = _('show white space')
82 82 ig_ws = get_ignore_ws(fileid, GET)
83 83 ln_ctx = get_line_ctx(fileid, GET)
84 84 # global option
85 85 if fileid is None:
86 86 if ig_ws is None:
87 87 params['ignorews'] += [1]
88 88 lbl = _('ignore white space')
89 89 ctx_key = 'context'
90 90 ctx_val = ln_ctx
91 91 # per file options
92 92 else:
93 93 if ig_ws is None:
94 94 params[fileid] += ['WS:1']
95 95 lbl = _('ignore white space')
96 96
97 97 ctx_key = fileid
98 98 ctx_val = 'C:%s' % ln_ctx
99 99 # if we have passed in ln_ctx pass it along to our params
100 100 if ln_ctx:
101 101 params[ctx_key] += [ctx_val]
102 102
103 103 params['anchor'] = fileid
104 104 img = h.image(h.url('/images/icons/text_strikethrough.png'), lbl, class_='icon')
105 105 return h.link_to(img, h.url.current(**params), title=lbl, class_='tooltip')
106 106
107 107
108 108 def get_line_ctx(fid, GET):
109 109 ln_ctx_global = GET.get('context')
110 110 ln_ctx = filter(lambda k: k.startswith('C'), GET.getall(fid))
111 111
112 112 if ln_ctx:
113 113 retval = ln_ctx[0].split(':')[-1]
114 114 else:
115 115 retval = ln_ctx_global
116 116
117 117 try:
118 118 return int(retval)
119 119 except:
120 120 return
121 121
122 122
123 123 def _context_url(GET, fileid=None):
124 124 """
125 125 Generates url for context lines
126 126
127 127 :param fileid:
128 128 """
129 129
130 130 fileid = str(fileid) if fileid else None
131 131 ig_ws = get_ignore_ws(fileid, GET)
132 132 ln_ctx = (get_line_ctx(fileid, GET) or 3) * 2
133 133
134 134 params = defaultdict(list)
135 135 _update_with_GET(params, GET)
136 136
137 137 # global option
138 138 if fileid is None:
139 139 if ln_ctx > 0:
140 140 params['context'] += [ln_ctx]
141 141
142 142 if ig_ws:
143 143 ig_ws_key = 'ignorews'
144 144 ig_ws_val = 1
145 145
146 146 # per file option
147 147 else:
148 148 params[fileid] += ['C:%s' % ln_ctx]
149 149 ig_ws_key = fileid
150 150 ig_ws_val = 'WS:%s' % 1
151 151
152 152 if ig_ws:
153 153 params[ig_ws_key] += [ig_ws_val]
154 154
155 155 lbl = _('%s line context') % ln_ctx
156 156
157 157 params['anchor'] = fileid
158 158 img = h.image(h.url('/images/icons/table_add.png'), lbl, class_='icon')
159 159 return h.link_to(img, h.url.current(**params), title=lbl, class_='tooltip')
160 160
161 161
162 162 class ChangesetController(BaseRepoController):
163 163
164 164 @LoginRequired()
165 165 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
166 166 'repository.admin')
167 167 def __before__(self):
168 168 super(ChangesetController, self).__before__()
169 169 c.affected_files_cut_off = 60
170 170 repo_model = RepoModel()
171 171 c.users_array = repo_model.get_users_js()
172 172 c.users_groups_array = repo_model.get_users_groups_js()
173 173
174 174 def index(self, revision):
175 175
176 176 c.anchor_url = anchor_url
177 177 c.ignorews_url = _ignorews_url
178 178 c.context_url = _context_url
179 179 limit_off = request.GET.get('fulldiff')
180 180 #get ranges of revisions if preset
181 181 rev_range = revision.split('...')[:2]
182 182 enable_comments = True
183 183 try:
184 184 if len(rev_range) == 2:
185 185 enable_comments = False
186 186 rev_start = rev_range[0]
187 187 rev_end = rev_range[1]
188 188 rev_ranges = c.rhodecode_repo.get_changesets(start=rev_start,
189 189 end=rev_end)
190 190 else:
191 191 rev_ranges = [c.rhodecode_repo.get_changeset(revision)]
192 192
193 193 c.cs_ranges = list(rev_ranges)
194 194 if not c.cs_ranges:
195 195 raise RepositoryError('Changeset range returned empty result')
196 196
197 197 except (RepositoryError, ChangesetDoesNotExistError, Exception), e:
198 198 log.error(traceback.format_exc())
199 199 h.flash(str(e), category='warning')
200 200 return redirect(url('home'))
201 201
202 202 c.changes = OrderedDict()
203 203
204 204 c.lines_added = 0 # count of lines added
205 205 c.lines_deleted = 0 # count of lines removes
206 206
207 207 cumulative_diff = 0
208 208 c.cut_off = False # defines if cut off limit is reached
209 209 c.changeset_statuses = ChangesetStatus.STATUSES
210 210 c.comments = []
211 211 c.statuses = []
212 212 c.inline_comments = []
213 213 c.inline_cnt = 0
214 214 # Iterate over ranges (default changeset view is always one changeset)
215 215 for changeset in c.cs_ranges:
216 216
217 217 c.statuses.extend([ChangesetStatusModel()\
218 218 .get_status(c.rhodecode_db_repo.repo_id,
219 219 changeset.raw_id)])
220 220
221 221 c.comments.extend(ChangesetCommentsModel()\
222 222 .get_comments(c.rhodecode_db_repo.repo_id,
223 223 revision=changeset.raw_id))
224 224 inlines = ChangesetCommentsModel()\
225 225 .get_inline_comments(c.rhodecode_db_repo.repo_id,
226 226 revision=changeset.raw_id)
227 227 c.inline_comments.extend(inlines)
228 228 c.changes[changeset.raw_id] = []
229 229 try:
230 230 changeset_parent = changeset.parents[0]
231 231 except IndexError:
232 232 changeset_parent = None
233 233
234 234 #==================================================================
235 235 # ADDED FILES
236 236 #==================================================================
237 237 for node in changeset.added:
238 238 fid = h.FID(revision, node.path)
239 239 line_context_lcl = get_line_ctx(fid, request.GET)
240 240 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
241 241 lim = self.cut_off_limit
242 242 if cumulative_diff > self.cut_off_limit:
243 243 lim = -1 if limit_off is None else None
244 244 size, cs1, cs2, diff, st = wrapped_diff(
245 245 filenode_old=None,
246 246 filenode_new=node,
247 247 cut_off_limit=lim,
248 248 ignore_whitespace=ign_whitespace_lcl,
249 249 line_context=line_context_lcl,
250 250 enable_comments=enable_comments
251 251 )
252 252 cumulative_diff += size
253 253 c.lines_added += st[0]
254 254 c.lines_deleted += st[1]
255 255 c.changes[changeset.raw_id].append(
256 256 ('added', node, diff, cs1, cs2, st)
257 257 )
258 258
259 259 #==================================================================
260 260 # CHANGED FILES
261 261 #==================================================================
262 262 for node in changeset.changed:
263 263 try:
264 264 filenode_old = changeset_parent.get_node(node.path)
265 265 except ChangesetError:
266 266 log.warning('Unable to fetch parent node for diff')
267 267 filenode_old = FileNode(node.path, '', EmptyChangeset())
268 268
269 269 fid = h.FID(revision, node.path)
270 270 line_context_lcl = get_line_ctx(fid, request.GET)
271 271 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
272 272 lim = self.cut_off_limit
273 273 if cumulative_diff > self.cut_off_limit:
274 274 lim = -1 if limit_off is None else None
275 275 size, cs1, cs2, diff, st = wrapped_diff(
276 276 filenode_old=filenode_old,
277 277 filenode_new=node,
278 278 cut_off_limit=lim,
279 279 ignore_whitespace=ign_whitespace_lcl,
280 280 line_context=line_context_lcl,
281 281 enable_comments=enable_comments
282 282 )
283 283 cumulative_diff += size
284 284 c.lines_added += st[0]
285 285 c.lines_deleted += st[1]
286 286 c.changes[changeset.raw_id].append(
287 287 ('changed', node, diff, cs1, cs2, st)
288 288 )
289 289 #==================================================================
290 290 # REMOVED FILES
291 291 #==================================================================
292 292 for node in changeset.removed:
293 293 c.changes[changeset.raw_id].append(
294 294 ('removed', node, None, None, None, (0, 0))
295 295 )
296 296
297 297 # count inline comments
298 298 for __, lines in c.inline_comments:
299 299 for comments in lines.values():
300 300 c.inline_cnt += len(comments)
301 301
302 302 if len(c.cs_ranges) == 1:
303 303 c.changeset = c.cs_ranges[0]
304 304 c.changes = c.changes[c.changeset.raw_id]
305 305
306 306 return render('changeset/changeset.html')
307 307 else:
308 308 return render('changeset/changeset_range.html')
309 309
310 310 def raw_changeset(self, revision):
311 311
312 312 method = request.GET.get('diff', 'show')
313 313 ignore_whitespace = request.GET.get('ignorews') == '1'
314 314 line_context = request.GET.get('context', 3)
315 315 try:
316 316 c.scm_type = c.rhodecode_repo.alias
317 317 c.changeset = c.rhodecode_repo.get_changeset(revision)
318 318 except RepositoryError:
319 319 log.error(traceback.format_exc())
320 320 return redirect(url('home'))
321 321 else:
322 322 try:
323 323 c.changeset_parent = c.changeset.parents[0]
324 324 except IndexError:
325 325 c.changeset_parent = None
326 326 c.changes = []
327 327
328 328 for node in c.changeset.added:
329 329 filenode_old = FileNode(node.path, '')
330 330 if filenode_old.is_binary or node.is_binary:
331 331 diff = _('binary file') + '\n'
332 332 else:
333 333 f_gitdiff = diffs.get_gitdiff(filenode_old, node,
334 334 ignore_whitespace=ignore_whitespace,
335 335 context=line_context)
336 336 diff = diffs.DiffProcessor(f_gitdiff,
337 337 format='gitdiff').raw_diff()
338 338
339 339 cs1 = None
340 340 cs2 = node.changeset.raw_id
341 341 c.changes.append(('added', node, diff, cs1, cs2))
342 342
343 343 for node in c.changeset.changed:
344 344 filenode_old = c.changeset_parent.get_node(node.path)
345 345 if filenode_old.is_binary or node.is_binary:
346 346 diff = _('binary file')
347 347 else:
348 348 f_gitdiff = diffs.get_gitdiff(filenode_old, node,
349 349 ignore_whitespace=ignore_whitespace,
350 350 context=line_context)
351 351 diff = diffs.DiffProcessor(f_gitdiff,
352 352 format='gitdiff').raw_diff()
353 353
354 354 cs1 = filenode_old.changeset.raw_id
355 355 cs2 = node.changeset.raw_id
356 356 c.changes.append(('changed', node, diff, cs1, cs2))
357 357
358 358 response.content_type = 'text/plain'
359 359
360 360 if method == 'download':
361 361 response.content_disposition = 'attachment; filename=%s.patch' \
362 362 % revision
363 363
364 364 c.parent_tmpl = ''.join(['# Parent %s\n' % x.raw_id
365 365 for x in c.changeset.parents])
366 366
367 367 c.diffs = ''
368 368 for x in c.changes:
369 369 c.diffs += x[2]
370 370
371 371 return render('changeset/raw_changeset.html')
372 372
373 373 @jsonify
374 374 def comment(self, repo_name, revision):
375 375 status = request.POST.get('changeset_status')
376 376 change_status = request.POST.get('change_changeset_status')
377 377
378 378 comm = ChangesetCommentsModel().create(
379 379 text=request.POST.get('text'),
380 repo_id=c.rhodecode_db_repo.repo_id,
381 user_id=c.rhodecode_user.user_id,
380 repo=c.rhodecode_db_repo.repo_id,
381 user=c.rhodecode_user.user_id,
382 382 revision=revision,
383 383 f_path=request.POST.get('f_path'),
384 384 line_no=request.POST.get('line'),
385 385 status_change=(ChangesetStatus.get_status_lbl(status)
386 386 if status and change_status else None)
387 387 )
388 388
389 389 # get status if set !
390 390 if status and change_status:
391 391 ChangesetStatusModel().set_status(
392 392 c.rhodecode_db_repo.repo_id,
393 393 status,
394 394 c.rhodecode_user.user_id,
395 395 comm,
396 396 revision=revision,
397 397 )
398 398 action_logger(self.rhodecode_user,
399 399 'user_commented_revision:%s' % revision,
400 400 c.rhodecode_db_repo, self.ip_addr, self.sa)
401 401
402 402 Session.commit()
403 403
404 404 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
405 405 return redirect(h.url('changeset_home', repo_name=repo_name,
406 406 revision=revision))
407 407
408 408 data = {
409 409 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
410 410 }
411 411 if comm:
412 412 c.co = comm
413 413 data.update(comm.get_dict())
414 414 data.update({'rendered_text':
415 415 render('changeset/changeset_comment_block.html')})
416 416
417 417 return data
418 418
419 419 @jsonify
420 420 def delete_comment(self, repo_name, comment_id):
421 421 co = ChangesetComment.get(comment_id)
422 422 owner = lambda: co.author.user_id == c.rhodecode_user.user_id
423 423 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
424 424 ChangesetCommentsModel().delete(comment=co)
425 425 Session.commit()
426 426 return True
427 427 else:
428 428 raise HTTPForbidden()
@@ -1,308 +1,328 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.pullrequests
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 pull requests controller for rhodecode for initializing pull requests
7 7
8 8 :created_on: May 7, 2012
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25 import logging
26 26 import traceback
27 27
28 28 from webob.exc import HTTPNotFound, HTTPForbidden
29 29 from collections import defaultdict
30 30 from itertools import groupby
31 31
32 32 from pylons import request, response, session, tmpl_context as c, url
33 33 from pylons.controllers.util import abort, redirect
34 34 from pylons.i18n.translation import _
35 35 from pylons.decorators import jsonify
36 36
37 from rhodecode.lib.compat import json
37 38 from rhodecode.lib.base import BaseRepoController, render
38 39 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
39 40 from rhodecode.lib import helpers as h
40 41 from rhodecode.lib import diffs
41 42 from rhodecode.lib.utils import action_logger
42 43 from rhodecode.model.db import User, PullRequest, ChangesetStatus,\
43 44 ChangesetComment
44 45 from rhodecode.model.pull_request import PullRequestModel
45 46 from rhodecode.model.meta import Session
46 47 from rhodecode.model.repo import RepoModel
47 48 from rhodecode.model.comment import ChangesetCommentsModel
48 49 from rhodecode.model.changeset_status import ChangesetStatusModel
49 50
50 51 log = logging.getLogger(__name__)
51 52
52 53
53 54 class PullrequestsController(BaseRepoController):
54 55
55 56 @LoginRequired()
56 57 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
57 58 'repository.admin')
58 59 def __before__(self):
59 60 super(PullrequestsController, self).__before__()
60 61
61 62 def _get_repo_refs(self, repo):
62 63 hist_l = []
63 64
64 65 branches_group = ([('branch:%s:%s' % (k, v), k) for
65 66 k, v in repo.branches.iteritems()], _("Branches"))
66 67 bookmarks_group = ([('book:%s:%s' % (k, v), k) for
67 68 k, v in repo.bookmarks.iteritems()], _("Bookmarks"))
68 69 tags_group = ([('tag:%s:%s' % (k, v), k) for
69 70 k, v in repo.tags.iteritems()], _("Tags"))
70 71
71 72 hist_l.append(bookmarks_group)
72 73 hist_l.append(branches_group)
73 74 hist_l.append(tags_group)
74 75
75 76 return hist_l
76 77
77 78 def show_all(self, repo_name):
78 79 c.pull_requests = PullRequestModel().get_all(repo_name)
79 80 c.repo_name = repo_name
80 81 return render('/pullrequests/pullrequest_show_all.html')
81 82
82 83 def index(self):
83 84 org_repo = c.rhodecode_db_repo
84 85
85 86 if org_repo.scm_instance.alias != 'hg':
86 87 log.error('Review not available for GIT REPOS')
87 88 raise HTTPNotFound
88 89
90 other_repos_info = {}
91
89 92 c.org_refs = self._get_repo_refs(c.rhodecode_repo)
90 93 c.org_repos = []
91 94 c.other_repos = []
92 95 c.org_repos.append((org_repo.repo_name, '%s/%s' % (
93 96 org_repo.user.username, c.repo_name))
94 97 )
95 98
96 99 c.other_refs = c.org_refs
97 100 c.other_repos.extend(c.org_repos)
101
102 #add orginal repo
103 other_repos_info[org_repo.repo_name] = {
104 'gravatar': h.gravatar_url(org_repo.user.email, 24),
105 'description': org_repo.description
106 }
107
98 108 c.default_pull_request = org_repo.repo_name
99 109 #gather forks and add to this list
100 110 for fork in org_repo.forks:
101 111 c.other_repos.append((fork.repo_name, '%s/%s' % (
102 112 fork.user.username, fork.repo_name))
103 113 )
114 other_repos_info[fork.repo_name] = {
115 'gravatar': h.gravatar_url(fork.user.email, 24),
116 'description': fork.description
117 }
104 118 #add parents of this fork also
105 119 if org_repo.parent:
106 120 c.default_pull_request = org_repo.parent.repo_name
107 121 c.other_repos.append((org_repo.parent.repo_name, '%s/%s' % (
108 122 org_repo.parent.user.username,
109 123 org_repo.parent.repo_name))
110 124 )
125 other_repos_info[org_repo.parent.repo_name] = {
126 'gravatar': h.gravatar_url(org_repo.parent.user.email, 24),
127 'description': org_repo.parent.description
128 }
111 129
130 c.other_repos_info = json.dumps(other_repos_info)
112 131 c.review_members = []
113 132 c.available_members = []
114 133 for u in User.query().filter(User.username != 'default').all():
115 134 uname = u.username
116 135 if org_repo.user == u:
117 136 uname = _('%s (owner)' % u.username)
118 137 # auto add owner to pull-request recipients
119 138 c.review_members.append([u.user_id, uname])
120 139 c.available_members.append([u.user_id, uname])
121 140 return render('/pullrequests/pullrequest.html')
122 141
123 142 def create(self, repo_name):
124 143 req_p = request.POST
125 144 org_repo = req_p['org_repo']
126 145 org_ref = req_p['org_ref']
127 146 other_repo = req_p['other_repo']
128 147 other_ref = req_p['other_ref']
129 148 revisions = req_p.getall('revisions')
130 149 reviewers = req_p.getall('review_members')
131 150 #TODO: wrap this into a FORM !!!
132 151
133 152 title = req_p['pullrequest_title']
134 153 description = req_p['pullrequest_desc']
135 154
136 155 try:
137 model = PullRequestModel()
138 pull_request = model.create(self.rhodecode_user.user_id, org_repo,
139 org_ref, other_repo, other_ref, revisions,
140 reviewers, title, description)
141 Session.commit()
156 pull_request = PullRequestModel().create(
157 self.rhodecode_user.user_id, org_repo, org_ref, other_repo,
158 other_ref, revisions, reviewers, title, description
159 )
160 Session().commit()
142 161 h.flash(_('Successfully opened new pull request'),
143 162 category='success')
144 163 except Exception:
145 164 h.flash(_('Error occurred during sending pull request'),
146 165 category='error')
147 166 log.error(traceback.format_exc())
167 return redirect(url('changelog_home', repo_name=org_repo,))
148 168
149 169 return redirect(url('pullrequest_show', repo_name=other_repo,
150 170 pull_request_id=pull_request.pull_request_id))
151 171
152 172 def _load_compare_data(self, pull_request):
153 173 """
154 174 Load context data needed for generating compare diff
155 175
156 176 :param pull_request:
157 177 :type pull_request:
158 178 """
159 179
160 180 org_repo = pull_request.org_repo
161 181 org_ref_type, org_ref_, org_ref = pull_request.org_ref.split(':')
162 182 other_repo = pull_request.other_repo
163 183 other_ref_type, other_ref, other_ref_ = pull_request.other_ref.split(':')
164 184
165 185 org_ref = (org_ref_type, org_ref)
166 186 other_ref = (other_ref_type, other_ref)
167 187
168 188 c.org_repo = org_repo
169 189 c.other_repo = other_repo
170 190
171 191 c.cs_ranges, discovery_data = PullRequestModel().get_compare_data(
172 192 org_repo, org_ref, other_repo, other_ref
173 193 )
174 194
175 195 c.statuses = c.rhodecode_db_repo.statuses([x.raw_id for x in
176 196 c.cs_ranges])
177 197 # defines that we need hidden inputs with changesets
178 198 c.as_form = request.GET.get('as_form', False)
179 199
180 200 c.org_ref = org_ref[1]
181 201 c.other_ref = other_ref[1]
182 202 # diff needs to have swapped org with other to generate proper diff
183 203 _diff = diffs.differ(other_repo, other_ref, org_repo, org_ref,
184 204 discovery_data)
185 205 diff_processor = diffs.DiffProcessor(_diff, format='gitdiff')
186 206 _parsed = diff_processor.prepare()
187 207
188 208 c.files = []
189 209 c.changes = {}
190 210
191 211 for f in _parsed:
192 212 fid = h.FID('', f['filename'])
193 213 c.files.append([fid, f['operation'], f['filename'], f['stats']])
194 214 diff = diff_processor.as_html(enable_comments=True,
195 215 diff_lines=[f])
196 216 c.changes[fid] = [f['operation'], f['filename'], diff]
197 217
198 218 def show(self, repo_name, pull_request_id):
199 219 repo_model = RepoModel()
200 220 c.users_array = repo_model.get_users_js()
201 221 c.users_groups_array = repo_model.get_users_groups_js()
202 222 c.pull_request = PullRequest.get_or_404(pull_request_id)
203 223
204 224 cc_model = ChangesetCommentsModel()
205 225 cs_model = ChangesetStatusModel()
206 226 _cs_statuses = cs_model.get_statuses(c.pull_request.org_repo,
207 227 pull_request=c.pull_request,
208 228 with_revisions=True)
209 229
210 230 cs_statuses = defaultdict(list)
211 231 for st in _cs_statuses:
212 232 cs_statuses[st.author.username] += [st]
213 233
214 234 c.pull_request_reviewers = []
215 235 for o in c.pull_request.reviewers:
216 236 st = cs_statuses.get(o.user.username, None)
217 237 if st:
218 238 sorter = lambda k: k.version
219 239 st = [(x, list(y)[0])
220 240 for x, y in (groupby(sorted(st, key=sorter), sorter))]
221 241 c.pull_request_reviewers.append([o.user, st])
222 242
223 243 # pull_requests repo_name we opened it against
224 244 # ie. other_repo must match
225 245 if repo_name != c.pull_request.other_repo.repo_name:
226 246 raise HTTPNotFound
227 247
228 248 # load compare data into template context
229 249 self._load_compare_data(c.pull_request)
230 250
231 251 # inline comments
232 252 c.inline_cnt = 0
233 253 c.inline_comments = cc_model.get_inline_comments(
234 254 c.rhodecode_db_repo.repo_id,
235 255 pull_request=pull_request_id)
236 256 # count inline comments
237 257 for __, lines in c.inline_comments:
238 258 for comments in lines.values():
239 259 c.inline_cnt += len(comments)
240 260 # comments
241 261 c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id,
242 262 pull_request=pull_request_id)
243 263
244 264 # changeset(pull-request) status
245 265 c.current_changeset_status = cs_model.calculate_status(
246 266 c.pull_request_reviewers
247 267 )
248 268 c.changeset_statuses = ChangesetStatus.STATUSES
249 269 c.target_repo = c.pull_request.org_repo.repo_name
250 270 return render('/pullrequests/pullrequest_show.html')
251 271
252 272 @jsonify
253 273 def comment(self, repo_name, pull_request_id):
254 274
255 275 status = request.POST.get('changeset_status')
256 276 change_status = request.POST.get('change_changeset_status')
257 277
258 278 comm = ChangesetCommentsModel().create(
259 279 text=request.POST.get('text'),
260 repo_id=c.rhodecode_db_repo.repo_id,
261 user_id=c.rhodecode_user.user_id,
280 repo=c.rhodecode_db_repo.repo_id,
281 user=c.rhodecode_user.user_id,
262 282 pull_request=pull_request_id,
263 283 f_path=request.POST.get('f_path'),
264 284 line_no=request.POST.get('line'),
265 285 status_change=(ChangesetStatus.get_status_lbl(status)
266 286 if status and change_status else None)
267 287 )
268 288
269 289 # get status if set !
270 290 if status and change_status:
271 291 ChangesetStatusModel().set_status(
272 292 c.rhodecode_db_repo.repo_id,
273 293 status,
274 294 c.rhodecode_user.user_id,
275 295 comm,
276 296 pull_request=pull_request_id
277 297 )
278 298 action_logger(self.rhodecode_user,
279 299 'user_commented_pull_request:%s' % pull_request_id,
280 300 c.rhodecode_db_repo, self.ip_addr, self.sa)
281 301
282 Session.commit()
302 Session().commit()
283 303
284 304 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
285 305 return redirect(h.url('pullrequest_show', repo_name=repo_name,
286 306 pull_request_id=pull_request_id))
287 307
288 308 data = {
289 309 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
290 310 }
291 311 if comm:
292 312 c.co = comm
293 313 data.update(comm.get_dict())
294 314 data.update({'rendered_text':
295 315 render('changeset/changeset_comment_block.html')})
296 316
297 317 return data
298 318
299 319 @jsonify
300 320 def delete_comment(self, repo_name, comment_id):
301 321 co = ChangesetComment.get(comment_id)
302 322 owner = lambda: co.author.user_id == c.rhodecode_user.user_id
303 323 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
304 324 ChangesetCommentsModel().delete(comment=co)
305 Session.commit()
325 Session().commit()
306 326 return True
307 327 else:
308 328 raise HTTPForbidden() No newline at end of file
@@ -1,213 +1,224 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.comment
4 4 ~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 comments model for RhodeCode
7 7
8 8 :created_on: Nov 11, 2011
9 9 :author: marcink
10 10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27 import traceback
28 28
29 29 from pylons.i18n.translation import _
30 30 from sqlalchemy.util.compat import defaultdict
31 31
32 32 from rhodecode.lib.utils2 import extract_mentioned_users, safe_unicode
33 33 from rhodecode.lib import helpers as h
34 34 from rhodecode.model import BaseModel
35 35 from rhodecode.model.db import ChangesetComment, User, Repository, \
36 36 Notification, PullRequest
37 37 from rhodecode.model.notification import NotificationModel
38 38
39 39 log = logging.getLogger(__name__)
40 40
41 41
42 42 class ChangesetCommentsModel(BaseModel):
43 43
44 44 cls = ChangesetComment
45 45
46 46 def __get_changeset_comment(self, changeset_comment):
47 47 return self._get_instance(ChangesetComment, changeset_comment)
48 48
49 49 def __get_pull_request(self, pull_request):
50 50 return self._get_instance(PullRequest, pull_request)
51 51
52 52 def _extract_mentions(self, s):
53 53 user_objects = []
54 54 for username in extract_mentioned_users(s):
55 55 user_obj = User.get_by_username(username, case_insensitive=True)
56 56 if user_obj:
57 57 user_objects.append(user_obj)
58 58 return user_objects
59 59
60 def create(self, text, repo_id, user_id, revision=None, pull_request=None,
60 def create(self, text, repo, user, revision=None, pull_request=None,
61 61 f_path=None, line_no=None, status_change=None):
62 62 """
63 63 Creates new comment for changeset or pull request.
64 64 IF status_change is not none this comment is associated with a
65 65 status change of changeset or changesets associated with pull request
66 66
67 67 :param text:
68 :param repo_id:
69 :param user_id:
68 :param repo:
69 :param user:
70 70 :param revision:
71 71 :param pull_request:
72 72 :param f_path:
73 73 :param line_no:
74 74 :param status_change:
75 75 """
76 76 if not text:
77 77 return
78 78
79 repo = Repository.get(repo_id)
79 repo = self._get_repo(repo)
80 user = self._get_user(user)
80 81 comment = ChangesetComment()
81 82 comment.repo = repo
82 comment.user_id = user_id
83 comment.author = user
83 84 comment.text = text
84 85 comment.f_path = f_path
85 86 comment.line_no = line_no
86 87
87 88 if revision:
88 89 cs = repo.scm_instance.get_changeset(revision)
89 90 desc = "%s - %s" % (cs.short_id, h.shorter(cs.message, 256))
90 91 author_email = cs.author_email
91 92 comment.revision = revision
92 93 elif pull_request:
93 94 pull_request = self.__get_pull_request(pull_request)
94 95 comment.pull_request = pull_request
95 desc = ''
96 desc = pull_request.pull_request_id
96 97 else:
97 98 raise Exception('Please specify revision or pull_request_id')
98 99
99 100 self.sa.add(comment)
100 101 self.sa.flush()
101 102
102 103 # make notification
103 104 line = ''
104 105 body = text
105 106
106 107 #changeset
107 108 if revision:
108 109 if line_no:
109 110 line = _('on line %s') % line_no
110 111 subj = safe_unicode(
111 h.link_to('Re commit: %(commit_desc)s %(line)s' % \
112 {'commit_desc': desc, 'line': line},
112 h.link_to('Re commit: %(desc)s %(line)s' % \
113 {'desc': desc, 'line': line},
113 114 h.url('changeset_home', repo_name=repo.repo_name,
114 115 revision=revision,
115 116 anchor='comment-%s' % comment.comment_id,
116 117 qualified=True,
117 118 )
118 119 )
119 120 )
120 121 notification_type = Notification.TYPE_CHANGESET_COMMENT
121 122 # get the current participants of this changeset
122 123 recipients = ChangesetComment.get_users(revision=revision)
123 124 # add changeset author if it's in rhodecode system
124 125 recipients += [User.get_by_email(author_email)]
125 126 #pull request
126 127 elif pull_request:
127 #TODO: make this something usefull
128 subj = 'commented on pull request something...'
128 subj = safe_unicode(
129 h.link_to('Re pull request: %(desc)s %(line)s' % \
130 {'desc': desc, 'line': line},
131 h.url('pullrequest_show',
132 repo_name=pull_request.other_repo.repo_name,
133 pull_request_id=pull_request.pull_request_id,
134 anchor='comment-%s' % comment.comment_id,
135 qualified=True,
136 )
137 )
138 )
139
129 140 notification_type = Notification.TYPE_PULL_REQUEST_COMMENT
130 141 # get the current participants of this pull request
131 142 recipients = ChangesetComment.get_users(pull_request_id=
132 143 pull_request.pull_request_id)
133 144 # add pull request author
134 145 recipients += [pull_request.author]
135 146
136 147 # create notification objects, and emails
137 148 NotificationModel().create(
138 created_by=user_id, subject=subj, body=body,
149 created_by=user, subject=subj, body=body,
139 150 recipients=recipients, type_=notification_type,
140 151 email_kwargs={'status_change': status_change}
141 152 )
142 153
143 154 mention_recipients = set(self._extract_mentions(body))\
144 155 .difference(recipients)
145 156 if mention_recipients:
146 157 subj = _('[Mention]') + ' ' + subj
147 158 NotificationModel().create(
148 created_by=user_id, subject=subj, body=body,
159 created_by=user, subject=subj, body=body,
149 160 recipients=mention_recipients,
150 161 type_=notification_type,
151 162 email_kwargs={'status_change': status_change}
152 163 )
153 164
154 165 return comment
155 166
156 167 def delete(self, comment):
157 168 """
158 169 Deletes given comment
159 170
160 171 :param comment_id:
161 172 """
162 173 comment = self.__get_changeset_comment(comment)
163 174 self.sa.delete(comment)
164 175
165 176 return comment
166 177
167 178 def get_comments(self, repo_id, revision=None, pull_request=None):
168 179 """
169 180 Get's main comments based on revision or pull_request_id
170 181
171 182 :param repo_id:
172 183 :type repo_id:
173 184 :param revision:
174 185 :type revision:
175 186 :param pull_request:
176 187 :type pull_request:
177 188 """
178 189
179 190 q = ChangesetComment.query()\
180 191 .filter(ChangesetComment.repo_id == repo_id)\
181 192 .filter(ChangesetComment.line_no == None)\
182 193 .filter(ChangesetComment.f_path == None)
183 194 if revision:
184 195 q = q.filter(ChangesetComment.revision == revision)
185 196 elif pull_request:
186 197 pull_request = self.__get_pull_request(pull_request)
187 198 q = q.filter(ChangesetComment.pull_request == pull_request)
188 199 else:
189 200 raise Exception('Please specify revision or pull_request')
190 201 return q.all()
191 202
192 203 def get_inline_comments(self, repo_id, revision=None, pull_request=None):
193 204 q = self.sa.query(ChangesetComment)\
194 205 .filter(ChangesetComment.repo_id == repo_id)\
195 206 .filter(ChangesetComment.line_no != None)\
196 207 .filter(ChangesetComment.f_path != None)\
197 208 .order_by(ChangesetComment.comment_id.asc())\
198 209
199 210 if revision:
200 211 q = q.filter(ChangesetComment.revision == revision)
201 212 elif pull_request:
202 213 pull_request = self.__get_pull_request(pull_request)
203 214 q = q.filter(ChangesetComment.pull_request == pull_request)
204 215 else:
205 216 raise Exception('Please specify revision or pull_request_id')
206 217
207 218 comments = q.all()
208 219
209 220 paths = defaultdict(lambda: defaultdict(list))
210 221
211 222 for co in comments:
212 223 paths[co.f_path][co.line_no].append(co)
213 224 return paths.items()
@@ -1,189 +1,196 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.pull_reuquest
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 pull request model for RhodeCode
7 7
8 8 :created_on: Jun 6, 2012
9 9 :author: marcink
10 10 :copyright: (C) 2012-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27 import binascii
28 28 from pylons.i18n.translation import _
29 29
30 30 from rhodecode.lib import helpers as h
31 31 from rhodecode.model import BaseModel
32 from rhodecode.model.db import PullRequest, PullRequestReviewers, Notification
32 from rhodecode.model.db import PullRequest, PullRequestReviewers, Notification,\
33 ChangesetStatus
33 34 from rhodecode.model.notification import NotificationModel
34 35 from rhodecode.lib.utils2 import safe_unicode
35 36
36 37 from rhodecode.lib.vcs.utils.hgcompat import discovery
38 from rhodecode.model.changeset_status import ChangesetStatusModel
39 from rhodecode.model.comment import ChangesetCommentsModel
40 from rhodecode.model.meta import Session
37 41
38 42 log = logging.getLogger(__name__)
39 43
40 44
41 45 class PullRequestModel(BaseModel):
42 46
43 47 cls = PullRequest
44 48
45 49 def get_all(self, repo):
46 50 repo = self._get_repo(repo)
47 51 return PullRequest.query().filter(PullRequest.other_repo == repo).all()
48 52
49 53 def create(self, created_by, org_repo, org_ref, other_repo,
50 54 other_ref, revisions, reviewers, title, description=None):
55
51 56 created_by_user = self._get_user(created_by)
57 org_repo = self._get_repo(org_repo)
58 other_repo = self._get_repo(other_repo)
52 59
53 60 new = PullRequest()
54 new.org_repo = self._get_repo(org_repo)
61 new.org_repo = org_repo
55 62 new.org_ref = org_ref
56 new.other_repo = self._get_repo(other_repo)
63 new.other_repo = other_repo
57 64 new.other_ref = other_ref
58 65 new.revisions = revisions
59 66 new.title = title
60 67 new.description = description
61 68 new.author = created_by_user
62 69 self.sa.add(new)
63
70 Session().flush()
64 71 #members
65 72 for member in reviewers:
66 73 _usr = self._get_user(member)
67 74 reviewer = PullRequestReviewers(_usr, new)
68 75 self.sa.add(reviewer)
69 76
70 77 #notification to reviewers
71 78 notif = NotificationModel()
72 79
73 80 subject = safe_unicode(
74 81 h.link_to(
75 82 _('%(user)s wants you to review pull request #%(pr_id)s') % \
76 83 {'user': created_by_user.username,
77 84 'pr_id': new.pull_request_id},
78 85 h.url('pullrequest_show', repo_name=other_repo,
79 86 pull_request_id=new.pull_request_id,
80 87 qualified=True,
81 88 )
82 89 )
83 90 )
84 91 body = description
85 notif.create(created_by=created_by, subject=subject, body=body,
92 notif.create(created_by=created_by_user, subject=subject, body=body,
86 93 recipients=reviewers,
87 94 type_=Notification.TYPE_PULL_REQUEST,)
88 95
89 96 return new
90 97
91 98 def _get_changesets(self, org_repo, org_ref, other_repo, other_ref,
92 99 discovery_data):
93 100 """
94 101 Returns a list of changesets that are incoming from org_repo@org_ref
95 102 to other_repo@other_ref
96 103
97 104 :param org_repo:
98 105 :type org_repo:
99 106 :param org_ref:
100 107 :type org_ref:
101 108 :param other_repo:
102 109 :type other_repo:
103 110 :param other_ref:
104 111 :type other_ref:
105 112 :param tmp:
106 113 :type tmp:
107 114 """
108 115 changesets = []
109 116 #case two independent repos
110 117 if org_repo != other_repo:
111 118 common, incoming, rheads = discovery_data
112 119
113 120 if not incoming:
114 121 revs = []
115 122 else:
116 123 revs = org_repo._repo.changelog.findmissing(common, rheads)
117 124
118 125 for cs in reversed(map(binascii.hexlify, revs)):
119 126 changesets.append(org_repo.get_changeset(cs))
120 127 else:
121 128 revs = ['ancestors(%s) and not ancestors(%s)' % (org_ref[1],
122 129 other_ref[1])]
123 130 from mercurial import scmutil
124 131 out = scmutil.revrange(org_repo._repo, revs)
125 132 for cs in reversed(out):
126 133 changesets.append(org_repo.get_changeset(cs))
127 134
128 135 return changesets
129 136
130 137 def _get_discovery(self, org_repo, org_ref, other_repo, other_ref):
131 138 """
132 139 Get's mercurial discovery data used to calculate difference between
133 140 repos and refs
134 141
135 142 :param org_repo:
136 143 :type org_repo:
137 144 :param org_ref:
138 145 :type org_ref:
139 146 :param other_repo:
140 147 :type other_repo:
141 148 :param other_ref:
142 149 :type other_ref:
143 150 """
144 151
145 152 other = org_repo._repo
146 153 repo = other_repo._repo
147 154 tip = other[org_ref[1]]
148 155 log.debug('Doing discovery for %s@%s vs %s@%s' % (
149 156 org_repo, org_ref, other_repo, other_ref)
150 157 )
151 158 log.debug('Filter heads are %s[%s]' % (tip, org_ref[1]))
152 159 tmp = discovery.findcommonincoming(
153 160 repo=repo, # other_repo we check for incoming
154 161 remote=other, # org_repo source for incoming
155 162 heads=[tip.node()],
156 163 force=False
157 164 )
158 165 return tmp
159 166
160 167 def get_compare_data(self, org_repo, org_ref, other_repo, other_ref):
161 168 """
162 169 Returns a tuple of incomming changesets, and discoverydata cache
163 170
164 171 :param org_repo:
165 172 :type org_repo:
166 173 :param org_ref:
167 174 :type org_ref:
168 175 :param other_repo:
169 176 :type other_repo:
170 177 :param other_ref:
171 178 :type other_ref:
172 179 """
173 180
174 181 if len(org_ref) != 2 or not isinstance(org_ref, (list, tuple)):
175 182 raise Exception('org_ref must be a two element list/tuple')
176 183
177 184 if len(other_ref) != 2 or not isinstance(org_ref, (list, tuple)):
178 185 raise Exception('other_ref must be a two element list/tuple')
179 186
180 187 discovery_data = self._get_discovery(org_repo.scm_instance,
181 188 org_ref,
182 189 other_repo.scm_instance,
183 190 other_ref)
184 191 cs_ranges = self._get_changesets(org_repo.scm_instance,
185 192 org_ref,
186 193 other_repo.scm_instance,
187 194 other_ref,
188 195 discovery_data)
189 196 return cs_ranges, discovery_data
@@ -1,192 +1,196 b''
1 1 <%inherit file="/base/base.html"/>
2 2
3 3 <%def name="title()">
4 4 ${c.repo_name} ${_('New pull request')}
5 5 </%def>
6 6
7 7 <%def name="breadcrumbs_links()">
8 8 ${h.link_to(_(u'Home'),h.url('/'))}
9 9 &raquo;
10 10 ${h.link_to(c.repo_name,h.url('changelog_home',repo_name=c.repo_name))}
11 11 &raquo;
12 12 ${_('New pull request')}
13 13 </%def>
14 14
15 15 <%def name="main()">
16 16
17 17 <div class="box">
18 18 <!-- box / title -->
19 19 <div class="title">
20 20 ${self.breadcrumbs()}
21 21 </div>
22 22 ${h.form(url('pullrequest', repo_name=c.repo_name), method='post', id='pull_request_form')}
23 23 <div style="float:left;padding:0px 30px 30px 30px">
24 24 <div style="padding:0px 5px 5px 5px">
25 25 <span>
26 26 <a id="refresh" href="#">
27 27 <img class="icon" title="${_('Refresh')}" alt="${_('Refresh')}" src="${h.url('/images/icons/arrow_refresh.png')}"/>
28 28 ${_('refresh overview')}
29 29 </a>
30 30 </span>
31 31 </div>
32 32 ##ORG
33 33 <div style="float:left">
34 34 <div class="fork_user">
35 35 <div class="gravatar">
36 36 <img alt="gravatar" src="${h.gravatar_url(c.rhodecode_db_repo.user.email,24)}"/>
37 37 </div>
38 38 <span style="font-size: 20px">
39 39 ${h.select('org_repo','',c.org_repos,class_='refs')}:${h.select('org_ref','',c.org_refs,class_='refs')}
40 40 </span>
41 41 <div style="padding:5px 3px 3px 42px;">${c.rhodecode_db_repo.description}</div>
42 42 </div>
43 43 <div style="clear:both;padding-top: 10px"></div>
44 44 </div>
45 45 <div style="float:left;font-size:24px;padding:0px 20px">
46 46 <img height=32 width=32 src="${h.url('/images/arrow_right_64.png')}"/>
47 47 </div>
48 48
49 49 ##OTHER, most Probably the PARENT OF THIS FORK
50 50 <div style="float:left">
51 51 <div class="fork_user">
52 52 <div class="gravatar">
53 <img alt="gravatar" src="${h.gravatar_url(c.rhodecode_db_repo.user.email,24)}"/>
53 <img id="other_repo_gravatar" alt="gravatar" src=""/>
54 54 </div>
55 55 <span style="font-size: 20px">
56 56 ${h.select('other_repo',c.default_pull_request ,c.other_repos,class_='refs')}:${h.select('other_ref','',c.other_refs,class_='refs')}
57 57 </span>
58 <div style="padding:5px 3px 3px 42px;">${c.rhodecode_db_repo.description}</div>
58 <div id="other_repo_desc" style="padding:5px 3px 3px 42px;"></div>
59 59 </div>
60 60 <div style="clear:both;padding-top: 10px"></div>
61 61 </div>
62 62 <div style="clear:both;padding-top: 10px"></div>
63 63 ## overview pulled by ajax
64 64 <div style="float:left" id="pull_request_overview"></div>
65 65 <div style="float:left;clear:both;padding:10px 10px 10px 0px;display:none">
66 66 <a id="pull_request_overview_url" href="#">${_('Detailed compare view')}</a>
67 67 </div>
68 68 </div>
69 69 <div style="float:left; border-left:1px dashed #eee">
70 70 <h4>${_('Pull request reviewers')}</h4>
71 71 <div id="reviewers" style="padding:0px 0px 0px 15px">
72 72 ##TODO: make this nicer :)
73 73 <table class="table noborder">
74 74 <tr>
75 75 <td>
76 76 <div>
77 77 <div style="float:left">
78 78 <div class="text" style="padding: 0px 0px 6px;">${_('Chosen reviewers')}</div>
79 79 ${h.select('review_members',[x[0] for x in c.review_members],c.review_members,multiple=True,size=8,style="min-width:210px")}
80 80 <div id="remove_all_elements" style="cursor:pointer;text-align:center">
81 81 ${_('Remove all elements')}
82 82 <img alt="remove" style="vertical-align:text-bottom" src="${h.url('/images/icons/arrow_right.png')}"/>
83 83 </div>
84 84 </div>
85 85 <div style="float:left;width:20px;padding-top:50px">
86 86 <img alt="add" id="add_element"
87 87 style="padding:2px;cursor:pointer"
88 88 src="${h.url('/images/icons/arrow_left.png')}"/>
89 89 <br />
90 90 <img alt="remove" id="remove_element"
91 91 style="padding:2px;cursor:pointer"
92 92 src="${h.url('/images/icons/arrow_right.png')}"/>
93 93 </div>
94 94 <div style="float:left">
95 95 <div class="text" style="padding: 0px 0px 6px;">${_('Available reviewers')}</div>
96 96 ${h.select('available_members',[],c.available_members,multiple=True,size=8,style="min-width:210px")}
97 97 <div id="add_all_elements" style="cursor:pointer;text-align:center">
98 98 <img alt="add" style="vertical-align:text-bottom" src="${h.url('/images/icons/arrow_left.png')}"/>
99 99 ${_('Add all elements')}
100 100 </div>
101 101 </div>
102 102 </div>
103 103 </td>
104 104 </tr>
105 105 </table>
106 106 </div>
107 107 </div>
108 108 <h3>${_('Create new pull request')}</h3>
109 109
110 110 <div class="form">
111 111 <!-- fields -->
112 112
113 113 <div class="fields">
114 114
115 115 <div class="field">
116 116 <div class="label">
117 117 <label for="pullrequest_title">${_('Title')}:</label>
118 118 </div>
119 119 <div class="input">
120 120 ${h.text('pullrequest_title',size=30)}
121 121 </div>
122 122 </div>
123 123
124 124 <div class="field">
125 125 <div class="label label-textarea">
126 126 <label for="pullrequest_desc">${_('description')}:</label>
127 127 </div>
128 128 <div class="textarea text-area editor">
129 129 ${h.textarea('pullrequest_desc',size=30)}
130 130 </div>
131 131 </div>
132 132
133 133 <div class="buttons">
134 134 ${h.submit('save',_('Send pull request'),class_="ui-button")}
135 135 ${h.reset('reset',_('Reset'),class_="ui-button")}
136 136 </div>
137 137 </div>
138 138 </div>
139 139 ${h.end_form()}
140 140
141 141 </div>
142 142
143 143 <script type="text/javascript">
144 144 MultiSelectWidget('review_members','available_members','pull_request_form');
145
145 var other_repos_info = ${c.other_repos_info|n};
146 146 var loadPreview = function(){
147 147 YUD.setStyle(YUD.get('pull_request_overview_url').parentElement,'display','none');
148 148 var url = "${h.url('compare_url',
149 149 repo_name='org_repo',
150 150 org_ref_type='branch', org_ref='org_ref',
151 151 other_ref_type='branch', other_ref='other_ref',
152 152 repo='other_repo',
153 153 as_form=True)}";
154 154
155 155 var select_refs = YUQ('#pull_request_form select.refs')
156 156
157 157 for(var i=0;i<select_refs.length;i++){
158 158 var select_ref = select_refs[i];
159 159 var select_ref_data = select_ref.value.split(':');
160 160 var key = null;
161 161 var val = null;
162 162 if(select_ref_data.length>1){
163 163 key = select_ref.name+"_type";
164 164 val = select_ref_data[0];
165 165 url = url.replace(key,val);
166 166
167 167 key = select_ref.name;
168 168 val = select_ref_data[1];
169 169 url = url.replace(key,val);
170 170
171 171 }else{
172 172 key = select_ref.name;
173 173 val = select_ref.value;
174 174 url = url.replace(key,val);
175 175 }
176 176 }
177 177
178 178 ypjax(url,'pull_request_overview', function(data){
179 var sel_box = YUQ('#pull_request_form #other_repo')[0];
180 var repo_name = sel_box.options[sel_box.selectedIndex].value;
179 181 YUD.get('pull_request_overview_url').href = url;
180 182 YUD.setStyle(YUD.get('pull_request_overview_url').parentElement,'display','');
183 YUD.get('other_repo_gravatar').src = other_repos_info[repo_name]['gravatar'];
184 YUD.get('other_repo_desc').innerHTML = other_repos_info[repo_name]['description'];
181 185 })
182 186 }
183 187 YUE.on('refresh','click',function(e){
184 188 loadPreview()
185 189 })
186 190
187 191 //lazy load overview after 0.5s
188 192 setTimeout(loadPreview, 500)
189 193
190 194 </script>
191 195
192 196 </%def>
General Comments 0
You need to be logged in to leave comments. Login now