##// END OF EJS Templates
compare: drop target_repo - it is always other_repo
Mads Kiilerich -
r3324:c9b85375 beta
parent child Browse files
Show More
@@ -1,179 +1,178
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.compare
3 rhodecode.controllers.compare
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 compare controller for pylons showing differences between two
6 compare controller for pylons showing differences between two
7 repos, branches, bookmarks or tips
7 repos, branches, bookmarks or tips
8
8
9 :created_on: May 6, 2012
9 :created_on: May 6, 2012
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software: you can redistribute it and/or modify
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
17 # (at your option) any later version.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 import logging
26 import logging
27 import traceback
27 import traceback
28
28
29 from webob.exc import HTTPNotFound
29 from webob.exc import HTTPNotFound
30 from pylons import request, response, session, tmpl_context as c, url
30 from pylons import request, response, session, tmpl_context as c, url
31 from pylons.controllers.util import abort, redirect
31 from pylons.controllers.util import abort, redirect
32 from pylons.i18n.translation import _
32 from pylons.i18n.translation import _
33
33
34 from rhodecode.lib.vcs.exceptions import EmptyRepositoryError, RepositoryError
34 from rhodecode.lib.vcs.exceptions import EmptyRepositoryError, RepositoryError
35 from rhodecode.lib import helpers as h
35 from rhodecode.lib import helpers as h
36 from rhodecode.lib.base import BaseRepoController, render
36 from rhodecode.lib.base import BaseRepoController, render
37 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
37 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
38 from rhodecode.lib import diffs
38 from rhodecode.lib import diffs
39
39
40 from rhodecode.model.db import Repository
40 from rhodecode.model.db import Repository
41 from rhodecode.model.pull_request import PullRequestModel
41 from rhodecode.model.pull_request import PullRequestModel
42 from webob.exc import HTTPBadRequest
42 from webob.exc import HTTPBadRequest
43 from rhodecode.lib.utils2 import str2bool
43 from rhodecode.lib.utils2 import str2bool
44 from rhodecode.lib.diffs import LimitedDiffContainer
44 from rhodecode.lib.diffs import LimitedDiffContainer
45 from rhodecode.lib.vcs.backends.base import EmptyChangeset
45 from rhodecode.lib.vcs.backends.base import EmptyChangeset
46
46
47 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
48
48
49
49
50 class CompareController(BaseRepoController):
50 class CompareController(BaseRepoController):
51
51
52 @LoginRequired()
52 @LoginRequired()
53 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
53 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
54 'repository.admin')
54 'repository.admin')
55 def __before__(self):
55 def __before__(self):
56 super(CompareController, self).__before__()
56 super(CompareController, self).__before__()
57
57
58 def __get_cs_or_redirect(self, rev, repo, redirect_after=True,
58 def __get_cs_or_redirect(self, rev, repo, redirect_after=True,
59 partial=False):
59 partial=False):
60 """
60 """
61 Safe way to get changeset if error occur it redirects to changeset with
61 Safe way to get changeset if error occur it redirects to changeset with
62 proper message. If partial is set then don't do redirect raise Exception
62 proper message. If partial is set then don't do redirect raise Exception
63 instead
63 instead
64
64
65 :param rev: revision to fetch
65 :param rev: revision to fetch
66 :param repo: repo instance
66 :param repo: repo instance
67 """
67 """
68
68
69 try:
69 try:
70 type_, rev = rev
70 type_, rev = rev
71 return repo.scm_instance.get_changeset(rev)
71 return repo.scm_instance.get_changeset(rev)
72 except EmptyRepositoryError, e:
72 except EmptyRepositoryError, e:
73 if not redirect_after:
73 if not redirect_after:
74 return None
74 return None
75 h.flash(h.literal(_('There are no changesets yet')),
75 h.flash(h.literal(_('There are no changesets yet')),
76 category='warning')
76 category='warning')
77 redirect(url('summary_home', repo_name=repo.repo_name))
77 redirect(url('summary_home', repo_name=repo.repo_name))
78
78
79 except RepositoryError, e:
79 except RepositoryError, e:
80 log.error(traceback.format_exc())
80 log.error(traceback.format_exc())
81 h.flash(str(e), category='warning')
81 h.flash(str(e), category='warning')
82 if not partial:
82 if not partial:
83 redirect(h.url('summary_home', repo_name=repo.repo_name))
83 redirect(h.url('summary_home', repo_name=repo.repo_name))
84 raise HTTPBadRequest()
84 raise HTTPBadRequest()
85
85
86 def index(self, org_ref_type, org_ref, other_ref_type, other_ref):
86 def index(self, org_ref_type, org_ref, other_ref_type, other_ref):
87
87
88 org_repo = c.rhodecode_db_repo.repo_name
88 org_repo = c.rhodecode_db_repo.repo_name
89 org_ref = (org_ref_type, org_ref)
89 org_ref = (org_ref_type, org_ref)
90 other_ref = (other_ref_type, other_ref)
90 other_ref = (other_ref_type, other_ref)
91 other_repo = request.GET.get('other_repo', org_repo)
91 other_repo = request.GET.get('other_repo', org_repo)
92 c.fulldiff = fulldiff = request.GET.get('fulldiff')
92 c.fulldiff = fulldiff = request.GET.get('fulldiff')
93 rev_start = request.GET.get('rev_start')
93 rev_start = request.GET.get('rev_start')
94 rev_end = request.GET.get('rev_end')
94 rev_end = request.GET.get('rev_end')
95
95
96 c.swap_url = h.url('compare_url', as_form=request.GET.get('as_form'),
96 c.swap_url = h.url('compare_url', as_form=request.GET.get('as_form'),
97 repo_name=other_repo,
97 repo_name=other_repo,
98 org_ref_type=other_ref[0], org_ref=other_ref[1],
98 org_ref_type=other_ref[0], org_ref=other_ref[1],
99 other_repo=org_repo,
99 other_repo=org_repo,
100 other_ref_type=org_ref[0], other_ref=org_ref[1])
100 other_ref_type=org_ref[0], other_ref=org_ref[1])
101
101
102 c.org_repo = org_repo = Repository.get_by_repo_name(org_repo)
102 c.org_repo = org_repo = Repository.get_by_repo_name(org_repo)
103 c.other_repo = other_repo = Repository.get_by_repo_name(other_repo)
103 c.other_repo = other_repo = Repository.get_by_repo_name(other_repo)
104
104
105 if c.org_repo is None:
105 if c.org_repo is None:
106 log.error('Could not find org repo %s' % org_repo)
106 log.error('Could not find org repo %s' % org_repo)
107 raise HTTPNotFound
107 raise HTTPNotFound
108 if c.other_repo is None:
108 if c.other_repo is None:
109 log.error('Could not find other repo %s' % other_repo)
109 log.error('Could not find other repo %s' % other_repo)
110 raise HTTPNotFound
110 raise HTTPNotFound
111
111
112 if c.org_repo != c.other_repo and h.is_git(c.rhodecode_repo):
112 if c.org_repo != c.other_repo and h.is_git(c.rhodecode_repo):
113 log.error('compare of two remote repos not available for GIT REPOS')
113 log.error('compare of two remote repos not available for GIT REPOS')
114 raise HTTPNotFound
114 raise HTTPNotFound
115
115
116 if c.org_repo.scm_instance.alias != c.other_repo.scm_instance.alias:
116 if c.org_repo.scm_instance.alias != c.other_repo.scm_instance.alias:
117 log.error('compare of two different kind of remote repos not available')
117 log.error('compare of two different kind of remote repos not available')
118 raise HTTPNotFound
118 raise HTTPNotFound
119
119
120 partial = request.environ.get('HTTP_X_PARTIAL_XHR')
120 partial = request.environ.get('HTTP_X_PARTIAL_XHR')
121 self.__get_cs_or_redirect(rev=org_ref, repo=org_repo, partial=partial)
121 self.__get_cs_or_redirect(rev=org_ref, repo=org_repo, partial=partial)
122 self.__get_cs_or_redirect(rev=other_ref, repo=other_repo, partial=partial)
122 self.__get_cs_or_redirect(rev=other_ref, repo=other_repo, partial=partial)
123
123
124 if rev_start and rev_end:
124 if rev_start and rev_end:
125 #replace our org_ref with given CS
125 #replace our org_ref with given CS
126 org_ref = ('rev', rev_start)
126 org_ref = ('rev', rev_start)
127 other_ref = ('rev', rev_end)
127 other_ref = ('rev', rev_end)
128
128
129 c.cs_ranges, ancestor = PullRequestModel().get_compare_data(
129 c.cs_ranges, ancestor = PullRequestModel().get_compare_data(
130 org_repo, org_ref, other_repo, other_ref)
130 org_repo, org_ref, other_repo, other_ref)
131
131
132 c.statuses = c.rhodecode_db_repo.statuses([x.raw_id for x in
132 c.statuses = c.rhodecode_db_repo.statuses([x.raw_id for x in
133 c.cs_ranges])
133 c.cs_ranges])
134 c.target_repo = c.other_repo.repo_name
135 # defines that we need hidden inputs with changesets
134 # defines that we need hidden inputs with changesets
136 c.as_form = request.GET.get('as_form', False)
135 c.as_form = request.GET.get('as_form', False)
137 if partial:
136 if partial:
138 return render('compare/compare_cs.html')
137 return render('compare/compare_cs.html')
139
138
140 c.org_ref = org_ref[1]
139 c.org_ref = org_ref[1]
141 c.other_ref = other_ref[1]
140 c.other_ref = other_ref[1]
142
141
143 if ancestor and c.org_repo != c.other_repo:
142 if ancestor and c.org_repo != c.other_repo:
144 # case we want a simple diff without incoming changesets,
143 # case we want a simple diff without incoming changesets,
145 # previewing what will be merged.
144 # previewing what will be merged.
146 # Make the diff on the forked repo, with
145 # Make the diff on the forked repo, with
147 # revision that is common ancestor
146 # revision that is common ancestor
148 _org_ref = org_ref
147 _org_ref = org_ref
149 log.debug('Using ancestor %s as org_ref instead of %s', ancestor, _org_ref)
148 log.debug('Using ancestor %s as org_ref instead of %s', ancestor, _org_ref)
150 org_ref = ('rev', ancestor)
149 org_ref = ('rev', ancestor)
151 org_repo = other_repo
150 org_repo = other_repo
152
151
153 diff_limit = self.cut_off_limit if not fulldiff else None
152 diff_limit = self.cut_off_limit if not fulldiff else None
154
153
155 _diff = diffs.differ(org_repo, org_ref, other_repo, other_ref)
154 _diff = diffs.differ(org_repo, org_ref, other_repo, other_ref)
156
155
157 diff_processor = diffs.DiffProcessor(_diff or '', format='gitdiff',
156 diff_processor = diffs.DiffProcessor(_diff or '', format='gitdiff',
158 diff_limit=diff_limit)
157 diff_limit=diff_limit)
159 _parsed = diff_processor.prepare()
158 _parsed = diff_processor.prepare()
160
159
161 c.limited_diff = False
160 c.limited_diff = False
162 if isinstance(_parsed, LimitedDiffContainer):
161 if isinstance(_parsed, LimitedDiffContainer):
163 c.limited_diff = True
162 c.limited_diff = True
164
163
165 c.files = []
164 c.files = []
166 c.changes = {}
165 c.changes = {}
167 c.lines_added = 0
166 c.lines_added = 0
168 c.lines_deleted = 0
167 c.lines_deleted = 0
169 for f in _parsed:
168 for f in _parsed:
170 st = f['stats']
169 st = f['stats']
171 if st[0] != 'b':
170 if st[0] != 'b':
172 c.lines_added += st[0]
171 c.lines_added += st[0]
173 c.lines_deleted += st[1]
172 c.lines_deleted += st[1]
174 fid = h.FID('', f['filename'])
173 fid = h.FID('', f['filename'])
175 c.files.append([fid, f['operation'], f['filename'], f['stats']])
174 c.files.append([fid, f['operation'], f['filename'], f['stats']])
176 diff = diff_processor.as_html(enable_comments=False, parsed_lines=[f])
175 diff = diff_processor.as_html(enable_comments=False, parsed_lines=[f])
177 c.changes[fid] = [f['operation'], f['filename'], diff]
176 c.changes[fid] = [f['operation'], f['filename'], diff]
178
177
179 return render('compare/compare_diff.html')
178 return render('compare/compare_diff.html')
@@ -1,486 +1,485
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.pullrequests
3 rhodecode.controllers.pullrequests
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 pull requests controller for rhodecode for initializing pull requests
6 pull requests controller for rhodecode for initializing pull requests
7
7
8 :created_on: May 7, 2012
8 :created_on: May 7, 2012
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 import logging
25 import logging
26 import traceback
26 import traceback
27 import formencode
27 import formencode
28
28
29 from webob.exc import HTTPNotFound, HTTPForbidden
29 from webob.exc import HTTPNotFound, HTTPForbidden
30 from collections import defaultdict
30 from collections import defaultdict
31 from itertools import groupby
31 from itertools import groupby
32
32
33 from pylons import request, response, session, tmpl_context as c, url
33 from pylons import request, response, session, tmpl_context as c, url
34 from pylons.controllers.util import abort, redirect
34 from pylons.controllers.util import abort, redirect
35 from pylons.i18n.translation import _
35 from pylons.i18n.translation import _
36
36
37 from rhodecode.lib.compat import json
37 from rhodecode.lib.compat import json
38 from rhodecode.lib.base import BaseRepoController, render
38 from rhodecode.lib.base import BaseRepoController, render
39 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator,\
39 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator,\
40 NotAnonymous
40 NotAnonymous
41 from rhodecode.lib import helpers as h
41 from rhodecode.lib import helpers as h
42 from rhodecode.lib import diffs
42 from rhodecode.lib import diffs
43 from rhodecode.lib.utils import action_logger, jsonify
43 from rhodecode.lib.utils import action_logger, jsonify
44 from rhodecode.lib.vcs.exceptions import EmptyRepositoryError
44 from rhodecode.lib.vcs.exceptions import EmptyRepositoryError
45 from rhodecode.lib.vcs.backends.base import EmptyChangeset
45 from rhodecode.lib.vcs.backends.base import EmptyChangeset
46 from rhodecode.lib.diffs import LimitedDiffContainer
46 from rhodecode.lib.diffs import LimitedDiffContainer
47 from rhodecode.model.db import User, PullRequest, ChangesetStatus,\
47 from rhodecode.model.db import User, PullRequest, ChangesetStatus,\
48 ChangesetComment
48 ChangesetComment
49 from rhodecode.model.pull_request import PullRequestModel
49 from rhodecode.model.pull_request import PullRequestModel
50 from rhodecode.model.meta import Session
50 from rhodecode.model.meta import Session
51 from rhodecode.model.repo import RepoModel
51 from rhodecode.model.repo import RepoModel
52 from rhodecode.model.comment import ChangesetCommentsModel
52 from rhodecode.model.comment import ChangesetCommentsModel
53 from rhodecode.model.changeset_status import ChangesetStatusModel
53 from rhodecode.model.changeset_status import ChangesetStatusModel
54 from rhodecode.model.forms import PullRequestForm
54 from rhodecode.model.forms import PullRequestForm
55
55
56 log = logging.getLogger(__name__)
56 log = logging.getLogger(__name__)
57
57
58
58
59 class PullrequestsController(BaseRepoController):
59 class PullrequestsController(BaseRepoController):
60
60
61 @LoginRequired()
61 @LoginRequired()
62 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
62 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
63 'repository.admin')
63 'repository.admin')
64 def __before__(self):
64 def __before__(self):
65 super(PullrequestsController, self).__before__()
65 super(PullrequestsController, self).__before__()
66 repo_model = RepoModel()
66 repo_model = RepoModel()
67 c.users_array = repo_model.get_users_js()
67 c.users_array = repo_model.get_users_js()
68 c.users_groups_array = repo_model.get_users_groups_js()
68 c.users_groups_array = repo_model.get_users_groups_js()
69
69
70 def _get_repo_refs(self, repo):
70 def _get_repo_refs(self, repo):
71 hist_l = []
71 hist_l = []
72
72
73 branches_group = ([('branch:%s:%s' % (k, v), k) for
73 branches_group = ([('branch:%s:%s' % (k, v), k) for
74 k, v in repo.branches.iteritems()], _("Branches"))
74 k, v in repo.branches.iteritems()], _("Branches"))
75 bookmarks_group = ([('book:%s:%s' % (k, v), k) for
75 bookmarks_group = ([('book:%s:%s' % (k, v), k) for
76 k, v in repo.bookmarks.iteritems()], _("Bookmarks"))
76 k, v in repo.bookmarks.iteritems()], _("Bookmarks"))
77 tags_group = ([('tag:%s:%s' % (k, v), k) for
77 tags_group = ([('tag:%s:%s' % (k, v), k) for
78 k, v in repo.tags.iteritems()], _("Tags"))
78 k, v in repo.tags.iteritems()], _("Tags"))
79
79
80 hist_l.append(bookmarks_group)
80 hist_l.append(bookmarks_group)
81 hist_l.append(branches_group)
81 hist_l.append(branches_group)
82 hist_l.append(tags_group)
82 hist_l.append(tags_group)
83
83
84 return hist_l
84 return hist_l
85
85
86 def _get_default_rev(self, repo):
86 def _get_default_rev(self, repo):
87 """
87 """
88 Get's default revision to do compare on pull request
88 Get's default revision to do compare on pull request
89
89
90 :param repo:
90 :param repo:
91 """
91 """
92 repo = repo.scm_instance
92 repo = repo.scm_instance
93 if 'default' in repo.branches:
93 if 'default' in repo.branches:
94 return 'default'
94 return 'default'
95 else:
95 else:
96 #if repo doesn't have default branch return first found
96 #if repo doesn't have default branch return first found
97 return repo.branches.keys()[0]
97 return repo.branches.keys()[0]
98
98
99 def _get_is_allowed_change_status(self, pull_request):
99 def _get_is_allowed_change_status(self, pull_request):
100 owner = self.rhodecode_user.user_id == pull_request.user_id
100 owner = self.rhodecode_user.user_id == pull_request.user_id
101 reviewer = self.rhodecode_user.user_id in [x.user_id for x in
101 reviewer = self.rhodecode_user.user_id in [x.user_id for x in
102 pull_request.reviewers]
102 pull_request.reviewers]
103 return (self.rhodecode_user.admin or owner or reviewer)
103 return (self.rhodecode_user.admin or owner or reviewer)
104
104
105 def show_all(self, repo_name):
105 def show_all(self, repo_name):
106 c.pull_requests = PullRequestModel().get_all(repo_name)
106 c.pull_requests = PullRequestModel().get_all(repo_name)
107 c.repo_name = repo_name
107 c.repo_name = repo_name
108 return render('/pullrequests/pullrequest_show_all.html')
108 return render('/pullrequests/pullrequest_show_all.html')
109
109
110 @NotAnonymous()
110 @NotAnonymous()
111 def index(self):
111 def index(self):
112 org_repo = c.rhodecode_db_repo
112 org_repo = c.rhodecode_db_repo
113
113
114 if org_repo.scm_instance.alias != 'hg':
114 if org_repo.scm_instance.alias != 'hg':
115 log.error('Review not available for GIT REPOS')
115 log.error('Review not available for GIT REPOS')
116 raise HTTPNotFound
116 raise HTTPNotFound
117
117
118 try:
118 try:
119 org_repo.scm_instance.get_changeset()
119 org_repo.scm_instance.get_changeset()
120 except EmptyRepositoryError, e:
120 except EmptyRepositoryError, e:
121 h.flash(h.literal(_('There are no changesets yet')),
121 h.flash(h.literal(_('There are no changesets yet')),
122 category='warning')
122 category='warning')
123 redirect(url('summary_home', repo_name=org_repo.repo_name))
123 redirect(url('summary_home', repo_name=org_repo.repo_name))
124
124
125 other_repos_info = {}
125 other_repos_info = {}
126
126
127 c.org_refs = self._get_repo_refs(c.rhodecode_repo)
127 c.org_refs = self._get_repo_refs(c.rhodecode_repo)
128 c.org_repos = []
128 c.org_repos = []
129 c.other_repos = []
129 c.other_repos = []
130 c.org_repos.append((org_repo.repo_name, '%s/%s' % (
130 c.org_repos.append((org_repo.repo_name, '%s/%s' % (
131 org_repo.user.username, c.repo_name))
131 org_repo.user.username, c.repo_name))
132 )
132 )
133
133
134 # add org repo to other so we can open pull request agains itself
134 # add org repo to other so we can open pull request agains itself
135 c.other_repos.extend(c.org_repos)
135 c.other_repos.extend(c.org_repos)
136
136
137 c.default_pull_request = org_repo.repo_name # repo name pre-selected
137 c.default_pull_request = org_repo.repo_name # repo name pre-selected
138 c.default_pull_request_rev = self._get_default_rev(org_repo) # revision pre-selected
138 c.default_pull_request_rev = self._get_default_rev(org_repo) # revision pre-selected
139 c.default_revs = self._get_repo_refs(org_repo.scm_instance)
139 c.default_revs = self._get_repo_refs(org_repo.scm_instance)
140 #add orginal repo
140 #add orginal repo
141 other_repos_info[org_repo.repo_name] = {
141 other_repos_info[org_repo.repo_name] = {
142 'gravatar': h.gravatar_url(org_repo.user.email, 24),
142 'gravatar': h.gravatar_url(org_repo.user.email, 24),
143 'description': org_repo.description,
143 'description': org_repo.description,
144 'revs': h.select('other_ref', '', c.default_revs, class_='refs')
144 'revs': h.select('other_ref', '', c.default_revs, class_='refs')
145 }
145 }
146
146
147 #gather forks and add to this list
147 #gather forks and add to this list
148 for fork in org_repo.forks:
148 for fork in org_repo.forks:
149 c.other_repos.append((fork.repo_name, '%s/%s' % (
149 c.other_repos.append((fork.repo_name, '%s/%s' % (
150 fork.user.username, fork.repo_name))
150 fork.user.username, fork.repo_name))
151 )
151 )
152 other_repos_info[fork.repo_name] = {
152 other_repos_info[fork.repo_name] = {
153 'gravatar': h.gravatar_url(fork.user.email, 24),
153 'gravatar': h.gravatar_url(fork.user.email, 24),
154 'description': fork.description,
154 'description': fork.description,
155 'revs': h.select('other_ref', '',
155 'revs': h.select('other_ref', '',
156 self._get_repo_refs(fork.scm_instance),
156 self._get_repo_refs(fork.scm_instance),
157 class_='refs')
157 class_='refs')
158 }
158 }
159 #add parents of this fork also, but only if it's not empty
159 #add parents of this fork also, but only if it's not empty
160 if org_repo.parent and org_repo.parent.scm_instance.revisions:
160 if org_repo.parent and org_repo.parent.scm_instance.revisions:
161 c.default_pull_request = org_repo.parent.repo_name
161 c.default_pull_request = org_repo.parent.repo_name
162 c.default_pull_request_rev = self._get_default_rev(org_repo.parent)
162 c.default_pull_request_rev = self._get_default_rev(org_repo.parent)
163 c.default_revs = self._get_repo_refs(org_repo.parent.scm_instance)
163 c.default_revs = self._get_repo_refs(org_repo.parent.scm_instance)
164 c.other_repos.append((org_repo.parent.repo_name, '%s/%s' % (
164 c.other_repos.append((org_repo.parent.repo_name, '%s/%s' % (
165 org_repo.parent.user.username,
165 org_repo.parent.user.username,
166 org_repo.parent.repo_name))
166 org_repo.parent.repo_name))
167 )
167 )
168 other_repos_info[org_repo.parent.repo_name] = {
168 other_repos_info[org_repo.parent.repo_name] = {
169 'gravatar': h.gravatar_url(org_repo.parent.user.email, 24),
169 'gravatar': h.gravatar_url(org_repo.parent.user.email, 24),
170 'description': org_repo.parent.description,
170 'description': org_repo.parent.description,
171 'revs': h.select('other_ref', '',
171 'revs': h.select('other_ref', '',
172 self._get_repo_refs(org_repo.parent.scm_instance),
172 self._get_repo_refs(org_repo.parent.scm_instance),
173 class_='refs')
173 class_='refs')
174 }
174 }
175
175
176 c.other_repos_info = json.dumps(other_repos_info)
176 c.other_repos_info = json.dumps(other_repos_info)
177 c.review_members = [org_repo.user]
177 c.review_members = [org_repo.user]
178 return render('/pullrequests/pullrequest.html')
178 return render('/pullrequests/pullrequest.html')
179
179
180 @NotAnonymous()
180 @NotAnonymous()
181 def create(self, repo_name):
181 def create(self, repo_name):
182 repo = RepoModel()._get_repo(repo_name)
182 repo = RepoModel()._get_repo(repo_name)
183 try:
183 try:
184 _form = PullRequestForm(repo.repo_id)().to_python(request.POST)
184 _form = PullRequestForm(repo.repo_id)().to_python(request.POST)
185 except formencode.Invalid, errors:
185 except formencode.Invalid, errors:
186 log.error(traceback.format_exc())
186 log.error(traceback.format_exc())
187 if errors.error_dict.get('revisions'):
187 if errors.error_dict.get('revisions'):
188 msg = 'Revisions: %s' % errors.error_dict['revisions']
188 msg = 'Revisions: %s' % errors.error_dict['revisions']
189 elif errors.error_dict.get('pullrequest_title'):
189 elif errors.error_dict.get('pullrequest_title'):
190 msg = _('Pull request requires a title with min. 3 chars')
190 msg = _('Pull request requires a title with min. 3 chars')
191 else:
191 else:
192 msg = _('error during creation of pull request')
192 msg = _('error during creation of pull request')
193
193
194 h.flash(msg, 'error')
194 h.flash(msg, 'error')
195 return redirect(url('pullrequest_home', repo_name=repo_name))
195 return redirect(url('pullrequest_home', repo_name=repo_name))
196
196
197 org_repo = _form['org_repo']
197 org_repo = _form['org_repo']
198 org_ref = _form['org_ref']
198 org_ref = _form['org_ref']
199 other_repo = _form['other_repo']
199 other_repo = _form['other_repo']
200 other_ref = _form['other_ref']
200 other_ref = _form['other_ref']
201 revisions = _form['revisions']
201 revisions = _form['revisions']
202 reviewers = _form['review_members']
202 reviewers = _form['review_members']
203
203
204 # if we have cherry picked pull request we don't care what is in
204 # if we have cherry picked pull request we don't care what is in
205 # org_ref/other_ref
205 # org_ref/other_ref
206 rev_start = request.POST.get('rev_start')
206 rev_start = request.POST.get('rev_start')
207 rev_end = request.POST.get('rev_end')
207 rev_end = request.POST.get('rev_end')
208
208
209 if rev_start and rev_end:
209 if rev_start and rev_end:
210 # this is swapped to simulate that rev_end is a revision from
210 # this is swapped to simulate that rev_end is a revision from
211 # parent of the fork
211 # parent of the fork
212 org_ref = 'rev:%s:%s' % (rev_end, rev_end)
212 org_ref = 'rev:%s:%s' % (rev_end, rev_end)
213 other_ref = 'rev:%s:%s' % (rev_start, rev_start)
213 other_ref = 'rev:%s:%s' % (rev_start, rev_start)
214
214
215 title = _form['pullrequest_title']
215 title = _form['pullrequest_title']
216 description = _form['pullrequest_desc']
216 description = _form['pullrequest_desc']
217
217
218 try:
218 try:
219 pull_request = PullRequestModel().create(
219 pull_request = PullRequestModel().create(
220 self.rhodecode_user.user_id, org_repo, org_ref, other_repo,
220 self.rhodecode_user.user_id, org_repo, org_ref, other_repo,
221 other_ref, revisions, reviewers, title, description
221 other_ref, revisions, reviewers, title, description
222 )
222 )
223 Session().commit()
223 Session().commit()
224 h.flash(_('Successfully opened new pull request'),
224 h.flash(_('Successfully opened new pull request'),
225 category='success')
225 category='success')
226 except Exception:
226 except Exception:
227 h.flash(_('Error occurred during sending pull request'),
227 h.flash(_('Error occurred during sending pull request'),
228 category='error')
228 category='error')
229 log.error(traceback.format_exc())
229 log.error(traceback.format_exc())
230 return redirect(url('pullrequest_home', repo_name=repo_name))
230 return redirect(url('pullrequest_home', repo_name=repo_name))
231
231
232 return redirect(url('pullrequest_show', repo_name=other_repo,
232 return redirect(url('pullrequest_show', repo_name=other_repo,
233 pull_request_id=pull_request.pull_request_id))
233 pull_request_id=pull_request.pull_request_id))
234
234
235 @NotAnonymous()
235 @NotAnonymous()
236 @jsonify
236 @jsonify
237 def update(self, repo_name, pull_request_id):
237 def update(self, repo_name, pull_request_id):
238 pull_request = PullRequest.get_or_404(pull_request_id)
238 pull_request = PullRequest.get_or_404(pull_request_id)
239 if pull_request.is_closed():
239 if pull_request.is_closed():
240 raise HTTPForbidden()
240 raise HTTPForbidden()
241 #only owner or admin can update it
241 #only owner or admin can update it
242 owner = pull_request.author.user_id == c.rhodecode_user.user_id
242 owner = pull_request.author.user_id == c.rhodecode_user.user_id
243 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
243 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
244 reviewers_ids = map(int, filter(lambda v: v not in [None, ''],
244 reviewers_ids = map(int, filter(lambda v: v not in [None, ''],
245 request.POST.get('reviewers_ids', '').split(',')))
245 request.POST.get('reviewers_ids', '').split(',')))
246
246
247 PullRequestModel().update_reviewers(pull_request_id, reviewers_ids)
247 PullRequestModel().update_reviewers(pull_request_id, reviewers_ids)
248 Session().commit()
248 Session().commit()
249 return True
249 return True
250 raise HTTPForbidden()
250 raise HTTPForbidden()
251
251
252 @NotAnonymous()
252 @NotAnonymous()
253 @jsonify
253 @jsonify
254 def delete(self, repo_name, pull_request_id):
254 def delete(self, repo_name, pull_request_id):
255 pull_request = PullRequest.get_or_404(pull_request_id)
255 pull_request = PullRequest.get_or_404(pull_request_id)
256 #only owner can delete it !
256 #only owner can delete it !
257 if pull_request.author.user_id == c.rhodecode_user.user_id:
257 if pull_request.author.user_id == c.rhodecode_user.user_id:
258 PullRequestModel().delete(pull_request)
258 PullRequestModel().delete(pull_request)
259 Session().commit()
259 Session().commit()
260 h.flash(_('Successfully deleted pull request'),
260 h.flash(_('Successfully deleted pull request'),
261 category='success')
261 category='success')
262 return redirect(url('admin_settings_my_account', anchor='pullrequests'))
262 return redirect(url('admin_settings_my_account', anchor='pullrequests'))
263 raise HTTPForbidden()
263 raise HTTPForbidden()
264
264
265 def _load_compare_data(self, pull_request, enable_comments=True):
265 def _load_compare_data(self, pull_request, enable_comments=True):
266 """
266 """
267 Load context data needed for generating compare diff
267 Load context data needed for generating compare diff
268
268
269 :param pull_request:
269 :param pull_request:
270 :type pull_request:
270 :type pull_request:
271 """
271 """
272 rev_start = request.GET.get('rev_start')
272 rev_start = request.GET.get('rev_start')
273 rev_end = request.GET.get('rev_end')
273 rev_end = request.GET.get('rev_end')
274
274
275 org_repo = pull_request.org_repo
275 org_repo = pull_request.org_repo
276 (org_ref_type,
276 (org_ref_type,
277 org_ref_name,
277 org_ref_name,
278 org_ref_rev) = pull_request.org_ref.split(':')
278 org_ref_rev) = pull_request.org_ref.split(':')
279
279
280 other_repo = org_repo
280 other_repo = org_repo
281 (other_ref_type,
281 (other_ref_type,
282 other_ref_name,
282 other_ref_name,
283 other_ref_rev) = pull_request.other_ref.split(':')
283 other_ref_rev) = pull_request.other_ref.split(':')
284
284
285 # despite opening revisions for bookmarks/branches/tags, we always
285 # despite opening revisions for bookmarks/branches/tags, we always
286 # convert this to rev to prevent changes after book or branch change
286 # convert this to rev to prevent changes after book or branch change
287 org_ref = ('rev', org_ref_rev)
287 org_ref = ('rev', org_ref_rev)
288 other_ref = ('rev', other_ref_rev)
288 other_ref = ('rev', other_ref_rev)
289
289
290 c.org_repo = org_repo
290 c.org_repo = org_repo
291 c.other_repo = other_repo
291 c.other_repo = other_repo
292
292
293 c.fulldiff = fulldiff = request.GET.get('fulldiff')
293 c.fulldiff = fulldiff = request.GET.get('fulldiff')
294
294
295 c.cs_ranges = [org_repo.get_changeset(x) for x in pull_request.revisions]
295 c.cs_ranges = [org_repo.get_changeset(x) for x in pull_request.revisions]
296
296
297 other_ref = ('rev', getattr(c.cs_ranges[0].parents[0]
297 other_ref = ('rev', getattr(c.cs_ranges[0].parents[0]
298 if c.cs_ranges[0].parents
298 if c.cs_ranges[0].parents
299 else EmptyChangeset(), 'raw_id'))
299 else EmptyChangeset(), 'raw_id'))
300
300
301 c.statuses = org_repo.statuses([x.raw_id for x in c.cs_ranges])
301 c.statuses = org_repo.statuses([x.raw_id for x in c.cs_ranges])
302 c.target_repo = other_repo.repo_name
303 # defines that we need hidden inputs with changesets
302 # defines that we need hidden inputs with changesets
304 c.as_form = request.GET.get('as_form', False)
303 c.as_form = request.GET.get('as_form', False)
305
304
306 c.org_ref = org_ref[1]
305 c.org_ref = org_ref[1]
307 c.other_ref = other_ref[1]
306 c.other_ref = other_ref[1]
308
307
309 diff_limit = self.cut_off_limit if not fulldiff else None
308 diff_limit = self.cut_off_limit if not fulldiff else None
310
309
311 #we swap org/other ref since we run a simple diff on one repo
310 #we swap org/other ref since we run a simple diff on one repo
312 _diff = diffs.differ(org_repo, other_ref, other_repo, org_ref)
311 _diff = diffs.differ(org_repo, other_ref, other_repo, org_ref)
313
312
314 diff_processor = diffs.DiffProcessor(_diff or '', format='gitdiff',
313 diff_processor = diffs.DiffProcessor(_diff or '', format='gitdiff',
315 diff_limit=diff_limit)
314 diff_limit=diff_limit)
316 _parsed = diff_processor.prepare()
315 _parsed = diff_processor.prepare()
317
316
318 c.limited_diff = False
317 c.limited_diff = False
319 if isinstance(_parsed, LimitedDiffContainer):
318 if isinstance(_parsed, LimitedDiffContainer):
320 c.limited_diff = True
319 c.limited_diff = True
321
320
322 c.files = []
321 c.files = []
323 c.changes = {}
322 c.changes = {}
324 c.lines_added = 0
323 c.lines_added = 0
325 c.lines_deleted = 0
324 c.lines_deleted = 0
326 for f in _parsed:
325 for f in _parsed:
327 st = f['stats']
326 st = f['stats']
328 if st[0] != 'b':
327 if st[0] != 'b':
329 c.lines_added += st[0]
328 c.lines_added += st[0]
330 c.lines_deleted += st[1]
329 c.lines_deleted += st[1]
331 fid = h.FID('', f['filename'])
330 fid = h.FID('', f['filename'])
332 c.files.append([fid, f['operation'], f['filename'], f['stats']])
331 c.files.append([fid, f['operation'], f['filename'], f['stats']])
333 diff = diff_processor.as_html(enable_comments=enable_comments,
332 diff = diff_processor.as_html(enable_comments=enable_comments,
334 parsed_lines=[f])
333 parsed_lines=[f])
335 c.changes[fid] = [f['operation'], f['filename'], diff]
334 c.changes[fid] = [f['operation'], f['filename'], diff]
336
335
337 def show(self, repo_name, pull_request_id):
336 def show(self, repo_name, pull_request_id):
338 repo_model = RepoModel()
337 repo_model = RepoModel()
339 c.users_array = repo_model.get_users_js()
338 c.users_array = repo_model.get_users_js()
340 c.users_groups_array = repo_model.get_users_groups_js()
339 c.users_groups_array = repo_model.get_users_groups_js()
341 c.pull_request = PullRequest.get_or_404(pull_request_id)
340 c.pull_request = PullRequest.get_or_404(pull_request_id)
342 c.allowed_to_change_status = self._get_is_allowed_change_status(c.pull_request)
341 c.allowed_to_change_status = self._get_is_allowed_change_status(c.pull_request)
343 cc_model = ChangesetCommentsModel()
342 cc_model = ChangesetCommentsModel()
344 cs_model = ChangesetStatusModel()
343 cs_model = ChangesetStatusModel()
345 _cs_statuses = cs_model.get_statuses(c.pull_request.org_repo,
344 _cs_statuses = cs_model.get_statuses(c.pull_request.org_repo,
346 pull_request=c.pull_request,
345 pull_request=c.pull_request,
347 with_revisions=True)
346 with_revisions=True)
348
347
349 cs_statuses = defaultdict(list)
348 cs_statuses = defaultdict(list)
350 for st in _cs_statuses:
349 for st in _cs_statuses:
351 cs_statuses[st.author.username] += [st]
350 cs_statuses[st.author.username] += [st]
352
351
353 c.pull_request_reviewers = []
352 c.pull_request_reviewers = []
354 c.pull_request_pending_reviewers = []
353 c.pull_request_pending_reviewers = []
355 for o in c.pull_request.reviewers:
354 for o in c.pull_request.reviewers:
356 st = cs_statuses.get(o.user.username, None)
355 st = cs_statuses.get(o.user.username, None)
357 if st:
356 if st:
358 sorter = lambda k: k.version
357 sorter = lambda k: k.version
359 st = [(x, list(y)[0])
358 st = [(x, list(y)[0])
360 for x, y in (groupby(sorted(st, key=sorter), sorter))]
359 for x, y in (groupby(sorted(st, key=sorter), sorter))]
361 else:
360 else:
362 c.pull_request_pending_reviewers.append(o.user)
361 c.pull_request_pending_reviewers.append(o.user)
363 c.pull_request_reviewers.append([o.user, st])
362 c.pull_request_reviewers.append([o.user, st])
364
363
365 # pull_requests repo_name we opened it against
364 # pull_requests repo_name we opened it against
366 # ie. other_repo must match
365 # ie. other_repo must match
367 if repo_name != c.pull_request.other_repo.repo_name:
366 if repo_name != c.pull_request.other_repo.repo_name:
368 raise HTTPNotFound
367 raise HTTPNotFound
369
368
370 # load compare data into template context
369 # load compare data into template context
371 enable_comments = not c.pull_request.is_closed()
370 enable_comments = not c.pull_request.is_closed()
372 self._load_compare_data(c.pull_request, enable_comments=enable_comments)
371 self._load_compare_data(c.pull_request, enable_comments=enable_comments)
373
372
374 # inline comments
373 # inline comments
375 c.inline_cnt = 0
374 c.inline_cnt = 0
376 c.inline_comments = cc_model.get_inline_comments(
375 c.inline_comments = cc_model.get_inline_comments(
377 c.rhodecode_db_repo.repo_id,
376 c.rhodecode_db_repo.repo_id,
378 pull_request=pull_request_id)
377 pull_request=pull_request_id)
379 # count inline comments
378 # count inline comments
380 for __, lines in c.inline_comments:
379 for __, lines in c.inline_comments:
381 for comments in lines.values():
380 for comments in lines.values():
382 c.inline_cnt += len(comments)
381 c.inline_cnt += len(comments)
383 # comments
382 # comments
384 c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id,
383 c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id,
385 pull_request=pull_request_id)
384 pull_request=pull_request_id)
386
385
387 try:
386 try:
388 cur_status = c.statuses[c.pull_request.revisions[0]][0]
387 cur_status = c.statuses[c.pull_request.revisions[0]][0]
389 except:
388 except:
390 log.error(traceback.format_exc())
389 log.error(traceback.format_exc())
391 cur_status = 'undefined'
390 cur_status = 'undefined'
392 if c.pull_request.is_closed() and 0:
391 if c.pull_request.is_closed() and 0:
393 c.current_changeset_status = cur_status
392 c.current_changeset_status = cur_status
394 else:
393 else:
395 # changeset(pull-request) status calulation based on reviewers
394 # changeset(pull-request) status calulation based on reviewers
396 c.current_changeset_status = cs_model.calculate_status(
395 c.current_changeset_status = cs_model.calculate_status(
397 c.pull_request_reviewers,
396 c.pull_request_reviewers,
398 )
397 )
399 c.changeset_statuses = ChangesetStatus.STATUSES
398 c.changeset_statuses = ChangesetStatus.STATUSES
400
399
401 return render('/pullrequests/pullrequest_show.html')
400 return render('/pullrequests/pullrequest_show.html')
402
401
403 @NotAnonymous()
402 @NotAnonymous()
404 @jsonify
403 @jsonify
405 def comment(self, repo_name, pull_request_id):
404 def comment(self, repo_name, pull_request_id):
406 pull_request = PullRequest.get_or_404(pull_request_id)
405 pull_request = PullRequest.get_or_404(pull_request_id)
407 if pull_request.is_closed():
406 if pull_request.is_closed():
408 raise HTTPForbidden()
407 raise HTTPForbidden()
409
408
410 status = request.POST.get('changeset_status')
409 status = request.POST.get('changeset_status')
411 change_status = request.POST.get('change_changeset_status')
410 change_status = request.POST.get('change_changeset_status')
412 text = request.POST.get('text')
411 text = request.POST.get('text')
413
412
414 allowed_to_change_status = self._get_is_allowed_change_status(pull_request)
413 allowed_to_change_status = self._get_is_allowed_change_status(pull_request)
415 if status and change_status and allowed_to_change_status:
414 if status and change_status and allowed_to_change_status:
416 text = text or (_('Status change -> %s')
415 text = text or (_('Status change -> %s')
417 % ChangesetStatus.get_status_lbl(status))
416 % ChangesetStatus.get_status_lbl(status))
418 comm = ChangesetCommentsModel().create(
417 comm = ChangesetCommentsModel().create(
419 text=text,
418 text=text,
420 repo=c.rhodecode_db_repo.repo_id,
419 repo=c.rhodecode_db_repo.repo_id,
421 user=c.rhodecode_user.user_id,
420 user=c.rhodecode_user.user_id,
422 pull_request=pull_request_id,
421 pull_request=pull_request_id,
423 f_path=request.POST.get('f_path'),
422 f_path=request.POST.get('f_path'),
424 line_no=request.POST.get('line'),
423 line_no=request.POST.get('line'),
425 status_change=(ChangesetStatus.get_status_lbl(status)
424 status_change=(ChangesetStatus.get_status_lbl(status)
426 if status and change_status and allowed_to_change_status else None)
425 if status and change_status and allowed_to_change_status else None)
427 )
426 )
428
427
429 action_logger(self.rhodecode_user,
428 action_logger(self.rhodecode_user,
430 'user_commented_pull_request:%s' % pull_request_id,
429 'user_commented_pull_request:%s' % pull_request_id,
431 c.rhodecode_db_repo, self.ip_addr, self.sa)
430 c.rhodecode_db_repo, self.ip_addr, self.sa)
432
431
433 if allowed_to_change_status:
432 if allowed_to_change_status:
434 # get status if set !
433 # get status if set !
435 if status and change_status:
434 if status and change_status:
436 ChangesetStatusModel().set_status(
435 ChangesetStatusModel().set_status(
437 c.rhodecode_db_repo.repo_id,
436 c.rhodecode_db_repo.repo_id,
438 status,
437 status,
439 c.rhodecode_user.user_id,
438 c.rhodecode_user.user_id,
440 comm,
439 comm,
441 pull_request=pull_request_id
440 pull_request=pull_request_id
442 )
441 )
443
442
444 if request.POST.get('save_close'):
443 if request.POST.get('save_close'):
445 if status in ['rejected', 'approved']:
444 if status in ['rejected', 'approved']:
446 PullRequestModel().close_pull_request(pull_request_id)
445 PullRequestModel().close_pull_request(pull_request_id)
447 action_logger(self.rhodecode_user,
446 action_logger(self.rhodecode_user,
448 'user_closed_pull_request:%s' % pull_request_id,
447 'user_closed_pull_request:%s' % pull_request_id,
449 c.rhodecode_db_repo, self.ip_addr, self.sa)
448 c.rhodecode_db_repo, self.ip_addr, self.sa)
450 else:
449 else:
451 h.flash(_('Closing pull request on other statuses than '
450 h.flash(_('Closing pull request on other statuses than '
452 'rejected or approved forbidden'),
451 'rejected or approved forbidden'),
453 category='warning')
452 category='warning')
454
453
455 Session().commit()
454 Session().commit()
456
455
457 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
456 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
458 return redirect(h.url('pullrequest_show', repo_name=repo_name,
457 return redirect(h.url('pullrequest_show', repo_name=repo_name,
459 pull_request_id=pull_request_id))
458 pull_request_id=pull_request_id))
460
459
461 data = {
460 data = {
462 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
461 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
463 }
462 }
464 if comm:
463 if comm:
465 c.co = comm
464 c.co = comm
466 data.update(comm.get_dict())
465 data.update(comm.get_dict())
467 data.update({'rendered_text':
466 data.update({'rendered_text':
468 render('changeset/changeset_comment_block.html')})
467 render('changeset/changeset_comment_block.html')})
469
468
470 return data
469 return data
471
470
472 @NotAnonymous()
471 @NotAnonymous()
473 @jsonify
472 @jsonify
474 def delete_comment(self, repo_name, comment_id):
473 def delete_comment(self, repo_name, comment_id):
475 co = ChangesetComment.get(comment_id)
474 co = ChangesetComment.get(comment_id)
476 if co.pull_request.is_closed():
475 if co.pull_request.is_closed():
477 #don't allow deleting comments on closed pull request
476 #don't allow deleting comments on closed pull request
478 raise HTTPForbidden()
477 raise HTTPForbidden()
479
478
480 owner = co.author.user_id == c.rhodecode_user.user_id
479 owner = co.author.user_id == c.rhodecode_user.user_id
481 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
480 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
482 ChangesetCommentsModel().delete(comment=co)
481 ChangesetCommentsModel().delete(comment=co)
483 Session().commit()
482 Session().commit()
484 return True
483 return True
485 else:
484 else:
486 raise HTTPForbidden()
485 raise HTTPForbidden()
@@ -1,28 +1,28
1 ## Changesets table !
1 ## Changesets table !
2 <div class="container">
2 <div class="container">
3 <table class="compare_view_commits noborder">
3 <table class="compare_view_commits noborder">
4 %if not c.cs_ranges:
4 %if not c.cs_ranges:
5 <span class="empty_data">${_('No changesets')}</span>
5 <span class="empty_data">${_('No changesets')}</span>
6 %else:
6 %else:
7 %for cnt, cs in enumerate(c.cs_ranges):
7 %for cnt, cs in enumerate(c.cs_ranges):
8 <tr>
8 <tr>
9 <td><div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(h.email_or_none(cs.author),14)}"/></div></td>
9 <td><div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(h.email_or_none(cs.author),14)}"/></div></td>
10 <td>
10 <td>
11 %if cs.raw_id in c.statuses:
11 %if cs.raw_id in c.statuses:
12 <div title="${c.statuses[cs.raw_id][1]}" class="changeset-status-ico"><img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses[cs.raw_id][0])}" /></div>
12 <div title="${c.statuses[cs.raw_id][1]}" class="changeset-status-ico"><img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses[cs.raw_id][0])}" /></div>
13 %endif
13 %endif
14 </td>
14 </td>
15 <td>${h.link_to('r%s:%s' % (cs.revision,h.short_id(cs.raw_id)),h.url('changeset_home',repo_name=c.target_repo,revision=cs.raw_id))}
15 <td>${h.link_to('r%s:%s' % (cs.revision,h.short_id(cs.raw_id)),h.url('changeset_home',repo_name=c.other_repo.repo_name,revision=cs.raw_id))}
16 %if c.as_form:
16 %if c.as_form:
17 ${h.hidden('revisions',cs.raw_id)}
17 ${h.hidden('revisions',cs.raw_id)}
18 %endif
18 %endif
19 </td>
19 </td>
20 <td><div class="author">${h.person(cs.author)}</div></td>
20 <td><div class="author">${h.person(cs.author)}</div></td>
21 <td><span class="tooltip" title="${h.tooltip(h.age(cs.date))}">${cs.date}</span></td>
21 <td><span class="tooltip" title="${h.tooltip(h.age(cs.date))}">${cs.date}</span></td>
22 <td><div class="message tooltip" title="${h.tooltip(cs.message)}" style="white-space:normal">${h.urlify_commit(h.shorter(cs.message, 60),c.repo_name)}</div></td>
22 <td><div class="message tooltip" title="${h.tooltip(cs.message)}" style="white-space:normal">${h.urlify_commit(h.shorter(cs.message, 60),c.repo_name)}</div></td>
23 </tr>
23 </tr>
24 %endfor
24 %endfor
25
25
26 %endif
26 %endif
27 </table>
27 </table>
28 </div>
28 </div>
General Comments 0
You need to be logged in to leave comments. Login now