##// END OF EJS Templates
pull-requests: migrated code from pylons to pyramid
marcink -
r1974:cb4db595 default
parent child Browse files
Show More
@@ -21,10 +21,10 b''
21
21
22 import pytest
22 import pytest
23 import urlobject
23 import urlobject
24 from pylons import url
25
24
26 from rhodecode.api.tests.utils import (
25 from rhodecode.api.tests.utils import (
27 build_data, api_call, assert_error, assert_ok)
26 build_data, api_call, assert_error, assert_ok)
27 from rhodecode.lib import helpers as h
28 from rhodecode.lib.utils2 import safe_unicode
28 from rhodecode.lib.utils2 import safe_unicode
29
29
30 pytestmark = pytest.mark.backends("git", "hg")
30 pytestmark = pytest.mark.backends("git", "hg")
@@ -46,10 +46,10 b' class TestGetPullRequest(object):'
46 assert response.status == '200 OK'
46 assert response.status == '200 OK'
47
47
48 url_obj = urlobject.URLObject(
48 url_obj = urlobject.URLObject(
49 url(
49 h.route_url(
50 'pullrequest_show',
50 'pullrequest_show',
51 repo_name=pull_request.target_repo.repo_name,
51 repo_name=pull_request.target_repo.repo_name,
52 pull_request_id=pull_request.pull_request_id, qualified=True))
52 pull_request_id=pull_request.pull_request_id))
53
53
54 pr_url = safe_unicode(
54 pr_url = safe_unicode(
55 url_obj.with_netloc(http_host_only_stub))
55 url_obj.with_netloc(http_host_only_stub))
@@ -217,7 +217,7 b' def includeme(config):'
217 # Pull Requests
217 # Pull Requests
218 config.add_route(
218 config.add_route(
219 name='pullrequest_show',
219 name='pullrequest_show',
220 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id}',
220 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}',
221 repo_route=True)
221 repo_route=True)
222
222
223 config.add_route(
223 config.add_route(
@@ -230,6 +230,51 b' def includeme(config):'
230 pattern='/{repo_name:.*?[^/]}/pull-request-data',
230 pattern='/{repo_name:.*?[^/]}/pull-request-data',
231 repo_route=True, repo_accepted_types=['hg', 'git'])
231 repo_route=True, repo_accepted_types=['hg', 'git'])
232
232
233 config.add_route(
234 name='pullrequest_repo_refs',
235 pattern='/{repo_name:.*?[^/]}/pull-request/refs/{target_repo_name:.*?[^/]}',
236 repo_route=True)
237
238 config.add_route(
239 name='pullrequest_repo_destinations',
240 pattern='/{repo_name:.*?[^/]}/pull-request/repo-destinations',
241 repo_route=True)
242
243 config.add_route(
244 name='pullrequest_new',
245 pattern='/{repo_name:.*?[^/]}/pull-request/new',
246 repo_route=True, repo_accepted_types=['hg', 'git'])
247
248 config.add_route(
249 name='pullrequest_create',
250 pattern='/{repo_name:.*?[^/]}/pull-request/create',
251 repo_route=True, repo_accepted_types=['hg', 'git'])
252
253 config.add_route(
254 name='pullrequest_update',
255 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/update',
256 repo_route=True)
257
258 config.add_route(
259 name='pullrequest_merge',
260 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/merge',
261 repo_route=True)
262
263 config.add_route(
264 name='pullrequest_delete',
265 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/delete',
266 repo_route=True)
267
268 config.add_route(
269 name='pullrequest_comment_create',
270 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/comment',
271 repo_route=True)
272
273 config.add_route(
274 name='pullrequest_comment_delete',
275 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/comment/{comment_id}/delete',
276 repo_route=True, repo_accepted_types=['hg', 'git'])
277
233 # Settings
278 # Settings
234 config.add_route(
279 config.add_route(
235 name='edit_repo',
280 name='edit_repo',
@@ -17,12 +17,11 b''
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import mock
20 import mock
22 import pytest
21 import pytest
23 from webob.exc import HTTPNotFound
24
22
25 import rhodecode
23 import rhodecode
24 from rhodecode.lib.vcs.backends.base import MergeResponse, MergeFailureReason
26 from rhodecode.lib.vcs.nodes import FileNode
25 from rhodecode.lib.vcs.nodes import FileNode
27 from rhodecode.lib import helpers as h
26 from rhodecode.lib import helpers as h
28 from rhodecode.model.changeset_status import ChangesetStatusModel
27 from rhodecode.model.changeset_status import ChangesetStatusModel
@@ -32,7 +31,7 b' from rhodecode.model.meta import Session'
32 from rhodecode.model.pull_request import PullRequestModel
31 from rhodecode.model.pull_request import PullRequestModel
33 from rhodecode.model.user import UserModel
32 from rhodecode.model.user import UserModel
34 from rhodecode.tests import (
33 from rhodecode.tests import (
35 assert_session_flash, url, TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN)
34 assert_session_flash, TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN)
36 from rhodecode.tests.utils import AssertResponse
35 from rhodecode.tests.utils import AssertResponse
37
36
38
37
@@ -42,6 +41,18 b' def route_path(name, params=None, **kwar'
42 base_url = {
41 base_url = {
43 'repo_changelog':'/{repo_name}/changelog',
42 'repo_changelog': '/{repo_name}/changelog',
44 'repo_changelog_file':'/{repo_name}/changelog/{commit_id}/{f_path}',
43 'repo_changelog_file': '/{repo_name}/changelog/{commit_id}/{f_path}',
44 'pullrequest_show': '/{repo_name}/pull-request/{pull_request_id}',
45 'pullrequest_show_all': '/{repo_name}/pull-request',
46 'pullrequest_show_all_data': '/{repo_name}/pull-request-data',
47 'pullrequest_repo_refs': '/{repo_name}/pull-request/refs/{target_repo_name:.*?[^/]}',
48 'pullrequest_repo_destinations': '/{repo_name}/pull-request/repo-destinations',
49 'pullrequest_new': '/{repo_name}/pull-request/new',
50 'pullrequest_create': '/{repo_name}/pull-request/create',
51 'pullrequest_update': '/{repo_name}/pull-request/{pull_request_id}/update',
52 'pullrequest_merge': '/{repo_name}/pull-request/{pull_request_id}/merge',
53 'pullrequest_delete': '/{repo_name}/pull-request/{pull_request_id}/delete',
54 'pullrequest_comment_create': '/{repo_name}/pull-request/{pull_request_id}/comment',
55 'pullrequest_comment_delete': '/{repo_name}/pull-request/{pull_request_id}/comment/{comment_id}/delete',
45 }[name].format(**kwargs)
56 }[name].format(**kwargs)
46
57
47 if params:
58 if params:
@@ -51,26 +62,26 b' def route_path(name, params=None, **kwar'
51
62
52 @pytest.mark.usefixtures('app', 'autologin_user')
63 @pytest.mark.usefixtures('app', 'autologin_user')
53 @pytest.mark.backends("git", "hg")
64 @pytest.mark.backends("git", "hg")
54 class TestPullrequestsController(object):
65 class TestPullrequestsView(object):
55
66
56 def test_index(self, backend):
67 def test_index(self, backend):
57 self.app.get(url(
68 self.app.get(route_path(
58 controller='pullrequests', action='index',
69 'pullrequest_new',
59 repo_name=backend.repo_name))
70 repo_name=backend.repo_name))
60
71
61 def test_option_menu_create_pull_request_exists(self, backend):
72 def test_option_menu_create_pull_request_exists(self, backend):
62 repo_name = backend.repo_name
73 repo_name = backend.repo_name
63 response = self.app.get(h.route_path('repo_summary', repo_name=repo_name))
74 response = self.app.get(h.route_path('repo_summary', repo_name=repo_name))
64
75
65 create_pr_link = '<a href="%s">Create Pull Request</a>' % url(
76 create_pr_link = '<a href="%s">Create Pull Request</a>' % route_path(
66 'pullrequest', repo_name=repo_name)
77 'pullrequest_new', repo_name=repo_name)
67 response.mustcontain(create_pr_link)
78 response.mustcontain(create_pr_link)
68
79
69 def test_create_pr_form_with_raw_commit_id(self, backend):
80 def test_create_pr_form_with_raw_commit_id(self, backend):
70 repo = backend.repo
81 repo = backend.repo
71
82
72 self.app.get(
83 self.app.get(
73 url(controller='pullrequests', action='index',
84 route_path('pullrequest_new',
74 repo_name=repo.repo_name,
85 repo_name=repo.repo_name,
75 commit=repo.get_commit().raw_id),
86 commit=repo.get_commit().raw_id),
76 status=200)
87 status=200)
@@ -80,10 +91,10 b' class TestPullrequestsController(object)'
80 pull_request = pr_util.create_pull_request(
91 pull_request = pr_util.create_pull_request(
81 mergeable=pr_merge_enabled, enable_notifications=False)
92 mergeable=pr_merge_enabled, enable_notifications=False)
82
93
83 response = self.app.get(url(
94 response = self.app.get(route_path(
84 controller='pullrequests', action='show',
95 'pullrequest_show',
85 repo_name=pull_request.target_repo.scm_instance().name,
96 repo_name=pull_request.target_repo.scm_instance().name,
86 pull_request_id=str(pull_request.pull_request_id)))
97 pull_request_id=pull_request.pull_request_id))
87
98
88 for commit_id in pull_request.revisions:
99 for commit_id in pull_request.revisions:
89 response.mustcontain(commit_id)
100 response.mustcontain(commit_id)
@@ -111,10 +122,10 b' class TestPullrequestsController(object)'
111 pull_request = pr_util.create_pull_request(
122 pull_request = pr_util.create_pull_request(
112 author=TEST_USER_REGULAR_LOGIN)
123 author=TEST_USER_REGULAR_LOGIN)
113
124
114 response = self.app.get(url(
125 response = self.app.get(route_path(
115 controller='pullrequests', action='show',
126 'pullrequest_show',
116 repo_name=pull_request.target_repo.scm_instance().name,
127 repo_name=pull_request.target_repo.scm_instance().name,
117 pull_request_id=str(pull_request.pull_request_id)))
128 pull_request_id=pull_request.pull_request_id))
118
129
119 response.mustcontain('Server-side pull request merging is disabled.')
130 response.mustcontain('Server-side pull request merging is disabled.')
120
131
@@ -126,10 +137,10 b' class TestPullrequestsController(object)'
126 pull_request.target_repo,
137 pull_request.target_repo,
127 UserModel().get_by_username(TEST_USER_REGULAR_LOGIN),
138 UserModel().get_by_username(TEST_USER_REGULAR_LOGIN),
128 'repository.write')
139 'repository.write')
129 response = self.app.get(url(
140 response = self.app.get(route_path(
130 controller='pullrequests', action='show',
141 'pullrequest_show',
131 repo_name=pull_request.target_repo.scm_instance().name,
142 repo_name=pull_request.target_repo.scm_instance().name,
132 pull_request_id=str(pull_request.pull_request_id)))
143 pull_request_id=pull_request.pull_request_id))
133
144
134 response.mustcontain('Server-side pull request merging is disabled.')
145 response.mustcontain('Server-side pull request merging is disabled.')
135
146
@@ -144,10 +155,10 b' class TestPullrequestsController(object)'
144 Session().add(pull_request)
155 Session().add(pull_request)
145 Session().commit()
156 Session().commit()
146
157
147 response = self.app.get(url(
158 response = self.app.get(route_path(
148 controller='pullrequests', action='show',
159 'pullrequest_show',
149 repo_name=pull_request.target_repo.scm_instance().name,
160 repo_name=pull_request.target_repo.scm_instance().name,
150 pull_request_id=str(pull_request.pull_request_id)))
161 pull_request_id=pull_request.pull_request_id))
151
162
152 for commit_id in pull_request.revisions:
163 for commit_id in pull_request.revisions:
153 response.mustcontain(commit_id)
164 response.mustcontain(commit_id)
@@ -158,22 +169,21 b' class TestPullrequestsController(object)'
158 Session().add(pull_request)
169 Session().add(pull_request)
159 Session().commit()
170 Session().commit()
160
171
161 self.app.get(url(
172 self.app.get(route_path(
162 controller='pullrequests', action='show',
173 'pullrequest_show',
163 repo_name=pull_request.target_repo.scm_instance().name,
174 repo_name=pull_request.target_repo.scm_instance().name,
164 pull_request_id=str(pull_request.pull_request_id)))
175 pull_request_id=pull_request.pull_request_id))
165
176
166 def test_edit_title_description(self, pr_util, csrf_token):
177 def test_edit_title_description(self, pr_util, csrf_token):
167 pull_request = pr_util.create_pull_request()
178 pull_request = pr_util.create_pull_request()
168 pull_request_id = pull_request.pull_request_id
179 pull_request_id = pull_request.pull_request_id
169
180
170 response = self.app.post(
181 response = self.app.post(
171 url(controller='pullrequests', action='update',
182 route_path('pullrequest_update',
172 repo_name=pull_request.target_repo.repo_name,
183 repo_name=pull_request.target_repo.repo_name,
173 pull_request_id=str(pull_request_id)),
184 pull_request_id=pull_request_id),
174 params={
185 params={
175 'edit_pull_request': 'true',
186 'edit_pull_request': 'true',
176 '_method': 'put',
177 'title': 'New title',
187 'title': 'New title',
178 'description': 'New description',
188 'description': 'New description',
179 'csrf_token': csrf_token})
189 'csrf_token': csrf_token})
@@ -192,12 +202,11 b' class TestPullrequestsController(object)'
192 pr_util.close()
202 pr_util.close()
193
203
194 response = self.app.post(
204 response = self.app.post(
195 url(controller='pullrequests', action='update',
205 route_path('pullrequest_update',
196 repo_name=pull_request.target_repo.repo_name,
206 repo_name=pull_request.target_repo.repo_name,
197 pull_request_id=str(pull_request_id)),
207 pull_request_id=pull_request_id),
198 params={
208 params={
199 'edit_pull_request': 'true',
209 'edit_pull_request': 'true',
200 '_method': 'put',
201 'title': 'New title',
210 'title': 'New title',
202 'description': 'New description',
211 'description': 'New description',
203 'csrf_token': csrf_token})
212 'csrf_token': csrf_token})
@@ -217,10 +226,10 b' class TestPullrequestsController(object)'
217 pull_request_id = pull_request.pull_request_id
226 pull_request_id = pull_request.pull_request_id
218
227
219 response = self.app.post(
228 response = self.app.post(
220 url(controller='pullrequests', action='update',
229 route_path('pullrequest_update',
221 repo_name=pull_request.target_repo.repo_name,
230 repo_name=pull_request.target_repo.repo_name,
222 pull_request_id=str(pull_request_id)),
231 pull_request_id=pull_request_id),
223 params={'update_commits': 'true', '_method': 'put',
232 params={'update_commits': 'true',
224 'csrf_token': csrf_token})
233 'csrf_token': csrf_token})
225
234
226 expected_msg = PullRequestModel.UPDATE_STATUS_MESSAGES[
235 expected_msg = PullRequestModel.UPDATE_STATUS_MESSAGES[
@@ -236,10 +245,10 b' class TestPullrequestsController(object)'
236 Session().commit()
245 Session().commit()
237
246
238 pull_request_id = pull_request.pull_request_id
247 pull_request_id = pull_request.pull_request_id
239 pull_request_url = url(
248 pull_request_url = route_path(
240 controller='pullrequests', action='show',
249 'pullrequest_show',
241 repo_name=pull_request.target_repo.repo_name,
250 repo_name=pull_request.target_repo.repo_name,
242 pull_request_id=str(pull_request_id))
251 pull_request_id=pull_request_id)
243
252
244 response = self.app.get(pull_request_url)
253 response = self.app.get(pull_request_url)
245
254
@@ -258,10 +267,9 b' class TestPullrequestsController(object)'
258 repo = pull_request.target_repo.repo_id
267 repo = pull_request.target_repo.repo_id
259
268
260 self.app.post(
269 self.app.post(
261 url(controller='pullrequests',
270 route_path('pullrequest_comment_create',
262 action='comment',
263 repo_name=pull_request.target_repo.scm_instance().name,
271 repo_name=pull_request.target_repo.scm_instance().name,
264 pull_request_id=str(pull_request_id)),
272 pull_request_id=pull_request_id),
265 params={
273 params={
266 'close_pull_request': '1',
274 'close_pull_request': '1',
267 'text': 'Closing a PR',
275 'text': 'Closing a PR',
@@ -298,10 +306,9 b' class TestPullrequestsController(object)'
298 repo = pull_request.target_repo.repo_id
306 repo = pull_request.target_repo.repo_id
299
307
300 self.app.post(
308 self.app.post(
301 url(controller='pullrequests',
309 route_path('pullrequest_comment_create',
302 action='comment',
303 repo_name=pull_request.target_repo.scm_instance().name,
310 repo_name=pull_request.target_repo.scm_instance().name,
304 pull_request_id=str(pull_request_id)),
311 pull_request_id=pull_request_id),
305 params={
312 params={
306 'close_pull_request': '1',
313 'close_pull_request': '1',
307 'csrf_token': csrf_token},
314 'csrf_token': csrf_token},
@@ -326,10 +333,9 b' class TestPullrequestsController(object)'
326 pull_request_id = pull_request.pull_request_id
333 pull_request_id = pull_request.pull_request_id
327
334
328 response = self.app.post(
335 response = self.app.post(
329 url(controller='pullrequests',
336 route_path('pullrequest_comment_create',
330 action='comment',
331 repo_name=pull_request.target_repo.scm_instance().name,
337 repo_name=pull_request.target_repo.scm_instance().name,
332 pull_request_id=str(pull_request.pull_request_id)),
338 pull_request_id=pull_request.pull_request_id),
333 params={
339 params={
334 'close_pull_request': 'true',
340 'close_pull_request': 'true',
335 'csrf_token': csrf_token},
341 'csrf_token': csrf_token},
@@ -356,11 +362,7 b' class TestPullrequestsController(object)'
356 source = backend.create_repo(heads=['change2'])
362 source = backend.create_repo(heads=['change2'])
357
363
358 response = self.app.post(
364 response = self.app.post(
359 url(
365 route_path('pullrequest_create', repo_name=source.repo_name),
360 controller='pullrequests',
361 action='create',
362 repo_name=source.repo_name
363 ),
364 [
366 [
365 ('source_repo', source.repo_name),
367 ('source_repo', source.repo_name),
366 ('source_ref', 'branch:default:' + commit_ids['change2']),
368 ('source_ref', 'branch:default:' + commit_ids['change2']),
@@ -417,11 +419,7 b' class TestPullrequestsController(object)'
417 source = backend.create_repo(heads=['change'])
419 source = backend.create_repo(heads=['change'])
418
420
419 response = self.app.post(
421 response = self.app.post(
420 url(
422 route_path('pullrequest_create', repo_name=source.repo_name),
421 controller='pullrequests',
422 action='create',
423 repo_name=source.repo_name
424 ),
425 [
423 [
426 ('source_repo', source.repo_name),
424 ('source_repo', source.repo_name),
427 ('source_ref', 'branch:default:' + commit_ids['change']),
425 ('source_ref', 'branch:default:' + commit_ids['change']),
@@ -485,11 +483,7 b' class TestPullrequestsController(object)'
485 source = backend.create_repo(heads=['change'])
483 source = backend.create_repo(heads=['change'])
486
484
487 response = self.app.post(
485 response = self.app.post(
488 url(
486 route_path('pullrequest_create', repo_name=source.repo_name),
489 controller='pullrequests',
490 action='create',
491 repo_name=source.repo_name
492 ),
493 [
487 [
494 ('source_repo', source.repo_name),
488 ('source_repo', source.repo_name),
495 ('source_ref', 'branch:default:' + commit_ids['change']),
489 ('source_ref', 'branch:default:' + commit_ids['change']),
@@ -542,10 +536,9 b' class TestPullrequestsController(object)'
542 repo_name = pull_request.target_repo.scm_instance().name,
536 repo_name = pull_request.target_repo.scm_instance().name,
543
537
544 response = self.app.post(
538 response = self.app.post(
545 url(controller='pullrequests',
539 route_path('pullrequest_merge',
546 action='merge',
547 repo_name=str(repo_name[0]),
540 repo_name=str(repo_name[0]),
548 pull_request_id=str(pull_request_id)),
541 pull_request_id=pull_request_id),
549 params={'csrf_token': csrf_token}).follow()
542 params={'csrf_token': csrf_token}).follow()
550
543
551 pull_request = PullRequest.get(pull_request_id)
544 pull_request = PullRequest.get(pull_request_id)
@@ -584,10 +577,9 b' class TestPullrequestsController(object)'
584 pull_request = PullRequest.get(pull_request_id)
577 pull_request = PullRequest.get(pull_request_id)
585
578
586 response = self.app.post(
579 response = self.app.post(
587 url(controller='pullrequests',
580 route_path('pullrequest_merge',
588 action='merge',
589 repo_name=pull_request.target_repo.scm_instance().name,
581 repo_name=pull_request.target_repo.scm_instance().name,
590 pull_request_id=str(pull_request.pull_request_id)),
582 pull_request_id=pull_request.pull_request_id),
591 params={'csrf_token': csrf_token}).follow()
583 params={'csrf_token': csrf_token}).follow()
592
584
593 assert response.status_int == 200
585 assert response.status_int == 200
@@ -599,13 +591,12 b' class TestPullrequestsController(object)'
599 def test_merge_pull_request_not_approved(self, pr_util, csrf_token):
591 def test_merge_pull_request_not_approved(self, pr_util, csrf_token):
600 pull_request = pr_util.create_pull_request(mergeable=True)
592 pull_request = pr_util.create_pull_request(mergeable=True)
601 pull_request_id = pull_request.pull_request_id
593 pull_request_id = pull_request.pull_request_id
602 repo_name = pull_request.target_repo.scm_instance().name,
594 repo_name = pull_request.target_repo.scm_instance().name
603
595
604 response = self.app.post(
596 response = self.app.post(
605 url(controller='pullrequests',
597 route_path('pullrequest_merge',
606 action='merge',
598 repo_name=repo_name,
607 repo_name=str(repo_name[0]),
599 pull_request_id=pull_request_id),
608 pull_request_id=str(pull_request_id)),
609 params={'csrf_token': csrf_token}).follow()
600 params={'csrf_token': csrf_token}).follow()
610
601
611 assert response.status_int == 200
602 assert response.status_int == 200
@@ -614,6 +605,28 b' class TestPullrequestsController(object)'
614 'Merge is not currently possible because of below failed checks.')
605 'Merge is not currently possible because of below failed checks.')
615 response.mustcontain('Pull request reviewer approval is pending.')
606 response.mustcontain('Pull request reviewer approval is pending.')
616
607
608 def test_merge_pull_request_renders_failure_reason(
609 self, user_regular, csrf_token, pr_util):
610 pull_request = pr_util.create_pull_request(mergeable=True, approved=True)
611 pull_request_id = pull_request.pull_request_id
612 repo_name = pull_request.target_repo.scm_instance().name
613
614 model_patcher = mock.patch.multiple(
615 PullRequestModel,
616 merge=mock.Mock(return_value=MergeResponse(
617 True, False, 'STUB_COMMIT_ID', MergeFailureReason.PUSH_FAILED)),
618 merge_status=mock.Mock(return_value=(True, 'WRONG_MESSAGE')))
619
620 with model_patcher:
621 response = self.app.post(
622 route_path('pullrequest_merge',
623 repo_name=repo_name,
624 pull_request_id=pull_request_id),
625 params={'csrf_token': csrf_token}, status=302)
626
627 assert_session_flash(response, PullRequestModel.MERGE_STATUS_MESSAGES[
628 MergeFailureReason.PUSH_FAILED])
629
617 def test_update_source_revision(self, backend, csrf_token):
630 def test_update_source_revision(self, backend, csrf_token):
618 commits = [
631 commits = [
619 {'message': 'ancestor'},
632 {'message': 'ancestor'},
@@ -649,10 +662,10 b' class TestPullrequestsController(object)'
649
662
650 # update PR
663 # update PR
651 self.app.post(
664 self.app.post(
652 url(controller='pullrequests', action='update',
665 route_path('pullrequest_update',
653 repo_name=target.repo_name,
666 repo_name=target.repo_name,
654 pull_request_id=str(pull_request_id)),
667 pull_request_id=pull_request_id),
655 params={'update_commits': 'true', '_method': 'put',
668 params={'update_commits': 'true',
656 'csrf_token': csrf_token})
669 'csrf_token': csrf_token})
657
670
658 # check that we have now both revisions
671 # check that we have now both revisions
@@ -661,8 +674,8 b' class TestPullrequestsController(object)'
661 commit_ids['change-2'], commit_ids['change']]
674 commit_ids['change-2'], commit_ids['change']]
662
675
663 # TODO: johbo: this should be a test on its own
676 # TODO: johbo: this should be a test on its own
664 response = self.app.get(url(
677 response = self.app.get(route_path(
665 controller='pullrequests', action='index',
678 'pullrequest_new',
666 repo_name=target.repo_name))
679 repo_name=target.repo_name))
667 assert response.status_int == 200
680 assert response.status_int == 200
668 assert 'Pull request updated to' in response.body
681 assert 'Pull request updated to' in response.body
@@ -707,10 +720,10 b' class TestPullrequestsController(object)'
707
720
708 # update PR
721 # update PR
709 self.app.post(
722 self.app.post(
710 url(controller='pullrequests', action='update',
723 route_path('pullrequest_update',
711 repo_name=target.repo_name,
724 repo_name=target.repo_name,
712 pull_request_id=str(pull_request_id)),
725 pull_request_id=pull_request_id),
713 params={'update_commits': 'true', '_method': 'put',
726 params={'update_commits': 'true',
714 'csrf_token': csrf_token},
727 'csrf_token': csrf_token},
715 status=200)
728 status=200)
716
729
@@ -722,8 +735,8 b' class TestPullrequestsController(object)'
722 commit_id=commit_ids['ancestor-new'])
735 commit_id=commit_ids['ancestor-new'])
723
736
724 # TODO: johbo: This should be a test on its own
737 # TODO: johbo: This should be a test on its own
725 response = self.app.get(url(
738 response = self.app.get(route_path(
726 controller='pullrequests', action='index',
739 'pullrequest_new',
727 repo_name=target.repo_name))
740 repo_name=target.repo_name))
728 assert response.status_int == 200
741 assert response.status_int == 200
729 assert 'Pull request updated to' in response.body
742 assert 'Pull request updated to' in response.body
@@ -770,10 +783,10 b' class TestPullrequestsController(object)'
770
783
771 # update PR
784 # update PR
772 self.app.post(
785 self.app.post(
773 url(controller='pullrequests', action='update',
786 route_path('pullrequest_update',
774 repo_name=target.repo_name,
787 repo_name=target.repo_name,
775 pull_request_id=str(pull_request_id)),
788 pull_request_id=pull_request_id),
776 params={'update_commits': 'true', '_method': 'put',
789 params={'update_commits': 'true',
777 'csrf_token': csrf_token},
790 'csrf_token': csrf_token},
778 status=200)
791 status=200)
779
792
@@ -814,10 +827,10 b' class TestPullrequestsController(object)'
814 vcs = repo.scm_instance()
827 vcs = repo.scm_instance()
815 vcs.remove_ref('refs/heads/{}'.format(branch_name))
828 vcs.remove_ref('refs/heads/{}'.format(branch_name))
816
829
817 response = self.app.get(url(
830 response = self.app.get(route_path(
818 controller='pullrequests', action='show',
831 'pullrequest_show',
819 repo_name=repo.repo_name,
832 repo_name=repo.repo_name,
820 pull_request_id=str(pull_request.pull_request_id)))
833 pull_request_id=pull_request.pull_request_id))
821
834
822 assert response.status_int == 200
835 assert response.status_int == 200
823 assert_response = AssertResponse(response)
836 assert_response = AssertResponse(response)
@@ -846,10 +859,10 b' class TestPullrequestsController(object)'
846 else:
859 else:
847 vcs.strip(pr_util.commit_ids['new-feature'])
860 vcs.strip(pr_util.commit_ids['new-feature'])
848
861
849 response = self.app.get(url(
862 response = self.app.get(route_path(
850 controller='pullrequests', action='show',
863 'pullrequest_show',
851 repo_name=pr_util.target_repository.repo_name,
864 repo_name=pr_util.target_repository.repo_name,
852 pull_request_id=str(pull_request.pull_request_id)))
865 pull_request_id=pull_request.pull_request_id))
853
866
854 assert response.status_int == 200
867 assert response.status_int == 200
855 assert_response = AssertResponse(response)
868 assert_response = AssertResponse(response)
@@ -882,20 +895,20 b' class TestPullrequestsController(object)'
882 vcs.strip(pr_util.commit_ids['new-feature'])
895 vcs.strip(pr_util.commit_ids['new-feature'])
883
896
884 response = self.app.post(
897 response = self.app.post(
885 url(controller='pullrequests', action='update',
898 route_path('pullrequest_update',
886 repo_name=pull_request.target_repo.repo_name,
899 repo_name=pull_request.target_repo.repo_name,
887 pull_request_id=str(pull_request.pull_request_id)),
900 pull_request_id=pull_request.pull_request_id),
888 params={'update_commits': 'true', '_method': 'put',
901 params={'update_commits': 'true',
889 'csrf_token': csrf_token})
902 'csrf_token': csrf_token})
890
903
891 assert response.status_int == 200
904 assert response.status_int == 200
892 assert response.body == 'true'
905 assert response.body == 'true'
893
906
894 # Make sure that after update, it won't raise 500 errors
907 # Make sure that after update, it won't raise 500 errors
895 response = self.app.get(url(
908 response = self.app.get(route_path(
896 controller='pullrequests', action='show',
909 'pullrequest_show',
897 repo_name=pr_util.target_repository.repo_name,
910 repo_name=pr_util.target_repository.repo_name,
898 pull_request_id=str(pull_request.pull_request_id)))
911 pull_request_id=pull_request.pull_request_id))
899
912
900 assert response.status_int == 200
913 assert response.status_int == 200
901 assert_response = AssertResponse(response)
914 assert_response = AssertResponse(response)
@@ -910,10 +923,10 b' class TestPullrequestsController(object)'
910 Session().add(pull_request)
923 Session().add(pull_request)
911 Session().commit()
924 Session().commit()
912
925
913 response = self.app.get(url(
926 response = self.app.get(route_path(
914 controller='pullrequests', action='show',
927 'pullrequest_show',
915 repo_name=pull_request.target_repo.scm_instance().name,
928 repo_name=pull_request.target_repo.scm_instance().name,
916 pull_request_id=str(pull_request.pull_request_id)))
929 pull_request_id=pull_request.pull_request_id))
917 assert response.status_int == 200
930 assert response.status_int == 200
918 assert_response = AssertResponse(response)
931 assert_response = AssertResponse(response)
919
932
@@ -944,10 +957,10 b' class TestPullrequestsController(object)'
944 Session().add(pull_request)
957 Session().add(pull_request)
945 Session().commit()
958 Session().commit()
946
959
947 response = self.app.get(url(
960 response = self.app.get(route_path(
948 controller='pullrequests', action='show',
961 'pullrequest_show',
949 repo_name=pull_request.target_repo.scm_instance().name,
962 repo_name=pull_request.target_repo.scm_instance().name,
950 pull_request_id=str(pull_request.pull_request_id)))
963 pull_request_id=pull_request.pull_request_id))
951 assert response.status_int == 200
964 assert response.status_int == 200
952 assert_response = AssertResponse(response)
965 assert_response = AssertResponse(response)
953
966
@@ -966,10 +979,10 b' class TestPullrequestsController(object)'
966 Session().add(pull_request)
979 Session().add(pull_request)
967 Session().commit()
980 Session().commit()
968
981
969 response = self.app.get(url(
982 response = self.app.get(route_path(
970 controller='pullrequests', action='show',
983 'pullrequest_show',
971 repo_name=pull_request.target_repo.scm_instance().name,
984 repo_name=pull_request.target_repo.scm_instance().name,
972 pull_request_id=str(pull_request.pull_request_id)))
985 pull_request_id=pull_request.pull_request_id))
973 assert response.status_int == 200
986 assert response.status_int == 200
974 assert_response = AssertResponse(response)
987 assert_response = AssertResponse(response)
975
988
@@ -996,10 +1009,10 b' class TestPullrequestsController(object)'
996 shadow_url = '{host}/{repo}/pull-request/{pr_id}/repository'.format(
1009 shadow_url = '{host}/{repo}/pull-request/{pr_id}/repository'.format(
997 host=http_host_only_stub, repo=target_repo.name, pr_id=pr_id)
1010 host=http_host_only_stub, repo=target_repo.name, pr_id=pr_id)
998
1011
999 response = self.app.get(url(
1012 response = self.app.get(route_path(
1000 controller='pullrequests', action='show',
1013 'pullrequest_show',
1001 repo_name=target_repo.name,
1014 repo_name=target_repo.name,
1002 pull_request_id=str(pr_id)))
1015 pull_request_id=pr_id))
1003
1016
1004 assertr = AssertResponse(response)
1017 assertr = AssertResponse(response)
1005 if mergeable:
1018 if mergeable:
@@ -1019,10 +1032,10 b' class TestPullrequestsControllerDelete(o'
1019 pull_request = pr_util.create_pull_request(
1032 pull_request = pr_util.create_pull_request(
1020 author=user_admin.username, enable_notifications=False)
1033 author=user_admin.username, enable_notifications=False)
1021
1034
1022 response = self.app.get(url(
1035 response = self.app.get(route_path(
1023 controller='pullrequests', action='show',
1036 'pullrequest_show',
1024 repo_name=pull_request.target_repo.scm_instance().name,
1037 repo_name=pull_request.target_repo.scm_instance().name,
1025 pull_request_id=str(pull_request.pull_request_id)))
1038 pull_request_id=pull_request.pull_request_id))
1026
1039
1027 response.mustcontain('id="delete_pullrequest"')
1040 response.mustcontain('id="delete_pullrequest"')
1028 response.mustcontain('Confirm to delete this pull request')
1041 response.mustcontain('Confirm to delete this pull request')
@@ -1032,10 +1045,10 b' class TestPullrequestsControllerDelete(o'
1032 pull_request = pr_util.create_pull_request(
1045 pull_request = pr_util.create_pull_request(
1033 author=user_regular.username, enable_notifications=False)
1046 author=user_regular.username, enable_notifications=False)
1034
1047
1035 response = self.app.get(url(
1048 response = self.app.get(route_path(
1036 controller='pullrequests', action='show',
1049 'pullrequest_show',
1037 repo_name=pull_request.target_repo.scm_instance().name,
1050 repo_name=pull_request.target_repo.scm_instance().name,
1038 pull_request_id=str(pull_request.pull_request_id)))
1051 pull_request_id=pull_request.pull_request_id))
1039
1052
1040 response.mustcontain('id="delete_pullrequest"')
1053 response.mustcontain('id="delete_pullrequest"')
1041 response.mustcontain('Confirm to delete this pull request')
1054 response.mustcontain('Confirm to delete this pull request')
@@ -1045,10 +1058,10 b' class TestPullrequestsControllerDelete(o'
1045 pull_request = pr_util.create_pull_request(
1058 pull_request = pr_util.create_pull_request(
1046 author=user_admin.username, enable_notifications=False)
1059 author=user_admin.username, enable_notifications=False)
1047
1060
1048 response = self.app.get(url(
1061 response = self.app.get(route_path(
1049 controller='pullrequests', action='show',
1062 'pullrequest_show',
1050 repo_name=pull_request.target_repo.scm_instance().name,
1063 repo_name=pull_request.target_repo.scm_instance().name,
1051 pull_request_id=str(pull_request.pull_request_id)))
1064 pull_request_id=pull_request.pull_request_id))
1052 response.mustcontain(no=['id="delete_pullrequest"'])
1065 response.mustcontain(no=['id="delete_pullrequest"'])
1053 response.mustcontain(no=['Confirm to delete this pull request'])
1066 response.mustcontain(no=['Confirm to delete this pull request'])
1054
1067
@@ -1063,10 +1076,10 b' class TestPullrequestsControllerDelete(o'
1063 pull_request.target_repo, user_regular,
1076 pull_request.target_repo, user_regular,
1064 'repository.write')
1077 'repository.write')
1065
1078
1066 response = self.app.get(url(
1079 response = self.app.get(route_path(
1067 controller='pullrequests', action='show',
1080 'pullrequest_show',
1068 repo_name=pull_request.target_repo.scm_instance().name,
1081 repo_name=pull_request.target_repo.scm_instance().name,
1069 pull_request_id=str(pull_request.pull_request_id)))
1082 pull_request_id=pull_request.pull_request_id))
1070
1083
1071 response.mustcontain('id="open_edit_pullrequest"')
1084 response.mustcontain('id="open_edit_pullrequest"')
1072 response.mustcontain('id="delete_pullrequest"')
1085 response.mustcontain('id="delete_pullrequest"')
@@ -1078,9 +1091,10 b' class TestPullrequestsControllerDelete(o'
1078 pull_request = pr_util.create_pull_request(
1091 pull_request = pr_util.create_pull_request(
1079 author=user_admin.username, enable_notifications=False)
1092 author=user_admin.username, enable_notifications=False)
1080
1093
1081 self.app.get(url(
1094 self.app.get(route_path(
1082 controller='pullrequests', action='delete_comment',
1095 'pullrequest_comment_delete',
1083 repo_name=pull_request.target_repo.scm_instance().name,
1096 repo_name=pull_request.target_repo.scm_instance().name,
1097 pull_request_id=pull_request.pull_request_id,
1084 comment_id=1024404), status=404)
1098 comment_id=1024404), status=404)
1085
1099
1086
1100
@@ -1090,17 +1104,9 b' def assert_pull_request_status(pull_requ'
1090 assert status == expected_status
1104 assert status == expected_status
1091
1105
1092
1106
1093 @pytest.mark.parametrize('action', ['index', 'create'])
1107 @pytest.mark.parametrize('route', ['pullrequest_new', 'pullrequest_create'])
1094 @pytest.mark.usefixtures("autologin_user")
1108 @pytest.mark.usefixtures("autologin_user")
1095 def test_redirects_to_repo_summary_for_svn_repositories(backend_svn, app, action):
1109 def test_forbidde_to_repo_summary_for_svn_repositories(backend_svn, app, route):
1096 response = app.get(url(
1110 response = app.get(
1097 controller='pullrequests', action=action,
1111 route_path(route, repo_name=backend_svn.repo_name), status=404)
1098 repo_name=backend_svn.repo_name))
1099 assert response.status_int == 302
1100
1112
1101 # Not allowed, redirect to the summary
1102 redirected = response.follow()
1103 summary_url = h.route_path('repo_summary', repo_name=backend_svn.repo_name)
1104
1105 # URL adds leading slash and path doesn't have it
1106 assert redirected.request.path == summary_url
This diff has been collapsed as it changes many lines, (630 lines changed) Show them Hide them
@@ -19,23 +19,35 b''
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22 import collections
22
23
23 import collections
24 import formencode
24 from pyramid.httpexceptions import HTTPFound, HTTPNotFound
25 import peppercorn
26 from pyramid.httpexceptions import (
27 HTTPFound, HTTPNotFound, HTTPForbidden, HTTPBadRequest)
25 from pyramid.view import view_config
28 from pyramid.view import view_config
29 from pyramid.renderers import render
26
30
31 from rhodecode import events
27 from rhodecode.apps._base import RepoAppView, DataGridAppView
32 from rhodecode.apps._base import RepoAppView, DataGridAppView
28 from rhodecode.lib import helpers as h, diffs, codeblocks
33
34 from rhodecode.lib import helpers as h, diffs, codeblocks, channelstream
35 from rhodecode.lib.base import vcs_operation_context
36 from rhodecode.lib.ext_json import json
29 from rhodecode.lib.auth import (
37 from rhodecode.lib.auth import (
30 LoginRequired, HasRepoPermissionAnyDecorator)
38 LoginRequired, HasRepoPermissionAnyDecorator, NotAnonymous, CSRFRequired)
31 from rhodecode.lib.utils2 import str2bool, safe_int, safe_str
39 from rhodecode.lib.utils2 import str2bool, safe_str, safe_unicode
32 from rhodecode.lib.vcs.backends.base import EmptyCommit
40 from rhodecode.lib.vcs.backends.base import EmptyCommit, UpdateFailureReason
33 from rhodecode.lib.vcs.exceptions import CommitDoesNotExistError, \
41 from rhodecode.lib.vcs.exceptions import (CommitDoesNotExistError,
34 RepositoryRequirementError, NodeDoesNotExistError
42 RepositoryRequirementError, NodeDoesNotExistError, EmptyRepositoryError)
43 from rhodecode.model.changeset_status import ChangesetStatusModel
35 from rhodecode.model.comment import CommentsModel
44 from rhodecode.model.comment import CommentsModel
36 from rhodecode.model.db import PullRequest, PullRequestVersion, \
45 from rhodecode.model.db import (func, or_, PullRequest, PullRequestVersion,
37 ChangesetComment, ChangesetStatus
46 ChangesetComment, ChangesetStatus, Repository)
47 from rhodecode.model.forms import PullRequestForm
48 from rhodecode.model.meta import Session
38 from rhodecode.model.pull_request import PullRequestModel, MergeCheck
49 from rhodecode.model.pull_request import PullRequestModel, MergeCheck
50 from rhodecode.model.scm import ScmModel
39
51
40 log = logging.getLogger(__name__)
52 log = logging.getLogger(__name__)
41
53
@@ -189,7 +201,6 b' class RepoPullRequestsView(RepoAppView, '
189 return data
201 return data
190
202
191 def _get_pr_version(self, pull_request_id, version=None):
203 def _get_pr_version(self, pull_request_id, version=None):
192 pull_request_id = safe_int(pull_request_id)
193 at_version = None
204 at_version = None
194
205
195 if version and version == 'latest':
206 if version and version == 'latest':
@@ -250,12 +261,12 b' class RepoPullRequestsView(RepoAppView, '
250 @LoginRequired()
261 @LoginRequired()
251 @HasRepoPermissionAnyDecorator(
262 @HasRepoPermissionAnyDecorator(
252 'repository.read', 'repository.write', 'repository.admin')
263 'repository.read', 'repository.write', 'repository.admin')
253 # @view_config(
264 @view_config(
254 # route_name='pullrequest_show', request_method='GET',
265 route_name='pullrequest_show', request_method='GET',
255 # renderer='rhodecode:templates/pullrequests/pullrequest_show.mako')
266 renderer='rhodecode:templates/pullrequests/pullrequest_show.mako')
256 def pull_request_show(self):
267 def pull_request_show(self):
257 pull_request_id = safe_int(
268 pull_request_id = self.request.matchdict.get('pull_request_id')
258 self.request.matchdict.get('pull_request_id'))
269
259 c = self.load_default_context()
270 c = self.load_default_context()
260
271
261 version = self.request.GET.get('version')
272 version = self.request.GET.get('version')
@@ -582,3 +593,590 b' class RepoPullRequestsView(RepoAppView, '
582 c.review_versions[_ver_pr] = status[0]
593 c.review_versions[_ver_pr] = status[0]
583
594
584 return self._get_template_context(c)
595 return self._get_template_context(c)
596
597 def assure_not_empty_repo(self):
598 _ = self.request.translate
599
600 try:
601 self.db_repo.scm_instance().get_commit()
602 except EmptyRepositoryError:
603 h.flash(h.literal(_('There are no commits yet')),
604 category='warning')
605 raise HTTPFound(
606 h.route_path('repo_summary', repo_name=self.db_repo.repo_name))
607
608 @LoginRequired()
609 @NotAnonymous()
610 @HasRepoPermissionAnyDecorator(
611 'repository.read', 'repository.write', 'repository.admin')
612 @view_config(
613 route_name='pullrequest_new', request_method='GET',
614 renderer='rhodecode:templates/pullrequests/pullrequest.mako')
615 def pull_request_new(self):
616 _ = self.request.translate
617 c = self.load_default_context()
618
619 self.assure_not_empty_repo()
620 source_repo = self.db_repo
621
622 commit_id = self.request.GET.get('commit')
623 branch_ref = self.request.GET.get('branch')
624 bookmark_ref = self.request.GET.get('bookmark')
625
626 try:
627 source_repo_data = PullRequestModel().generate_repo_data(
628 source_repo, commit_id=commit_id,
629 branch=branch_ref, bookmark=bookmark_ref)
630 except CommitDoesNotExistError as e:
631 log.exception(e)
632 h.flash(_('Commit does not exist'), 'error')
633 raise HTTPFound(
634 h.route_path('pullrequest_new', repo_name=source_repo.repo_name))
635
636 default_target_repo = source_repo
637
638 if source_repo.parent:
639 parent_vcs_obj = source_repo.parent.scm_instance()
640 if parent_vcs_obj and not parent_vcs_obj.is_empty():
641 # change default if we have a parent repo
642 default_target_repo = source_repo.parent
643
644 target_repo_data = PullRequestModel().generate_repo_data(
645 default_target_repo)
646
647 selected_source_ref = source_repo_data['refs']['selected_ref']
648
649 title_source_ref = selected_source_ref.split(':', 2)[1]
650 c.default_title = PullRequestModel().generate_pullrequest_title(
651 source=source_repo.repo_name,
652 source_ref=title_source_ref,
653 target=default_target_repo.repo_name
654 )
655
656 c.default_repo_data = {
657 'source_repo_name': source_repo.repo_name,
658 'source_refs_json': json.dumps(source_repo_data),
659 'target_repo_name': default_target_repo.repo_name,
660 'target_refs_json': json.dumps(target_repo_data),
661 }
662 c.default_source_ref = selected_source_ref
663
664 return self._get_template_context(c)
665
666 @LoginRequired()
667 @NotAnonymous()
668 @HasRepoPermissionAnyDecorator(
669 'repository.read', 'repository.write', 'repository.admin')
670 @view_config(
671 route_name='pullrequest_repo_refs', request_method='GET',
672 renderer='json_ext', xhr=True)
673 def pull_request_repo_refs(self):
674 target_repo_name = self.request.matchdict['target_repo_name']
675 repo = Repository.get_by_repo_name(target_repo_name)
676 if not repo:
677 raise HTTPNotFound()
678 return PullRequestModel().generate_repo_data(repo)
679
680 @LoginRequired()
681 @NotAnonymous()
682 @HasRepoPermissionAnyDecorator(
683 'repository.read', 'repository.write', 'repository.admin')
684 @view_config(
685 route_name='pullrequest_repo_destinations', request_method='GET',
686 renderer='json_ext', xhr=True)
687 def pull_request_repo_destinations(self):
688 _ = self.request.translate
689 filter_query = self.request.GET.get('query')
690
691 query = Repository.query() \
692 .order_by(func.length(Repository.repo_name)) \
693 .filter(
694 or_(Repository.repo_name == self.db_repo.repo_name,
695 Repository.fork_id == self.db_repo.repo_id))
696
697 if filter_query:
698 ilike_expression = u'%{}%'.format(safe_unicode(filter_query))
699 query = query.filter(
700 Repository.repo_name.ilike(ilike_expression))
701
702 add_parent = False
703 if self.db_repo.parent:
704 if filter_query in self.db_repo.parent.repo_name:
705 parent_vcs_obj = self.db_repo.parent.scm_instance()
706 if parent_vcs_obj and not parent_vcs_obj.is_empty():
707 add_parent = True
708
709 limit = 20 - 1 if add_parent else 20
710 all_repos = query.limit(limit).all()
711 if add_parent:
712 all_repos += [self.db_repo.parent]
713
714 repos = []
715 for obj in ScmModel().get_repos(all_repos):
716 repos.append({
717 'id': obj['name'],
718 'text': obj['name'],
719 'type': 'repo',
720 'obj': obj['dbrepo']
721 })
722
723 data = {
724 'more': False,
725 'results': [{
726 'text': _('Repositories'),
727 'children': repos
728 }] if repos else []
729 }
730 return data
731
732 @LoginRequired()
733 @NotAnonymous()
734 @HasRepoPermissionAnyDecorator(
735 'repository.read', 'repository.write', 'repository.admin')
736 @CSRFRequired()
737 @view_config(
738 route_name='pullrequest_create', request_method='POST',
739 renderer=None)
740 def pull_request_create(self):
741 _ = self.request.translate
742 self.assure_not_empty_repo()
743
744 controls = peppercorn.parse(self.request.POST.items())
745
746 try:
747 _form = PullRequestForm(self.db_repo.repo_id)().to_python(controls)
748 except formencode.Invalid as errors:
749 if errors.error_dict.get('revisions'):
750 msg = 'Revisions: %s' % errors.error_dict['revisions']
751 elif errors.error_dict.get('pullrequest_title'):
752 msg = _('Pull request requires a title with min. 3 chars')
753 else:
754 msg = _('Error creating pull request: {}').format(errors)
755 log.exception(msg)
756 h.flash(msg, 'error')
757
758 # would rather just go back to form ...
759 raise HTTPFound(
760 h.route_path('pullrequest_new', repo_name=self.db_repo_name))
761
762 source_repo = _form['source_repo']
763 source_ref = _form['source_ref']
764 target_repo = _form['target_repo']
765 target_ref = _form['target_ref']
766 commit_ids = _form['revisions'][::-1]
767
768 # find the ancestor for this pr
769 source_db_repo = Repository.get_by_repo_name(_form['source_repo'])
770 target_db_repo = Repository.get_by_repo_name(_form['target_repo'])
771
772 source_scm = source_db_repo.scm_instance()
773 target_scm = target_db_repo.scm_instance()
774
775 source_commit = source_scm.get_commit(source_ref.split(':')[-1])
776 target_commit = target_scm.get_commit(target_ref.split(':')[-1])
777
778 ancestor = source_scm.get_common_ancestor(
779 source_commit.raw_id, target_commit.raw_id, target_scm)
780
781 target_ref_type, target_ref_name, __ = _form['target_ref'].split(':')
782 target_ref = ':'.join((target_ref_type, target_ref_name, ancestor))
783
784 pullrequest_title = _form['pullrequest_title']
785 title_source_ref = source_ref.split(':', 2)[1]
786 if not pullrequest_title:
787 pullrequest_title = PullRequestModel().generate_pullrequest_title(
788 source=source_repo,
789 source_ref=title_source_ref,
790 target=target_repo
791 )
792
793 description = _form['pullrequest_desc']
794
795 get_default_reviewers_data, validate_default_reviewers = \
796 PullRequestModel().get_reviewer_functions()
797
798 # recalculate reviewers logic, to make sure we can validate this
799 reviewer_rules = get_default_reviewers_data(
800 self._rhodecode_db_user, source_db_repo,
801 source_commit, target_db_repo, target_commit)
802
803 given_reviewers = _form['review_members']
804 reviewers = validate_default_reviewers(given_reviewers, reviewer_rules)
805
806 try:
807 pull_request = PullRequestModel().create(
808 self._rhodecode_user.user_id, source_repo, source_ref, target_repo,
809 target_ref, commit_ids, reviewers, pullrequest_title,
810 description, reviewer_rules
811 )
812 Session().commit()
813 h.flash(_('Successfully opened new pull request'),
814 category='success')
815 except Exception as e:
816 msg = _('Error occurred during creation of this pull request.')
817 log.exception(msg)
818 h.flash(msg, category='error')
819 raise HTTPFound(
820 h.route_path('pullrequest_new', repo_name=self.db_repo_name))
821
822 raise HTTPFound(
823 h.route_path('pullrequest_show', repo_name=target_repo,
824 pull_request_id=pull_request.pull_request_id))
825
826 @LoginRequired()
827 @NotAnonymous()
828 @HasRepoPermissionAnyDecorator(
829 'repository.read', 'repository.write', 'repository.admin')
830 @CSRFRequired()
831 @view_config(
832 route_name='pullrequest_update', request_method='POST',
833 renderer='json_ext')
834 def pull_request_update(self):
835 pull_request_id = self.request.matchdict['pull_request_id']
836 pull_request = PullRequest.get_or_404(pull_request_id)
837
838 # only owner or admin can update it
839 allowed_to_update = PullRequestModel().check_user_update(
840 pull_request, self._rhodecode_user)
841 if allowed_to_update:
842 controls = peppercorn.parse(self.request.POST.items())
843
844 if 'review_members' in controls:
845 self._update_reviewers(
846 pull_request_id, controls['review_members'],
847 pull_request.reviewer_data)
848 elif str2bool(self.request.POST.get('update_commits', 'false')):
849 self._update_commits(pull_request)
850 elif str2bool(self.request.POST.get('edit_pull_request', 'false')):
851 self._edit_pull_request(pull_request)
852 else:
853 raise HTTPBadRequest()
854 return True
855 raise HTTPForbidden()
856
857 def _edit_pull_request(self, pull_request):
858 _ = self.request.translate
859 try:
860 PullRequestModel().edit(
861 pull_request, self.request.POST.get('title'),
862 self.request.POST.get('description'), self._rhodecode_user)
863 except ValueError:
864 msg = _(u'Cannot update closed pull requests.')
865 h.flash(msg, category='error')
866 return
867 else:
868 Session().commit()
869
870 msg = _(u'Pull request title & description updated.')
871 h.flash(msg, category='success')
872 return
873
874 def _update_commits(self, pull_request):
875 _ = self.request.translate
876 resp = PullRequestModel().update_commits(pull_request)
877
878 if resp.executed:
879
880 if resp.target_changed and resp.source_changed:
881 changed = 'target and source repositories'
882 elif resp.target_changed and not resp.source_changed:
883 changed = 'target repository'
884 elif not resp.target_changed and resp.source_changed:
885 changed = 'source repository'
886 else:
887 changed = 'nothing'
888
889 msg = _(
890 u'Pull request updated to "{source_commit_id}" with '
891 u'{count_added} added, {count_removed} removed commits. '
892 u'Source of changes: {change_source}')
893 msg = msg.format(
894 source_commit_id=pull_request.source_ref_parts.commit_id,
895 count_added=len(resp.changes.added),
896 count_removed=len(resp.changes.removed),
897 change_source=changed)
898 h.flash(msg, category='success')
899
900 channel = '/repo${}$/pr/{}'.format(
901 pull_request.target_repo.repo_name,
902 pull_request.pull_request_id)
903 message = msg + (
904 ' - <a onclick="window.location.reload()">'
905 '<strong>{}</strong></a>'.format(_('Reload page')))
906 channelstream.post_message(
907 channel, message, self._rhodecode_user.username,
908 registry=self.request.registry)
909 else:
910 msg = PullRequestModel.UPDATE_STATUS_MESSAGES[resp.reason]
911 warning_reasons = [
912 UpdateFailureReason.NO_CHANGE,
913 UpdateFailureReason.WRONG_REF_TYPE,
914 ]
915 category = 'warning' if resp.reason in warning_reasons else 'error'
916 h.flash(msg, category=category)
917
918 @LoginRequired()
919 @NotAnonymous()
920 @HasRepoPermissionAnyDecorator(
921 'repository.read', 'repository.write', 'repository.admin')
922 @CSRFRequired()
923 @view_config(
924 route_name='pullrequest_merge', request_method='POST',
925 renderer='json_ext')
926 def pull_request_merge(self):
927 """
928 Merge will perform a server-side merge of the specified
929 pull request, if the pull request is approved and mergeable.
930 After successful merging, the pull request is automatically
931 closed, with a relevant comment.
932 """
933 pull_request_id = self.request.matchdict['pull_request_id']
934 pull_request = PullRequest.get_or_404(pull_request_id)
935
936 check = MergeCheck.validate(pull_request, self._rhodecode_db_user)
937 merge_possible = not check.failed
938
939 for err_type, error_msg in check.errors:
940 h.flash(error_msg, category=err_type)
941
942 if merge_possible:
943 log.debug("Pre-conditions checked, trying to merge.")
944 extras = vcs_operation_context(
945 self.request.environ, repo_name=pull_request.target_repo.repo_name,
946 username=self._rhodecode_db_user.username, action='push',
947 scm=pull_request.target_repo.repo_type)
948 self._merge_pull_request(
949 pull_request, self._rhodecode_db_user, extras)
950 else:
951 log.debug("Pre-conditions failed, NOT merging.")
952
953 raise HTTPFound(
954 h.route_path('pullrequest_show',
955 repo_name=pull_request.target_repo.repo_name,
956 pull_request_id=pull_request.pull_request_id))
957
958 def _merge_pull_request(self, pull_request, user, extras):
959 _ = self.request.translate
960 merge_resp = PullRequestModel().merge(pull_request, user, extras=extras)
961
962 if merge_resp.executed:
963 log.debug("The merge was successful, closing the pull request.")
964 PullRequestModel().close_pull_request(
965 pull_request.pull_request_id, user)
966 Session().commit()
967 msg = _('Pull request was successfully merged and closed.')
968 h.flash(msg, category='success')
969 else:
970 log.debug(
971 "The merge was not successful. Merge response: %s",
972 merge_resp)
973 msg = PullRequestModel().merge_status_message(
974 merge_resp.failure_reason)
975 h.flash(msg, category='error')
976
977 def _update_reviewers(self, pull_request_id, review_members, reviewer_rules):
978 _ = self.request.translate
979 get_default_reviewers_data, validate_default_reviewers = \
980 PullRequestModel().get_reviewer_functions()
981
982 try:
983 reviewers = validate_default_reviewers(review_members, reviewer_rules)
984 except ValueError as e:
985 log.error('Reviewers Validation: {}'.format(e))
986 h.flash(e, category='error')
987 return
988
989 PullRequestModel().update_reviewers(
990 pull_request_id, reviewers, self._rhodecode_user)
991 h.flash(_('Pull request reviewers updated.'), category='success')
992 Session().commit()
993
994 @LoginRequired()
995 @NotAnonymous()
996 @HasRepoPermissionAnyDecorator(
997 'repository.read', 'repository.write', 'repository.admin')
998 @CSRFRequired()
999 @view_config(
1000 route_name='pullrequest_delete', request_method='POST',
1001 renderer='json_ext')
1002 def pull_request_delete(self):
1003 _ = self.request.translate
1004
1005 pull_request_id = self.request.matchdict['pull_request_id']
1006 pull_request = PullRequest.get_or_404(pull_request_id)
1007
1008 pr_closed = pull_request.is_closed()
1009 allowed_to_delete = PullRequestModel().check_user_delete(
1010 pull_request, self._rhodecode_user) and not pr_closed
1011
1012 # only owner can delete it !
1013 if allowed_to_delete:
1014 PullRequestModel().delete(pull_request, self._rhodecode_user)
1015 Session().commit()
1016 h.flash(_('Successfully deleted pull request'),
1017 category='success')
1018 raise HTTPFound(h.route_path('my_account_pullrequests'))
1019
1020 log.warning('user %s tried to delete pull request without access',
1021 self._rhodecode_user)
1022 raise HTTPNotFound()
1023
1024 @LoginRequired()
1025 @NotAnonymous()
1026 @HasRepoPermissionAnyDecorator(
1027 'repository.read', 'repository.write', 'repository.admin')
1028 @CSRFRequired()
1029 @view_config(
1030 route_name='pullrequest_comment_create', request_method='POST',
1031 renderer='json_ext')
1032 def pull_request_comment_create(self):
1033 _ = self.request.translate
1034 pull_request_id = self.request.matchdict['pull_request_id']
1035 pull_request = PullRequest.get_or_404(pull_request_id)
1036 if pull_request.is_closed():
1037 log.debug('comment: forbidden because pull request is closed')
1038 raise HTTPForbidden()
1039
1040 c = self.load_default_context()
1041
1042 status = self.request.POST.get('changeset_status', None)
1043 text = self.request.POST.get('text')
1044 comment_type = self.request.POST.get('comment_type')
1045 resolves_comment_id = self.request.POST.get('resolves_comment_id', None)
1046 close_pull_request = self.request.POST.get('close_pull_request')
1047
1048 # the logic here should work like following, if we submit close
1049 # pr comment, use `close_pull_request_with_comment` function
1050 # else handle regular comment logic
1051
1052 if close_pull_request:
1053 # only owner or admin or person with write permissions
1054 allowed_to_close = PullRequestModel().check_user_update(
1055 pull_request, self._rhodecode_user)
1056 if not allowed_to_close:
1057 log.debug('comment: forbidden because not allowed to close '
1058 'pull request %s', pull_request_id)
1059 raise HTTPForbidden()
1060 comment, status = PullRequestModel().close_pull_request_with_comment(
1061 pull_request, self._rhodecode_user, self.db_repo, message=text)
1062 Session().flush()
1063 events.trigger(
1064 events.PullRequestCommentEvent(pull_request, comment))
1065
1066 else:
1067 # regular comment case, could be inline, or one with status.
1068 # for that one we check also permissions
1069
1070 allowed_to_change_status = PullRequestModel().check_user_change_status(
1071 pull_request, self._rhodecode_user)
1072
1073 if status and allowed_to_change_status:
1074 message = (_('Status change %(transition_icon)s %(status)s')
1075 % {'transition_icon': '>',
1076 'status': ChangesetStatus.get_status_lbl(status)})
1077 text = text or message
1078
1079 comment = CommentsModel().create(
1080 text=text,
1081 repo=self.db_repo.repo_id,
1082 user=self._rhodecode_user.user_id,
1083 pull_request=pull_request_id,
1084 f_path=self.request.POST.get('f_path'),
1085 line_no=self.request.POST.get('line'),
1086 status_change=(ChangesetStatus.get_status_lbl(status)
1087 if status and allowed_to_change_status else None),
1088 status_change_type=(status
1089 if status and allowed_to_change_status else None),
1090 comment_type=comment_type,
1091 resolves_comment_id=resolves_comment_id
1092 )
1093
1094 if allowed_to_change_status:
1095 # calculate old status before we change it
1096 old_calculated_status = pull_request.calculated_review_status()
1097
1098 # get status if set !
1099 if status:
1100 ChangesetStatusModel().set_status(
1101 self.db_repo.repo_id,
1102 status,
1103 self._rhodecode_user.user_id,
1104 comment,
1105 pull_request=pull_request_id
1106 )
1107
1108 Session().flush()
1109 events.trigger(
1110 events.PullRequestCommentEvent(pull_request, comment))
1111
1112 # we now calculate the status of pull request, and based on that
1113 # calculation we set the commits status
1114 calculated_status = pull_request.calculated_review_status()
1115 if old_calculated_status != calculated_status:
1116 PullRequestModel()._trigger_pull_request_hook(
1117 pull_request, self._rhodecode_user, 'review_status_change')
1118
1119 Session().commit()
1120
1121 data = {
1122 'target_id': h.safeid(h.safe_unicode(
1123 self.request.POST.get('f_path'))),
1124 }
1125 if comment:
1126 c.co = comment
1127 rendered_comment = render(
1128 'rhodecode:templates/changeset/changeset_comment_block.mako',
1129 self._get_template_context(c), self.request)
1130
1131 data.update(comment.get_dict())
1132 data.update({'rendered_text': rendered_comment})
1133
1134 return data
1135
1136 @LoginRequired()
1137 @NotAnonymous()
1138 @HasRepoPermissionAnyDecorator(
1139 'repository.read', 'repository.write', 'repository.admin')
1140 @CSRFRequired()
1141 @view_config(
1142 route_name='pullrequest_comment_delete', request_method='POST',
1143 renderer='json_ext')
1144 def pull_request_comment_delete(self):
1145 commit_id = self.request.matchdict['commit_id']
1146 comment_id = self.request.matchdict['comment_id']
1147 pull_request_id = self.request.matchdict['pull_request_id']
1148
1149 pull_request = PullRequest.get_or_404(pull_request_id)
1150 if pull_request.is_closed():
1151 log.debug('comment: forbidden because pull request is closed')
1152 raise HTTPForbidden()
1153
1154 comment = ChangesetComment.get_or_404(comment_id)
1155 if not comment:
1156 log.debug('Comment with id:%s not found, skipping', comment_id)
1157 # comment already deleted in another call probably
1158 return True
1159
1160 if comment.pull_request.is_closed():
1161 # don't allow deleting comments on closed pull request
1162 raise HTTPForbidden()
1163
1164 is_repo_admin = h.HasRepoPermissionAny('repository.admin')(self.db_repo_name)
1165 super_admin = h.HasPermissionAny('hg.admin')()
1166 comment_owner = comment.author.user_id == self._rhodecode_user.user_id
1167 is_repo_comment = comment.repo.repo_name == self.db_repo_name
1168 comment_repo_admin = is_repo_admin and is_repo_comment
1169
1170 if super_admin or comment_owner or comment_repo_admin:
1171 old_calculated_status = comment.pull_request.calculated_review_status()
1172 CommentsModel().delete(comment=comment, user=self._rhodecode_user)
1173 Session().commit()
1174 calculated_status = comment.pull_request.calculated_review_status()
1175 if old_calculated_status != calculated_status:
1176 PullRequestModel()._trigger_pull_request_hook(
1177 comment.pull_request, self._rhodecode_user, 'review_status_change')
1178 return True
1179 else:
1180 log.warning('No permissions for user %s to delete comment_id: %s',
1181 self._rhodecode_db_user, comment_id)
1182 raise HTTPNotFound()
@@ -503,73 +503,6 b' def make_map(config):'
503 requirements=URL_NAME_REQUIREMENTS)
503 requirements=URL_NAME_REQUIREMENTS)
504
504
505
505
506 rmap.connect('pullrequest_home',
507 '/{repo_name}/pull-request/new', controller='pullrequests',
508 action='index', conditions={'function': check_repo,
509 'method': ['GET']},
510 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
511
512 rmap.connect('pullrequest',
513 '/{repo_name}/pull-request/new', controller='pullrequests',
514 action='create', conditions={'function': check_repo,
515 'method': ['POST']},
516 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
517
518 rmap.connect('pullrequest_repo_refs',
519 '/{repo_name}/pull-request/refs/{target_repo_name:.*?[^/]}',
520 controller='pullrequests',
521 action='get_repo_refs',
522 conditions={'function': check_repo, 'method': ['GET']},
523 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
524
525 rmap.connect('pullrequest_repo_destinations',
526 '/{repo_name}/pull-request/repo-destinations',
527 controller='pullrequests',
528 action='get_repo_destinations',
529 conditions={'function': check_repo, 'method': ['GET']},
530 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
531
532 rmap.connect('pullrequest_show',
533 '/{repo_name}/pull-request/{pull_request_id}',
534 controller='pullrequests',
535 action='show', conditions={'function': check_repo,
536 'method': ['GET']},
537 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
538
539 rmap.connect('pullrequest_update',
540 '/{repo_name}/pull-request/{pull_request_id}',
541 controller='pullrequests',
542 action='update', conditions={'function': check_repo,
543 'method': ['PUT']},
544 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
545
546 rmap.connect('pullrequest_merge',
547 '/{repo_name}/pull-request/{pull_request_id}',
548 controller='pullrequests',
549 action='merge', conditions={'function': check_repo,
550 'method': ['POST']},
551 requirements=URL_NAME_REQUIREMENTS)
552
553 rmap.connect('pullrequest_delete',
554 '/{repo_name}/pull-request/{pull_request_id}',
555 controller='pullrequests',
556 action='delete', conditions={'function': check_repo,
557 'method': ['DELETE']},
558 requirements=URL_NAME_REQUIREMENTS)
559
560 rmap.connect('pullrequest_comment',
561 '/{repo_name}/pull-request-comment/{pull_request_id}',
562 controller='pullrequests',
563 action='comment', conditions={'function': check_repo,
564 'method': ['POST']},
565 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
566
567 rmap.connect('pullrequest_comment_delete',
568 '/{repo_name}/pull-request-comment/{comment_id}/delete',
569 controller='pullrequests', action='delete_comment',
570 conditions={'function': check_repo, 'method': ['DELETE']},
571 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
572
573 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
506 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
574 controller='forks', action='fork_create',
507 controller='forks', action='fork_create',
575 conditions={'function': check_repo, 'method': ['POST']},
508 conditions={'function': check_repo, 'method': ['POST']},
@@ -298,12 +298,11 b' class CommentsModel(BaseModel):'
298 pr_target_repo = pull_request_obj.target_repo
298 pr_target_repo = pull_request_obj.target_repo
299 pr_source_repo = pull_request_obj.source_repo
299 pr_source_repo = pull_request_obj.source_repo
300
300
301 pr_comment_url = h.url(
301 pr_comment_url = h.route_url(
302 'pullrequest_show',
302 'pullrequest_show',
303 repo_name=pr_target_repo.repo_name,
303 repo_name=pr_target_repo.repo_name,
304 pull_request_id=pull_request_obj.pull_request_id,
304 pull_request_id=pull_request_obj.pull_request_id,
305 anchor='comment-%s' % comment.comment_id,
305 anchor='comment-%s' % comment.comment_id)
306 qualified=True,)
307
306
308 # set some variables for email notification
307 # set some variables for email notification
309 pr_target_repo_url = h.route_url(
308 pr_target_repo_url = h.route_url(
@@ -1541,6 +1541,7 b' class MergeCheck(object):'
1541 if fail_early:
1541 if fail_early:
1542 return merge_check
1542 return merge_check
1543
1543
1544 log.debug('MergeCheck: is failed: %s', merge_check.failed)
1544 return merge_check
1545 return merge_check
1545
1546
1546
1547
@@ -15,14 +15,6 b' function registerRCRoutes() {'
15 pyroutes.register('new_repo', '/_admin/create_repository', []);
15 pyroutes.register('new_repo', '/_admin/create_repository', []);
16 pyroutes.register('edit_user', '/_admin/users/%(user_id)s/edit', ['user_id']);
16 pyroutes.register('edit_user', '/_admin/users/%(user_id)s/edit', ['user_id']);
17 pyroutes.register('edit_user_group_members', '/_admin/user_groups/%(user_group_id)s/edit/members', ['user_group_id']);
17 pyroutes.register('edit_user_group_members', '/_admin/user_groups/%(user_group_id)s/edit/members', ['user_group_id']);
18 pyroutes.register('pullrequest_home', '/%(repo_name)s/pull-request/new', ['repo_name']);
19 pyroutes.register('pullrequest', '/%(repo_name)s/pull-request/new', ['repo_name']);
20 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
21 pyroutes.register('pullrequest_repo_destinations', '/%(repo_name)s/pull-request/repo-destinations', ['repo_name']);
22 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
23 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
24 pyroutes.register('pullrequest_comment', '/%(repo_name)s/pull-request-comment/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
25 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request-comment/%(comment_id)s/delete', ['repo_name', 'comment_id']);
26 pyroutes.register('favicon', '/favicon.ico', []);
18 pyroutes.register('favicon', '/favicon.ico', []);
27 pyroutes.register('robots', '/robots.txt', []);
19 pyroutes.register('robots', '/robots.txt', []);
28 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
20 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
@@ -152,6 +144,15 b' function registerRCRoutes() {'
152 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
144 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
153 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
145 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
154 pyroutes.register('pullrequest_show_all_data', '/%(repo_name)s/pull-request-data', ['repo_name']);
146 pyroutes.register('pullrequest_show_all_data', '/%(repo_name)s/pull-request-data', ['repo_name']);
147 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
148 pyroutes.register('pullrequest_repo_destinations', '/%(repo_name)s/pull-request/repo-destinations', ['repo_name']);
149 pyroutes.register('pullrequest_new', '/%(repo_name)s/pull-request/new', ['repo_name']);
150 pyroutes.register('pullrequest_create', '/%(repo_name)s/pull-request/create', ['repo_name']);
151 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s/update', ['repo_name', 'pull_request_id']);
152 pyroutes.register('pullrequest_merge', '/%(repo_name)s/pull-request/%(pull_request_id)s/merge', ['repo_name', 'pull_request_id']);
153 pyroutes.register('pullrequest_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/delete', ['repo_name', 'pull_request_id']);
154 pyroutes.register('pullrequest_comment_create', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment', ['repo_name', 'pull_request_id']);
155 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment/%(comment_id)s/delete', ['repo_name', 'pull_request_id', 'comment_id']);
155 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
156 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
156 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
157 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
157 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
158 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
@@ -138,7 +138,7 b' var bindToggleButtons = function() {'
138 'commit_id': this.commitId});
138 'commit_id': this.commitId});
139
139
140 } else if (this.pullRequestId) {
140 } else if (this.pullRequestId) {
141 this.submitUrl = pyroutes.url('pullrequest_comment',
141 this.submitUrl = pyroutes.url('pullrequest_comment_create',
142 {'repo_name': templateContext.repo_name,
142 {'repo_name': templateContext.repo_name,
143 'pull_request_id': this.pullRequestId});
143 'pull_request_id': this.pullRequestId});
144 this.selfUrl = pyroutes.url('pullrequest_show',
144 this.selfUrl = pyroutes.url('pullrequest_show',
@@ -387,7 +387,6 b' var editPullRequest = function(repo_name'
387 {"repo_name": repo_name, "pull_request_id": pull_request_id});
387 {"repo_name": repo_name, "pull_request_id": pull_request_id});
388
388
389 var postData = {
389 var postData = {
390 '_method': 'put',
391 'title': title,
390 'title': title,
392 'description': description,
391 'description': description,
393 'edit_pull_request': true,
392 'edit_pull_request': true,
@@ -276,7 +276,7 b''
276 %if c.rhodecode_user.username != h.DEFAULT_USER:
276 %if c.rhodecode_user.username != h.DEFAULT_USER:
277 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
277 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
278 <li><a href="${h.url('repo_fork_home',repo_name=c.repo_name)}">${_('Fork')}</a></li>
278 <li><a href="${h.url('repo_fork_home',repo_name=c.repo_name)}">${_('Fork')}</a></li>
279 <li><a href="${h.url('pullrequest_home',repo_name=c.repo_name)}">${_('Create Pull Request')}</a></li>
279 <li><a href="${h.route_path('pullrequest_new',repo_name=c.repo_name)}">${_('Create Pull Request')}</a></li>
280 %endif
280 %endif
281 %endif
281 %endif
282 </ul>
282 </ul>
@@ -55,7 +55,7 b''
55 ## pr open link
55 ## pr open link
56 %if h.is_hg(c.rhodecode_repo) or h.is_git(c.rhodecode_repo):
56 %if h.is_hg(c.rhodecode_repo) or h.is_git(c.rhodecode_repo):
57 <span>
57 <span>
58 <a id="open_new_pull_request" class="btn btn-small btn-success" href="${h.url('pullrequest_home',repo_name=c.repo_name)}">
58 <a id="open_new_pull_request" class="btn btn-small btn-success" href="${h.route_path('pullrequest_new',repo_name=c.repo_name)}">
59 ${_('Open new pull request')}
59 ${_('Open new pull request')}
60 </a>
60 </a>
61 </span>
61 </span>
@@ -176,7 +176,7 b''
176 .show();
176 .show();
177
177
178 $commitRangeClear.show();
178 $commitRangeClear.show();
179 var _url = pyroutes.url('pullrequest_home',
179 var _url = pyroutes.url('pullrequest_new',
180 {'repo_name': '${c.repo_name}',
180 {'repo_name': '${c.repo_name}',
181 'commit': revEnd});
181 'commit': revEnd});
182 open_new_pull_request.attr('href', _url);
182 open_new_pull_request.attr('href', _url);
@@ -186,12 +186,12 b''
186 $commitRangeClear.hide();
186 $commitRangeClear.hide();
187
187
188 %if c.branch_name:
188 %if c.branch_name:
189 var _url = pyroutes.url('pullrequest_home',
189 var _url = pyroutes.url('pullrequest_new',
190 {'repo_name': '${c.repo_name}',
190 {'repo_name': '${c.repo_name}',
191 'branch':'${c.branch_name}'});
191 'branch':'${c.branch_name}'});
192 open_new_pull_request.attr('href', _url);
192 open_new_pull_request.attr('href', _url);
193 %else:
193 %else:
194 var _url = pyroutes.url('pullrequest_home',
194 var _url = pyroutes.url('pullrequest_new',
195 {'repo_name': '${c.repo_name}'});
195 {'repo_name': '${c.repo_name}'});
196 open_new_pull_request.attr('href', _url);
196 open_new_pull_request.attr('href', _url);
197 %endif
197 %endif
@@ -27,7 +27,7 b''
27 <div class="edit-file-title">
27 <div class="edit-file-title">
28 ${self.breadcrumbs()}
28 ${self.breadcrumbs()}
29 </div>
29 </div>
30 ${h.secure_form(h.route_path('repo_files_delete_file', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.f_path), id='eform', method='POST', class_="form-horizontal")}
30 ${h.secure_form(h.route_path('repo_files_delete_file', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.f_path), id='eform', method='POST', class_="form-horizontal", request=request)}
31 <div class="edit-file-fieldset">
31 <div class="edit-file-fieldset">
32 <div class="fieldset">
32 <div class="fieldset">
33 <div id="destination-label" class="left-label">
33 <div id="destination-label" class="left-label">
@@ -42,7 +42,7 b''
42 </div>
42 </div>
43
43
44 <div class="table">
44 <div class="table">
45 ${h.secure_form(h.route_path('repo_files_update_file', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.f_path), id='eform', method='POST')}
45 ${h.secure_form(h.route_path('repo_files_update_file', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.f_path), id='eform', method='POST', request=request)}
46 <div id="codeblock" class="codeblock" >
46 <div id="codeblock" class="codeblock" >
47 <div class="code-header">
47 <div class="code-header">
48 <div class="stats">
48 <div class="stats">
@@ -22,7 +22,7 b''
22 ${self.repo_page_title(c.rhodecode_db_repo)}
22 ${self.repo_page_title(c.rhodecode_db_repo)}
23 </div>
23 </div>
24
24
25 ${h.secure_form(h.url('pullrequest', repo_name=c.repo_name), method='post', id='pull_request_form')}
25 ${h.secure_form(h.route_path('pullrequest_create', repo_name=c.repo_name), id='pull_request_form', method='POST', request=request)}
26
26
27 ${self.breadcrumbs()}
27 ${self.breadcrumbs()}
28
28
@@ -32,7 +32,7 b''
32 <div class="pull-request-merge-actions">
32 <div class="pull-request-merge-actions">
33 % if c.allowed_to_merge:
33 % if c.allowed_to_merge:
34 <div class="pull-right">
34 <div class="pull-right">
35 ${h.secure_form(h.url('pullrequest_merge', repo_name=c.repo_name, pull_request_id=c.pull_request.pull_request_id), id='merge_pull_request_form')}
35 ${h.secure_form(h.route_path('pullrequest_merge', repo_name=c.repo_name, pull_request_id=c.pull_request.pull_request_id), id='merge_pull_request_form', method='POST', request=request)}
36 <% merge_disabled = ' disabled' if c.pr_merge_possible is False else '' %>
36 <% merge_disabled = ' disabled' if c.pr_merge_possible is False else '' %>
37 <a class="btn" href="#" onclick="refreshMergeChecks(); return false;">${_('refresh checks')}</a>
37 <a class="btn" href="#" onclick="refreshMergeChecks(); return false;">${_('refresh checks')}</a>
38 <input type="submit" id="merge_pull_request" value="${_('Merge Pull Request')}" class="btn${merge_disabled}"${merge_disabled}>
38 <input type="submit" id="merge_pull_request" value="${_('Merge Pull Request')}" class="btn${merge_disabled}"${merge_disabled}>
@@ -32,7 +32,7 b''
32
32
33 <script type="text/javascript">
33 <script type="text/javascript">
34 // TODO: marcink switch this to pyroutes
34 // TODO: marcink switch this to pyroutes
35 AJAX_COMMENT_DELETE_URL = "${h.url('pullrequest_comment_delete',repo_name=c.repo_name,comment_id='__COMMENT_ID__')}";
35 AJAX_COMMENT_DELETE_URL = "${h.route_path('pullrequest_comment_delete',repo_name=c.repo_name,pull_request_id=c.pull_request.pull_request_id,comment_id='__COMMENT_ID__')}";
36 templateContext.pull_request_data.pull_request_id = ${c.pull_request.pull_request_id};
36 templateContext.pull_request_data.pull_request_id = ${c.pull_request.pull_request_id};
37 </script>
37 </script>
38 <div class="box">
38 <div class="box">
@@ -52,7 +52,7 b''
52 %if c.allowed_to_update:
52 %if c.allowed_to_update:
53 <div id="delete_pullrequest" class="pull-right action_button ${'' if c.allowed_to_delete else 'disabled' }" style="clear:inherit;padding: 0">
53 <div id="delete_pullrequest" class="pull-right action_button ${'' if c.allowed_to_delete else 'disabled' }" style="clear:inherit;padding: 0">
54 % if c.allowed_to_delete:
54 % if c.allowed_to_delete:
55 ${h.secure_form(h.url('pullrequest_delete', repo_name=c.pull_request.target_repo.repo_name, pull_request_id=c.pull_request.pull_request_id),method='delete')}
55 ${h.secure_form(h.route_path('pullrequest_delete', repo_name=c.pull_request.target_repo.repo_name, pull_request_id=c.pull_request.pull_request_id), method='POST', request=request)}
56 ${h.submit('remove_%s' % c.pull_request.pull_request_id, _('Delete'),
56 ${h.submit('remove_%s' % c.pull_request.pull_request_id, _('Delete'),
57 class_="btn btn-link btn-danger no-margin",onclick="return confirm('"+_('Confirm to delete this pull request')+"');")}
57 class_="btn btn-link btn-danger no-margin",onclick="return confirm('"+_('Confirm to delete this pull request')+"');")}
58 ${h.end_form()}
58 ${h.end_form()}
@@ -200,7 +200,7 b''
200 <tr class="version-pr" style="display: ${display_row}">
200 <tr class="version-pr" style="display: ${display_row}">
201 <td>
201 <td>
202 <code>
202 <code>
203 <a href="${h.url.current(version=ver_pr or 'latest')}">v${ver_pos}</a>
203 <a href="${request.current_route_path(_query=dict(version=ver_pr or 'latest'))}">v${ver_pos}</a>
204 </code>
204 </code>
205 </td>
205 </td>
206 <td>
206 <td>
@@ -616,7 +616,7 b''
616 </div>
616 </div>
617
617
618 ## main comment form and it status
618 ## main comment form and it status
619 ${comment.comments(h.url('pullrequest_comment', repo_name=c.repo_name,
619 ${comment.comments(h.route_path('pullrequest_comment_create', repo_name=c.repo_name,
620 pull_request_id=c.pull_request.pull_request_id),
620 pull_request_id=c.pull_request.pull_request_id),
621 c.pull_request_review_status,
621 c.pull_request_review_status,
622 is_pull_request=True, change_status=c.allowed_to_change_status)}
622 is_pull_request=True, change_status=c.allowed_to_change_status)}
@@ -730,7 +730,7 b''
730 };
730 };
731
731
732 refreshMergeChecks = function(){
732 refreshMergeChecks = function(){
733 var loadUrl = "${h.url.current(merge_checks=1)}";
733 var loadUrl = "${request.current_route_path(_query=dict(merge_checks=1))}";
734 $('.pull-request-merge').css('opacity', 0.3);
734 $('.pull-request-merge').css('opacity', 0.3);
735 $('.action-buttons-extra').css('opacity', 0.3);
735 $('.action-buttons-extra').css('opacity', 0.3);
736
736
@@ -30,7 +30,7 b''
30 <li>
30 <li>
31 %if c.rhodecode_user.username != h.DEFAULT_USER:
31 %if c.rhodecode_user.username != h.DEFAULT_USER:
32 <span>
32 <span>
33 <a id="open_new_pull_request" class="btn btn-small btn-success" href="${h.url('pullrequest_home',repo_name=c.repo_name)}">
33 <a id="open_new_pull_request" class="btn btn-small btn-success" href="${h.route_path('pullrequest_new',repo_name=c.repo_name)}">
34 ${_('Open new Pull Request')}
34 ${_('Open new Pull Request')}
35 </a>
35 </a>
36 </span>
36 </span>
@@ -200,13 +200,14 b' class TestAdminUsersGroupsController(Tes'
200 assert response.body == '{"members": []}'
200 assert response.body == '{"members": []}'
201 fixture.destroy_user_group(TEST_USER_GROUP)
201 fixture.destroy_user_group(TEST_USER_GROUP)
202
202
203 def test_usergroup_escape(self):
203 def test_usergroup_escape(self, user_util):
204 user = User.get_by_username('test_admin')
204 user = user_util.create_user(
205 user.name = '<img src="/image1" onload="alert(\'Hello, World!\');">'
205 username='escape_user',
206 user.lastname = (
206 firstname='<img src="/image2" onload="alert(\'Hello, World!\');">',
207 '<img src="/image2" onload="alert(\'Hello, World!\');">')
207 lastname='<img src="/image2" onload="alert(\'Hello, World!\');">'
208 Session().add(user)
208 )
209 Session().commit()
209
210 user_util.create_user_group(owner=user.username)
210
211
211 self.log_user()
212 self.log_user()
212 users_group_name = 'samplegroup'
213 users_group_name = 'samplegroup'
1 NO CONTENT: file was removed
NO CONTENT: file was removed
This diff has been collapsed as it changes many lines, (1018 lines changed) Show them Hide them
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now