Show More
@@ -687,6 +687,8 b' class PullrequestsController(BaseRepoCon' | |||||
687 | c.pull_request, c.rhodecode_user) and not c.pull_request.is_closed() |
|
687 | c.pull_request, c.rhodecode_user) and not c.pull_request.is_closed() | |
688 | c.shadow_clone_url = PullRequestModel().get_shadow_clone_url( |
|
688 | c.shadow_clone_url = PullRequestModel().get_shadow_clone_url( | |
689 | c.pull_request) |
|
689 | c.pull_request) | |
|
690 | c.allowed_to_delete = PullRequestModel().check_user_delete( | |||
|
691 | c.pull_request, c.rhodecode_user) and not c.pull_request.is_closed() | |||
690 |
|
692 | |||
691 | cc_model = ChangesetCommentsModel() |
|
693 | cc_model = ChangesetCommentsModel() | |
692 |
|
694 |
@@ -149,6 +149,11 b' class PullRequestModel(BaseModel):' | |||||
149 | owner = user.user_id == pull_request.user_id |
|
149 | owner = user.user_id == pull_request.user_id | |
150 | return self.check_user_merge(pull_request, user, api) or owner |
|
150 | return self.check_user_merge(pull_request, user, api) or owner | |
151 |
|
151 | |||
|
152 | def check_user_delete(self, pull_request, user): | |||
|
153 | owner = user.user_id == pull_request.user_id | |||
|
154 | _perms = ('repository.admin') | |||
|
155 | return self._check_perms(_perms, pull_request, user) or owner | |||
|
156 | ||||
152 | def check_user_change_status(self, pull_request, user, api=False): |
|
157 | def check_user_change_status(self, pull_request, user, api=False): | |
153 | reviewer = user.user_id in [x.user_id for x in |
|
158 | reviewer = user.user_id in [x.user_id for x in | |
154 | pull_request.reviewers] |
|
159 | pull_request.reviewers] |
@@ -1343,6 +1343,11 b' table.integrations {' | |||||
1343 | .pr-details-title { |
|
1343 | .pr-details-title { | |
1344 | padding-bottom: 8px; |
|
1344 | padding-bottom: 8px; | |
1345 | border-bottom: @border-thickness solid @grey5; |
|
1345 | border-bottom: @border-thickness solid @grey5; | |
|
1346 | ||||
|
1347 | .action_button.disabled { | |||
|
1348 | color: @grey4; | |||
|
1349 | cursor: inherit; | |||
|
1350 | } | |||
1346 | .action_button { |
|
1351 | .action_button { | |
1347 | color: @rcblue; |
|
1352 | color: @rcblue; | |
1348 | } |
|
1353 | } |
@@ -47,8 +47,18 b'' | |||||
47 | <div class="pr-details-title"> |
|
47 | <div class="pr-details-title"> | |
48 | ${_('Pull request #%s') % c.pull_request.pull_request_id} ${_('From')} ${h.format_date(c.pull_request.created_on)} |
|
48 | ${_('Pull request #%s') % c.pull_request.pull_request_id} ${_('From')} ${h.format_date(c.pull_request.created_on)} | |
49 | %if c.allowed_to_update: |
|
49 | %if c.allowed_to_update: | |
50 | <span id="open_edit_pullrequest" class="block-right action_button">${_('Edit')}</span> |
|
50 | <div id="delete_pullrequest" class="pull-right action_button ${'' if c.allowed_to_delete else 'disabled' }" style="clear:inherit;padding: 0"> | |
51 | <span id="close_edit_pullrequest" class="block-right action_button" style="display: none;">${_('Close')}</span> |
|
51 | % if c.allowed_to_delete: | |
|
52 | ${h.secure_form(url('pullrequest_delete', repo_name=c.pull_request.target_repo.repo_name, pull_request_id=c.pull_request.pull_request_id),method='delete')} | |||
|
53 | ${h.submit('remove_%s' % c.pull_request.pull_request_id, _('Delete'), | |||
|
54 | class_="btn btn-link btn-danger",onclick="return confirm('"+_('Confirm to delete this pull request')+"');")} | |||
|
55 | ${h.end_form()} | |||
|
56 | % else: | |||
|
57 | ${_('Delete')} | |||
|
58 | % endif | |||
|
59 | </div> | |||
|
60 | <div id="open_edit_pullrequest" class="pull-right action_button">${_('Edit')}</div> | |||
|
61 | <div id="close_edit_pullrequest" class="pull-right action_button" style="display: none;padding: 0">${_('Cancel edit')}</div> | |||
52 | %endif |
|
62 | %endif | |
53 | </div> |
|
63 | </div> | |
54 |
|
64 | |||
@@ -457,6 +467,7 b'' | |||||
457 | var PRDetails = { |
|
467 | var PRDetails = { | |
458 | editButton: $('#open_edit_pullrequest'), |
|
468 | editButton: $('#open_edit_pullrequest'), | |
459 | closeButton: $('#close_edit_pullrequest'), |
|
469 | closeButton: $('#close_edit_pullrequest'), | |
|
470 | deleteButton: $('#delete_pullrequest'), | |||
460 | viewFields: $('#pr-desc, #pr-title'), |
|
471 | viewFields: $('#pr-desc, #pr-title'), | |
461 | editFields: $('#pr-desc-edit, #pr-title-edit, #pr-save'), |
|
472 | editFields: $('#pr-desc-edit, #pr-title-edit, #pr-save'), | |
462 |
|
473 | |||
@@ -469,11 +480,15 b'' | |||||
469 | edit: function(event) { |
|
480 | edit: function(event) { | |
470 | this.viewFields.hide(); |
|
481 | this.viewFields.hide(); | |
471 | this.editButton.hide(); |
|
482 | this.editButton.hide(); | |
|
483 | this.deleteButton.hide(); | |||
|
484 | this.closeButton.show(); | |||
472 | this.editFields.show(); |
|
485 | this.editFields.show(); | |
473 | codeMirrorInstance.refresh(); |
|
486 | codeMirrorInstance.refresh(); | |
474 | }, |
|
487 | }, | |
475 |
|
488 | |||
476 | view: function(event) { |
|
489 | view: function(event) { | |
|
490 | this.editButton.show(); | |||
|
491 | this.deleteButton.show(); | |||
477 | this.editFields.hide(); |
|
492 | this.editFields.hide(); | |
478 | this.closeButton.hide(); |
|
493 | this.closeButton.hide(); | |
479 | this.viewFields.show(); |
|
494 | this.viewFields.show(); | |
@@ -504,7 +519,7 b'' | |||||
504 | this.closeButton.hide(); |
|
519 | this.closeButton.hide(); | |
505 | this.addButton.hide(); |
|
520 | this.addButton.hide(); | |
506 | this.removeButtons.css('visibility', 'hidden'); |
|
521 | this.removeButtons.css('visibility', 'hidden'); | |
507 | } |
|
522 | }, | |
508 | }; |
|
523 | }; | |
509 |
|
524 | |||
510 | PRDetails.init(); |
|
525 | PRDetails.init(); | |
@@ -603,12 +618,12 b'' | |||||
603 | if(button.hasClass("comments-visible")) { |
|
618 | if(button.hasClass("comments-visible")) { | |
604 | $('#{0} .inline-comments'.format(boxid)).each(function(index){ |
|
619 | $('#{0} .inline-comments'.format(boxid)).each(function(index){ | |
605 | $(this).hide(); |
|
620 | $(this).hide(); | |
606 | }) |
|
621 | }); | |
607 | button.removeClass("comments-visible"); |
|
622 | button.removeClass("comments-visible"); | |
608 | } else { |
|
623 | } else { | |
609 | $('#{0} .inline-comments'.format(boxid)).each(function(index){ |
|
624 | $('#{0} .inline-comments'.format(boxid)).each(function(index){ | |
610 | $(this).show(); |
|
625 | $(this).show(); | |
611 | }) |
|
626 | }); | |
612 | button.addClass("comments-visible"); |
|
627 | button.addClass("comments-visible"); | |
613 | } |
|
628 | } | |
614 | }); |
|
629 | }); |
@@ -27,7 +27,7 b' from rhodecode.model.pull_request import' | |||||
27 | from rhodecode.tests import assert_session_flash |
|
27 | from rhodecode.tests import assert_session_flash | |
28 |
|
28 | |||
29 |
|
29 | |||
30 | def test_merge_pull_request_renders_failure_reason(user_regular): |
|
30 | def test_merge_pull_request_renders_failure_reason(app, user_regular): | |
31 | pull_request = mock.Mock() |
|
31 | pull_request = mock.Mock() | |
32 | controller = pullrequests.PullrequestsController() |
|
32 | controller = pullrequests.PullrequestsController() | |
33 | model_patcher = mock.patch.multiple( |
|
33 | model_patcher = mock.patch.multiple( |
@@ -30,6 +30,7 b' from rhodecode.model.db import (' | |||||
30 | from rhodecode.model.meta import Session |
|
30 | from rhodecode.model.meta import Session | |
31 | from rhodecode.model.pull_request import PullRequestModel |
|
31 | from rhodecode.model.pull_request import PullRequestModel | |
32 | from rhodecode.model.user import UserModel |
|
32 | from rhodecode.model.user import UserModel | |
|
33 | from rhodecode.model.repo import RepoModel | |||
33 | from rhodecode.tests import assert_session_flash, url, TEST_USER_ADMIN_LOGIN |
|
34 | from rhodecode.tests import assert_session_flash, url, TEST_USER_ADMIN_LOGIN | |
34 | from rhodecode.tests.utils import AssertResponse |
|
35 | from rhodecode.tests.utils import AssertResponse | |
35 |
|
36 | |||
@@ -966,6 +967,68 b' class TestPullrequestsController:' | |||||
966 | assertr.no_element_exists('div.pr-mergeinfo') |
|
967 | assertr.no_element_exists('div.pr-mergeinfo') | |
967 |
|
968 | |||
968 |
|
969 | |||
|
970 | @pytest.mark.usefixtures('app') | |||
|
971 | @pytest.mark.backends("git", "hg") | |||
|
972 | class TestPullrequestsControllerDelete(object): | |||
|
973 | def test_pull_request_delete_button_permissions_admin( | |||
|
974 | self, autologin_user, user_admin, pr_util): | |||
|
975 | pull_request = pr_util.create_pull_request( | |||
|
976 | author=user_admin.username, enable_notifications=False) | |||
|
977 | ||||
|
978 | response = self.app.get(url( | |||
|
979 | controller='pullrequests', action='show', | |||
|
980 | repo_name=pull_request.target_repo.scm_instance().name, | |||
|
981 | pull_request_id=str(pull_request.pull_request_id))) | |||
|
982 | ||||
|
983 | response.mustcontain('id="delete_pullrequest"') | |||
|
984 | response.mustcontain('Confirm to delete this pull request') | |||
|
985 | ||||
|
986 | def test_pull_request_delete_button_permissions_owner( | |||
|
987 | self, autologin_regular_user, user_regular, pr_util): | |||
|
988 | pull_request = pr_util.create_pull_request( | |||
|
989 | author=user_regular.username, enable_notifications=False) | |||
|
990 | ||||
|
991 | response = self.app.get(url( | |||
|
992 | controller='pullrequests', action='show', | |||
|
993 | repo_name=pull_request.target_repo.scm_instance().name, | |||
|
994 | pull_request_id=str(pull_request.pull_request_id))) | |||
|
995 | ||||
|
996 | response.mustcontain('id="delete_pullrequest"') | |||
|
997 | response.mustcontain('Confirm to delete this pull request') | |||
|
998 | ||||
|
999 | def test_pull_request_delete_button_permissions_forbidden( | |||
|
1000 | self, autologin_regular_user, user_regular, user_admin, pr_util): | |||
|
1001 | pull_request = pr_util.create_pull_request( | |||
|
1002 | author=user_admin.username, enable_notifications=False) | |||
|
1003 | ||||
|
1004 | response = self.app.get(url( | |||
|
1005 | controller='pullrequests', action='show', | |||
|
1006 | repo_name=pull_request.target_repo.scm_instance().name, | |||
|
1007 | pull_request_id=str(pull_request.pull_request_id))) | |||
|
1008 | response.mustcontain(no=['id="delete_pullrequest"']) | |||
|
1009 | response.mustcontain(no=['Confirm to delete this pull request']) | |||
|
1010 | ||||
|
1011 | def test_pull_request_delete_button_permissions_can_update_cannot_delete( | |||
|
1012 | self, autologin_regular_user, user_regular, user_admin, pr_util, | |||
|
1013 | user_util): | |||
|
1014 | ||||
|
1015 | pull_request = pr_util.create_pull_request( | |||
|
1016 | author=user_admin.username, enable_notifications=False) | |||
|
1017 | ||||
|
1018 | user_util.grant_user_permission_to_repo( | |||
|
1019 | pull_request.target_repo, user_regular, | |||
|
1020 | 'repository.write') | |||
|
1021 | ||||
|
1022 | response = self.app.get(url( | |||
|
1023 | controller='pullrequests', action='show', | |||
|
1024 | repo_name=pull_request.target_repo.scm_instance().name, | |||
|
1025 | pull_request_id=str(pull_request.pull_request_id))) | |||
|
1026 | ||||
|
1027 | response.mustcontain('id="open_edit_pullrequest"') | |||
|
1028 | response.mustcontain('id="delete_pullrequest"') | |||
|
1029 | response.mustcontain(no=['Confirm to delete this pull request']) | |||
|
1030 | ||||
|
1031 | ||||
969 | def assert_pull_request_status(pull_request, expected_status): |
|
1032 | def assert_pull_request_status(pull_request, expected_status): | |
970 | status = ChangesetStatusModel().calculated_review_status( |
|
1033 | status = ChangesetStatusModel().calculated_review_status( | |
971 | pull_request=pull_request) |
|
1034 | pull_request=pull_request) |
General Comments 0
You need to be logged in to leave comments.
Login now