##// END OF EJS Templates
pull-requests: moved the listing of pull requests for repo into pyramid....
marcink -
r1766:d8048d6f default
parent child Browse files
Show More
@@ -0,0 +1,184 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import logging
22
23 from pyramid.view import view_config
24
25 from rhodecode.apps._base import RepoAppView, DataGridAppView
26 from rhodecode.lib import helpers as h
27 from rhodecode.lib import audit_logger
28 from rhodecode.lib.auth import (
29 LoginRequired, HasRepoPermissionAnyDecorator)
30 from rhodecode.lib.utils import PartialRenderer
31 from rhodecode.lib.utils2 import str2bool
32 from rhodecode.model.comment import CommentsModel
33 from rhodecode.model.db import PullRequest
34 from rhodecode.model.pull_request import PullRequestModel
35
36 log = logging.getLogger(__name__)
37
38
39 class RepoPullRequestsView(RepoAppView, DataGridAppView):
40
41 def load_default_context(self):
42 c = self._get_local_tmpl_context()
43
44 # TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead
45 c.repo_info = self.db_repo
46
47 self._register_global_c(c)
48 return c
49
50 def _get_pull_requests_list(
51 self, repo_name, source, filter_type, opened_by, statuses):
52
53 draw, start, limit = self._extract_chunk(self.request)
54 search_q, order_by, order_dir = self._extract_ordering(self.request)
55 _render = PartialRenderer('data_table/_dt_elements.mako')
56
57 # pagination
58
59 if filter_type == 'awaiting_review':
60 pull_requests = PullRequestModel().get_awaiting_review(
61 repo_name, source=source, opened_by=opened_by,
62 statuses=statuses, offset=start, length=limit,
63 order_by=order_by, order_dir=order_dir)
64 pull_requests_total_count = PullRequestModel().count_awaiting_review(
65 repo_name, source=source, statuses=statuses,
66 opened_by=opened_by)
67 elif filter_type == 'awaiting_my_review':
68 pull_requests = PullRequestModel().get_awaiting_my_review(
69 repo_name, source=source, opened_by=opened_by,
70 user_id=self._rhodecode_user.user_id, statuses=statuses,
71 offset=start, length=limit, order_by=order_by,
72 order_dir=order_dir)
73 pull_requests_total_count = PullRequestModel().count_awaiting_my_review(
74 repo_name, source=source, user_id=self._rhodecode_user.user_id,
75 statuses=statuses, opened_by=opened_by)
76 else:
77 pull_requests = PullRequestModel().get_all(
78 repo_name, source=source, opened_by=opened_by,
79 statuses=statuses, offset=start, length=limit,
80 order_by=order_by, order_dir=order_dir)
81 pull_requests_total_count = PullRequestModel().count_all(
82 repo_name, source=source, statuses=statuses,
83 opened_by=opened_by)
84
85 data = []
86 comments_model = CommentsModel()
87 for pr in pull_requests:
88 comments = comments_model.get_all_comments(
89 self.db_repo.repo_id, pull_request=pr)
90
91 data.append({
92 'name': _render('pullrequest_name',
93 pr.pull_request_id, pr.target_repo.repo_name),
94 'name_raw': pr.pull_request_id,
95 'status': _render('pullrequest_status',
96 pr.calculated_review_status()),
97 'title': _render(
98 'pullrequest_title', pr.title, pr.description),
99 'description': h.escape(pr.description),
100 'updated_on': _render('pullrequest_updated_on',
101 h.datetime_to_time(pr.updated_on)),
102 'updated_on_raw': h.datetime_to_time(pr.updated_on),
103 'created_on': _render('pullrequest_updated_on',
104 h.datetime_to_time(pr.created_on)),
105 'created_on_raw': h.datetime_to_time(pr.created_on),
106 'author': _render('pullrequest_author',
107 pr.author.full_contact, ),
108 'author_raw': pr.author.full_name,
109 'comments': _render('pullrequest_comments', len(comments)),
110 'comments_raw': len(comments),
111 'closed': pr.is_closed(),
112 })
113
114 data = ({
115 'draw': draw,
116 'data': data,
117 'recordsTotal': pull_requests_total_count,
118 'recordsFiltered': pull_requests_total_count,
119 })
120 return data
121
122 @LoginRequired()
123 @HasRepoPermissionAnyDecorator(
124 'repository.read', 'repository.write', 'repository.admin')
125 @view_config(
126 route_name='pullrequest_show_all', request_method='GET',
127 renderer='rhodecode:templates/pullrequests/pullrequests.mako')
128 def pull_request_list(self):
129 c = self.load_default_context()
130
131 req_get = self.request.GET
132 c.source = str2bool(req_get.get('source'))
133 c.closed = str2bool(req_get.get('closed'))
134 c.my = str2bool(req_get.get('my'))
135 c.awaiting_review = str2bool(req_get.get('awaiting_review'))
136 c.awaiting_my_review = str2bool(req_get.get('awaiting_my_review'))
137
138 c.active = 'open'
139 if c.my:
140 c.active = 'my'
141 if c.closed:
142 c.active = 'closed'
143 if c.awaiting_review and not c.source:
144 c.active = 'awaiting'
145 if c.source and not c.awaiting_review:
146 c.active = 'source'
147 if c.awaiting_my_review:
148 c.active = 'awaiting_my'
149
150 return self._get_template_context(c)
151
152 @LoginRequired()
153 @HasRepoPermissionAnyDecorator(
154 'repository.read', 'repository.write', 'repository.admin')
155 @view_config(
156 route_name='pullrequest_show_all_data', request_method='GET',
157 renderer='json_ext', xhr=True)
158 def pull_request_list_data(self):
159
160 # additional filters
161 req_get = self.request.GET
162 source = str2bool(req_get.get('source'))
163 closed = str2bool(req_get.get('closed'))
164 my = str2bool(req_get.get('my'))
165 awaiting_review = str2bool(req_get.get('awaiting_review'))
166 awaiting_my_review = str2bool(req_get.get('awaiting_my_review'))
167
168 filter_type = 'awaiting_review' if awaiting_review \
169 else 'awaiting_my_review' if awaiting_my_review \
170 else None
171
172 opened_by = None
173 if my:
174 opened_by = [self._rhodecode_user.user_id]
175
176 statuses = [PullRequest.STATUS_NEW, PullRequest.STATUS_OPEN]
177 if closed:
178 statuses = [PullRequest.STATUS_CLOSED]
179
180 data = self._get_pull_requests_list(
181 repo_name=self.db_repo_name, source=source,
182 filter_type=filter_type, opened_by=opened_by, statuses=statuses)
183
184 return data
@@ -1,273 +1,301 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
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
20
21 import time
21 import time
22 import logging
22 import logging
23 from pylons import tmpl_context as c
23 from pylons import tmpl_context as c
24 from pyramid.httpexceptions import HTTPFound
24 from pyramid.httpexceptions import HTTPFound
25
25
26 from rhodecode.lib import helpers as h
26 from rhodecode.lib import helpers as h
27 from rhodecode.lib.utils import PartialRenderer
27 from rhodecode.lib.utils import PartialRenderer
28 from rhodecode.lib.utils2 import StrictAttributeDict, safe_int, datetime_to_time
28 from rhodecode.lib.utils2 import StrictAttributeDict, safe_int, datetime_to_time
29 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
29 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
30 from rhodecode.lib.ext_json import json
30 from rhodecode.lib.ext_json import json
31 from rhodecode.model import repo
31 from rhodecode.model import repo
32 from rhodecode.model.db import User
32 from rhodecode.model.db import User
33 from rhodecode.model.scm import ScmModel
33 from rhodecode.model.scm import ScmModel
34
34
35 log = logging.getLogger(__name__)
35 log = logging.getLogger(__name__)
36
36
37
37
38 ADMIN_PREFIX = '/_admin'
38 ADMIN_PREFIX = '/_admin'
39 STATIC_FILE_PREFIX = '/_static'
39 STATIC_FILE_PREFIX = '/_static'
40
40
41
41
42 def get_format_ref_id(repo):
42 def get_format_ref_id(repo):
43 """Returns a `repo` specific reference formatter function"""
43 """Returns a `repo` specific reference formatter function"""
44 if h.is_svn(repo):
44 if h.is_svn(repo):
45 return _format_ref_id_svn
45 return _format_ref_id_svn
46 else:
46 else:
47 return _format_ref_id
47 return _format_ref_id
48
48
49
49
50 def _format_ref_id(name, raw_id):
50 def _format_ref_id(name, raw_id):
51 """Default formatting of a given reference `name`"""
51 """Default formatting of a given reference `name`"""
52 return name
52 return name
53
53
54
54
55 def _format_ref_id_svn(name, raw_id):
55 def _format_ref_id_svn(name, raw_id):
56 """Special way of formatting a reference for Subversion including path"""
56 """Special way of formatting a reference for Subversion including path"""
57 return '%s@%s' % (name, raw_id)
57 return '%s@%s' % (name, raw_id)
58
58
59
59
60 class TemplateArgs(StrictAttributeDict):
60 class TemplateArgs(StrictAttributeDict):
61 pass
61 pass
62
62
63
63
64 class BaseAppView(object):
64 class BaseAppView(object):
65
65
66 def __init__(self, context, request):
66 def __init__(self, context, request):
67 self.request = request
67 self.request = request
68 self.context = context
68 self.context = context
69 self.session = request.session
69 self.session = request.session
70 self._rhodecode_user = request.user # auth user
70 self._rhodecode_user = request.user # auth user
71 self._rhodecode_db_user = self._rhodecode_user.get_instance()
71 self._rhodecode_db_user = self._rhodecode_user.get_instance()
72 self._maybe_needs_password_change(
72 self._maybe_needs_password_change(
73 request.matched_route.name, self._rhodecode_db_user)
73 request.matched_route.name, self._rhodecode_db_user)
74
74
75 def _maybe_needs_password_change(self, view_name, user_obj):
75 def _maybe_needs_password_change(self, view_name, user_obj):
76 log.debug('Checking if user %s needs password change on view %s',
76 log.debug('Checking if user %s needs password change on view %s',
77 user_obj, view_name)
77 user_obj, view_name)
78 skip_user_views = [
78 skip_user_views = [
79 'logout', 'login',
79 'logout', 'login',
80 'my_account_password', 'my_account_password_update'
80 'my_account_password', 'my_account_password_update'
81 ]
81 ]
82
82
83 if not user_obj:
83 if not user_obj:
84 return
84 return
85
85
86 if user_obj.username == User.DEFAULT_USER:
86 if user_obj.username == User.DEFAULT_USER:
87 return
87 return
88
88
89 now = time.time()
89 now = time.time()
90 should_change = user_obj.user_data.get('force_password_change')
90 should_change = user_obj.user_data.get('force_password_change')
91 change_after = safe_int(should_change) or 0
91 change_after = safe_int(should_change) or 0
92 if should_change and now > change_after:
92 if should_change and now > change_after:
93 log.debug('User %s requires password change', user_obj)
93 log.debug('User %s requires password change', user_obj)
94 h.flash('You are required to change your password', 'warning',
94 h.flash('You are required to change your password', 'warning',
95 ignore_duplicate=True)
95 ignore_duplicate=True)
96
96
97 if view_name not in skip_user_views:
97 if view_name not in skip_user_views:
98 raise HTTPFound(
98 raise HTTPFound(
99 self.request.route_path('my_account_password'))
99 self.request.route_path('my_account_password'))
100
100
101 def _get_local_tmpl_context(self):
101 def _get_local_tmpl_context(self):
102 c = TemplateArgs()
102 c = TemplateArgs()
103 c.auth_user = self.request.user
103 c.auth_user = self.request.user
104 return c
104 return c
105
105
106 def _register_global_c(self, tmpl_args):
106 def _register_global_c(self, tmpl_args):
107 """
107 """
108 Registers attributes to pylons global `c`
108 Registers attributes to pylons global `c`
109 """
109 """
110 # TODO(marcink): remove once pyramid migration is finished
110 # TODO(marcink): remove once pyramid migration is finished
111 for k, v in tmpl_args.items():
111 for k, v in tmpl_args.items():
112 setattr(c, k, v)
112 setattr(c, k, v)
113
113
114 def _get_template_context(self, tmpl_args):
114 def _get_template_context(self, tmpl_args):
115 self._register_global_c(tmpl_args)
115 self._register_global_c(tmpl_args)
116
116
117 local_tmpl_args = {
117 local_tmpl_args = {
118 'defaults': {},
118 'defaults': {},
119 'errors': {},
119 'errors': {},
120 }
120 }
121 local_tmpl_args.update(tmpl_args)
121 local_tmpl_args.update(tmpl_args)
122 return local_tmpl_args
122 return local_tmpl_args
123
123
124 def load_default_context(self):
124 def load_default_context(self):
125 """
125 """
126 example:
126 example:
127
127
128 def load_default_context(self):
128 def load_default_context(self):
129 c = self._get_local_tmpl_context()
129 c = self._get_local_tmpl_context()
130 c.custom_var = 'foobar'
130 c.custom_var = 'foobar'
131 self._register_global_c(c)
131 self._register_global_c(c)
132 return c
132 return c
133 """
133 """
134 raise NotImplementedError('Needs implementation in view class')
134 raise NotImplementedError('Needs implementation in view class')
135
135
136
136
137 class RepoAppView(BaseAppView):
137 class RepoAppView(BaseAppView):
138
138
139 def __init__(self, context, request):
139 def __init__(self, context, request):
140 super(RepoAppView, self).__init__(context, request)
140 super(RepoAppView, self).__init__(context, request)
141 self.db_repo = request.db_repo
141 self.db_repo = request.db_repo
142 self.db_repo_name = self.db_repo.repo_name
142 self.db_repo_name = self.db_repo.repo_name
143 self.db_repo_pull_requests = ScmModel().get_pull_requests(self.db_repo)
143 self.db_repo_pull_requests = ScmModel().get_pull_requests(self.db_repo)
144
144
145 def _handle_missing_requirements(self, error):
145 def _handle_missing_requirements(self, error):
146 log.error(
146 log.error(
147 'Requirements are missing for repository %s: %s',
147 'Requirements are missing for repository %s: %s',
148 self.db_repo_name, error.message)
148 self.db_repo_name, error.message)
149
149
150 def _get_local_tmpl_context(self):
150 def _get_local_tmpl_context(self):
151 c = super(RepoAppView, self)._get_local_tmpl_context()
151 c = super(RepoAppView, self)._get_local_tmpl_context()
152 # register common vars for this type of view
152 # register common vars for this type of view
153 c.rhodecode_db_repo = self.db_repo
153 c.rhodecode_db_repo = self.db_repo
154 c.repo_name = self.db_repo_name
154 c.repo_name = self.db_repo_name
155 c.repository_pull_requests = self.db_repo_pull_requests
155 c.repository_pull_requests = self.db_repo_pull_requests
156
156
157 c.repository_requirements_missing = False
157 c.repository_requirements_missing = False
158 try:
158 try:
159 self.rhodecode_vcs_repo = self.db_repo.scm_instance()
159 self.rhodecode_vcs_repo = self.db_repo.scm_instance()
160 except RepositoryRequirementError as e:
160 except RepositoryRequirementError as e:
161 c.repository_requirements_missing = True
161 c.repository_requirements_missing = True
162 self._handle_missing_requirements(e)
162 self._handle_missing_requirements(e)
163
163
164 return c
164 return c
165
165
166
166
167 class DataGridAppView(object):
167 class DataGridAppView(object):
168 """
168 """
169 Common class to have re-usable grid rendering components
169 Common class to have re-usable grid rendering components
170 """
170 """
171
171
172 def _extract_ordering(self, request, column_map=None):
172 def _extract_ordering(self, request, column_map=None):
173 column_map = column_map or {}
173 column_map = column_map or {}
174 column_index = safe_int(request.GET.get('order[0][column]'))
174 column_index = safe_int(request.GET.get('order[0][column]'))
175 order_dir = request.GET.get(
175 order_dir = request.GET.get(
176 'order[0][dir]', 'desc')
176 'order[0][dir]', 'desc')
177 order_by = request.GET.get(
177 order_by = request.GET.get(
178 'columns[%s][data][sort]' % column_index, 'name_raw')
178 'columns[%s][data][sort]' % column_index, 'name_raw')
179
179
180 # translate datatable to DB columns
180 # translate datatable to DB columns
181 order_by = column_map.get(order_by) or order_by
181 order_by = column_map.get(order_by) or order_by
182
182
183 search_q = request.GET.get('search[value]')
183 search_q = request.GET.get('search[value]')
184 return search_q, order_by, order_dir
184 return search_q, order_by, order_dir
185
185
186 def _extract_chunk(self, request):
186 def _extract_chunk(self, request):
187 start = safe_int(request.GET.get('start'), 0)
187 start = safe_int(request.GET.get('start'), 0)
188 length = safe_int(request.GET.get('length'), 25)
188 length = safe_int(request.GET.get('length'), 25)
189 draw = safe_int(request.GET.get('draw'))
189 draw = safe_int(request.GET.get('draw'))
190 return draw, start, length
190 return draw, start, length
191
191
192
192
193 class BaseReferencesView(RepoAppView):
193 class BaseReferencesView(RepoAppView):
194 """
194 """
195 Base for reference view for branches, tags and bookmarks.
195 Base for reference view for branches, tags and bookmarks.
196 """
196 """
197 def load_default_context(self):
197 def load_default_context(self):
198 c = self._get_local_tmpl_context()
198 c = self._get_local_tmpl_context()
199
199
200 # TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead
200 # TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead
201 c.repo_info = self.db_repo
201 c.repo_info = self.db_repo
202
202
203 self._register_global_c(c)
203 self._register_global_c(c)
204 return c
204 return c
205
205
206 def load_refs_context(self, ref_items, partials_template):
206 def load_refs_context(self, ref_items, partials_template):
207 _render = PartialRenderer(partials_template)
207 _render = PartialRenderer(partials_template)
208 _data = []
208 _data = []
209 pre_load = ["author", "date", "message"]
209 pre_load = ["author", "date", "message"]
210
210
211 is_svn = h.is_svn(self.rhodecode_vcs_repo)
211 is_svn = h.is_svn(self.rhodecode_vcs_repo)
212 format_ref_id = get_format_ref_id(self.rhodecode_vcs_repo)
212 format_ref_id = get_format_ref_id(self.rhodecode_vcs_repo)
213
213
214 for ref_name, commit_id in ref_items:
214 for ref_name, commit_id in ref_items:
215 commit = self.rhodecode_vcs_repo.get_commit(
215 commit = self.rhodecode_vcs_repo.get_commit(
216 commit_id=commit_id, pre_load=pre_load)
216 commit_id=commit_id, pre_load=pre_load)
217
217
218 # TODO: johbo: Unify generation of reference links
218 # TODO: johbo: Unify generation of reference links
219 use_commit_id = '/' in ref_name or is_svn
219 use_commit_id = '/' in ref_name or is_svn
220 files_url = h.url(
220 files_url = h.url(
221 'files_home',
221 'files_home',
222 repo_name=c.repo_name,
222 repo_name=c.repo_name,
223 f_path=ref_name if is_svn else '',
223 f_path=ref_name if is_svn else '',
224 revision=commit_id if use_commit_id else ref_name,
224 revision=commit_id if use_commit_id else ref_name,
225 at=ref_name)
225 at=ref_name)
226
226
227 _data.append({
227 _data.append({
228 "name": _render('name', ref_name, files_url),
228 "name": _render('name', ref_name, files_url),
229 "name_raw": ref_name,
229 "name_raw": ref_name,
230 "date": _render('date', commit.date),
230 "date": _render('date', commit.date),
231 "date_raw": datetime_to_time(commit.date),
231 "date_raw": datetime_to_time(commit.date),
232 "author": _render('author', commit.author),
232 "author": _render('author', commit.author),
233 "commit": _render(
233 "commit": _render(
234 'commit', commit.message, commit.raw_id, commit.idx),
234 'commit', commit.message, commit.raw_id, commit.idx),
235 "commit_raw": commit.idx,
235 "commit_raw": commit.idx,
236 "compare": _render(
236 "compare": _render(
237 'compare', format_ref_id(ref_name, commit.raw_id)),
237 'compare', format_ref_id(ref_name, commit.raw_id)),
238 })
238 })
239 c.has_references = bool(_data)
239 c.has_references = bool(_data)
240 c.data = json.dumps(_data)
240 c.data = json.dumps(_data)
241
241
242
242
243 class RepoRoutePredicate(object):
243 class RepoRoutePredicate(object):
244 def __init__(self, val, config):
244 def __init__(self, val, config):
245 self.val = val
245 self.val = val
246
246
247 def text(self):
247 def text(self):
248 return 'repo_route = %s' % self.val
248 return 'repo_route = %s' % self.val
249
249
250 phash = text
250 phash = text
251
251
252 def __call__(self, info, request):
252 def __call__(self, info, request):
253 repo_name = info['match']['repo_name']
253 repo_name = info['match']['repo_name']
254 repo_model = repo.RepoModel()
254 repo_model = repo.RepoModel()
255 by_name_match = repo_model.get_by_repo_name(repo_name, cache=True)
255 by_name_match = repo_model.get_by_repo_name(repo_name, cache=True)
256 # if we match quickly from database, short circuit the operation,
256 # if we match quickly from database, short circuit the operation,
257 # and validate repo based on the type.
257 # and validate repo based on the type.
258 if by_name_match:
258 if by_name_match:
259 # register this as request object we can re-use later
259 # register this as request object we can re-use later
260 request.db_repo = by_name_match
260 request.db_repo = by_name_match
261 return True
261 return True
262
262
263 by_id_match = repo_model.get_repo_by_id(repo_name)
263 by_id_match = repo_model.get_repo_by_id(repo_name)
264 if by_id_match:
264 if by_id_match:
265 request.db_repo = by_id_match
265 request.db_repo = by_id_match
266 return True
266 return True
267
267
268 return False
268 return False
269
269
270
270
271 class RepoTypeRoutePredicate(object):
272 def __init__(self, val, config):
273 self.val = val or ['hg', 'git', 'svn']
274
275 def text(self):
276 return 'repo_accepted_type = %s' % self.val
277
278 phash = text
279
280 def __call__(self, info, request):
281
282 rhodecode_db_repo = request.db_repo
283
284 log.debug(
285 '%s checking repo type for %s in %s',
286 self.__class__.__name__, rhodecode_db_repo.repo_type, self.val)
287
288 if rhodecode_db_repo.repo_type in self.val:
289 return True
290 else:
291 log.warning('Current view is not supported for repo type:%s',
292 rhodecode_db_repo.repo_type)
293 return False
294
295
296
271 def includeme(config):
297 def includeme(config):
272 config.add_route_predicate(
298 config.add_route_predicate(
273 'repo_route', RepoRoutePredicate)
299 'repo_route', RepoRoutePredicate)
300 config.add_route_predicate(
301 'repo_accepted_types', RepoTypeRoutePredicate) No newline at end of file
@@ -1,115 +1,125 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
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
20
21
21
22 def includeme(config):
22 def includeme(config):
23
23
24 # Summary
24 # Summary
25 config.add_route(
25 config.add_route(
26 name='repo_summary_explicit',
26 name='repo_summary_explicit',
27 pattern='/{repo_name:.*?[^/]}/summary', repo_route=True)
27 pattern='/{repo_name:.*?[^/]}/summary', repo_route=True)
28
28
29 # Tags
29 # Tags
30 config.add_route(
30 config.add_route(
31 name='tags_home',
31 name='tags_home',
32 pattern='/{repo_name:.*?[^/]}/tags', repo_route=True)
32 pattern='/{repo_name:.*?[^/]}/tags', repo_route=True)
33
33
34 # Branches
34 # Branches
35 config.add_route(
35 config.add_route(
36 name='branches_home',
36 name='branches_home',
37 pattern='/{repo_name:.*?[^/]}/branches', repo_route=True)
37 pattern='/{repo_name:.*?[^/]}/branches', repo_route=True)
38
38
39 # Bookmarks
39 # Bookmarks
40 config.add_route(
40 config.add_route(
41 name='bookmarks_home',
41 name='bookmarks_home',
42 pattern='/{repo_name:.*?[^/]}/bookmarks', repo_route=True)
42 pattern='/{repo_name:.*?[^/]}/bookmarks', repo_route=True)
43
43
44 # Pull Requests
44 # Pull Requests
45 config.add_route(
45 config.add_route(
46 name='pullrequest_show',
46 name='pullrequest_show',
47 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id}',
47 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id}',
48 repo_route=True)
48 repo_route=True)
49
49
50 config.add_route(
51 name='pullrequest_show_all',
52 pattern='/{repo_name:.*?[^/]}/pull-request',
53 repo_route=True, repo_accepted_types=['hg', 'git'])
54
55 config.add_route(
56 name='pullrequest_show_all_data',
57 pattern='/{repo_name:.*?[^/]}/pull-request-data',
58 repo_route=True, repo_accepted_types=['hg', 'git'])
59
50 # Settings
60 # Settings
51 config.add_route(
61 config.add_route(
52 name='edit_repo',
62 name='edit_repo',
53 pattern='/{repo_name:.*?[^/]}/settings', repo_route=True)
63 pattern='/{repo_name:.*?[^/]}/settings', repo_route=True)
54
64
55 # Settings advanced
65 # Settings advanced
56 config.add_route(
66 config.add_route(
57 name='edit_repo_advanced',
67 name='edit_repo_advanced',
58 pattern='/{repo_name:.*?[^/]}/settings/advanced', repo_route=True)
68 pattern='/{repo_name:.*?[^/]}/settings/advanced', repo_route=True)
59 config.add_route(
69 config.add_route(
60 name='edit_repo_advanced_delete',
70 name='edit_repo_advanced_delete',
61 pattern='/{repo_name:.*?[^/]}/settings/advanced/delete', repo_route=True)
71 pattern='/{repo_name:.*?[^/]}/settings/advanced/delete', repo_route=True)
62 config.add_route(
72 config.add_route(
63 name='edit_repo_advanced_locking',
73 name='edit_repo_advanced_locking',
64 pattern='/{repo_name:.*?[^/]}/settings/advanced/locking', repo_route=True)
74 pattern='/{repo_name:.*?[^/]}/settings/advanced/locking', repo_route=True)
65 config.add_route(
75 config.add_route(
66 name='edit_repo_advanced_journal',
76 name='edit_repo_advanced_journal',
67 pattern='/{repo_name:.*?[^/]}/settings/advanced/journal', repo_route=True)
77 pattern='/{repo_name:.*?[^/]}/settings/advanced/journal', repo_route=True)
68 config.add_route(
78 config.add_route(
69 name='edit_repo_advanced_fork',
79 name='edit_repo_advanced_fork',
70 pattern='/{repo_name:.*?[^/]}/settings/advanced/fork', repo_route=True)
80 pattern='/{repo_name:.*?[^/]}/settings/advanced/fork', repo_route=True)
71
81
72 # Caches
82 # Caches
73 config.add_route(
83 config.add_route(
74 name='edit_repo_caches',
84 name='edit_repo_caches',
75 pattern='/{repo_name:.*?[^/]}/settings/caches', repo_route=True)
85 pattern='/{repo_name:.*?[^/]}/settings/caches', repo_route=True)
76
86
77 # Permissions
87 # Permissions
78 config.add_route(
88 config.add_route(
79 name='edit_repo_perms',
89 name='edit_repo_perms',
80 pattern='/{repo_name:.*?[^/]}/settings/permissions', repo_route=True)
90 pattern='/{repo_name:.*?[^/]}/settings/permissions', repo_route=True)
81
91
82 # Repo Review Rules
92 # Repo Review Rules
83 config.add_route(
93 config.add_route(
84 name='repo_reviewers',
94 name='repo_reviewers',
85 pattern='/{repo_name:.*?[^/]}/settings/review/rules', repo_route=True)
95 pattern='/{repo_name:.*?[^/]}/settings/review/rules', repo_route=True)
86
96
87 # Maintenance
97 # Maintenance
88 config.add_route(
98 config.add_route(
89 name='repo_maintenance',
99 name='repo_maintenance',
90 pattern='/{repo_name:.*?[^/]}/settings/maintenance', repo_route=True)
100 pattern='/{repo_name:.*?[^/]}/settings/maintenance', repo_route=True)
91
101
92 config.add_route(
102 config.add_route(
93 name='repo_maintenance_execute',
103 name='repo_maintenance_execute',
94 pattern='/{repo_name:.*?[^/]}/settings/maintenance/execute', repo_route=True)
104 pattern='/{repo_name:.*?[^/]}/settings/maintenance/execute', repo_route=True)
95
105
96 # Strip
106 # Strip
97 config.add_route(
107 config.add_route(
98 name='strip',
108 name='strip',
99 pattern='/{repo_name:.*?[^/]}/settings/strip', repo_route=True)
109 pattern='/{repo_name:.*?[^/]}/settings/strip', repo_route=True)
100
110
101 config.add_route(
111 config.add_route(
102 name='strip_check',
112 name='strip_check',
103 pattern='/{repo_name:.*?[^/]}/settings/strip_check', repo_route=True)
113 pattern='/{repo_name:.*?[^/]}/settings/strip_check', repo_route=True)
104
114
105 config.add_route(
115 config.add_route(
106 name='strip_execute',
116 name='strip_execute',
107 pattern='/{repo_name:.*?[^/]}/settings/strip_execute', repo_route=True)
117 pattern='/{repo_name:.*?[^/]}/settings/strip_execute', repo_route=True)
108
118
109 # NOTE(marcink): needs to be at the end for catch-all
119 # NOTE(marcink): needs to be at the end for catch-all
110 # config.add_route(
120 # config.add_route(
111 # name='repo_summary',
121 # name='repo_summary',
112 # pattern='/{repo_name:.*?[^/]}', repo_route=True)
122 # pattern='/{repo_name:.*?[^/]}', repo_route=True)
113
123
114 # Scan module for configuration decorators.
124 # Scan module for configuration decorators.
115 config.scan()
125 config.scan()
@@ -1,1037 +1,1030 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
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
20
21 """
21 """
22 Routes configuration
22 Routes configuration
23
23
24 The more specific and detailed routes should be defined first so they
24 The more specific and detailed routes should be defined first so they
25 may take precedent over the more generic routes. For more information
25 may take precedent over the more generic routes. For more information
26 refer to the routes manual at http://routes.groovie.org/docs/
26 refer to the routes manual at http://routes.groovie.org/docs/
27
27
28 IMPORTANT: if you change any routing here, make sure to take a look at lib/base.py
28 IMPORTANT: if you change any routing here, make sure to take a look at lib/base.py
29 and _route_name variable which uses some of stored naming here to do redirects.
29 and _route_name variable which uses some of stored naming here to do redirects.
30 """
30 """
31 import os
31 import os
32 import re
32 import re
33 from routes import Mapper
33 from routes import Mapper
34
34
35 # prefix for non repository related links needs to be prefixed with `/`
35 # prefix for non repository related links needs to be prefixed with `/`
36 ADMIN_PREFIX = '/_admin'
36 ADMIN_PREFIX = '/_admin'
37 STATIC_FILE_PREFIX = '/_static'
37 STATIC_FILE_PREFIX = '/_static'
38
38
39 # Default requirements for URL parts
39 # Default requirements for URL parts
40 URL_NAME_REQUIREMENTS = {
40 URL_NAME_REQUIREMENTS = {
41 # group name can have a slash in them, but they must not end with a slash
41 # group name can have a slash in them, but they must not end with a slash
42 'group_name': r'.*?[^/]',
42 'group_name': r'.*?[^/]',
43 'repo_group_name': r'.*?[^/]',
43 'repo_group_name': r'.*?[^/]',
44 # repo names can have a slash in them, but they must not end with a slash
44 # repo names can have a slash in them, but they must not end with a slash
45 'repo_name': r'.*?[^/]',
45 'repo_name': r'.*?[^/]',
46 # file path eats up everything at the end
46 # file path eats up everything at the end
47 'f_path': r'.*',
47 'f_path': r'.*',
48 # reference types
48 # reference types
49 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
49 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
50 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
50 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
51 }
51 }
52
52
53
53
54 def add_route_requirements(route_path, requirements):
54 def add_route_requirements(route_path, requirements):
55 """
55 """
56 Adds regex requirements to pyramid routes using a mapping dict
56 Adds regex requirements to pyramid routes using a mapping dict
57
57
58 >>> add_route_requirements('/{action}/{id}', {'id': r'\d+'})
58 >>> add_route_requirements('/{action}/{id}', {'id': r'\d+'})
59 '/{action}/{id:\d+}'
59 '/{action}/{id:\d+}'
60
60
61 """
61 """
62 for key, regex in requirements.items():
62 for key, regex in requirements.items():
63 route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
63 route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
64 return route_path
64 return route_path
65
65
66
66
67 class JSRoutesMapper(Mapper):
67 class JSRoutesMapper(Mapper):
68 """
68 """
69 Wrapper for routes.Mapper to make pyroutes compatible url definitions
69 Wrapper for routes.Mapper to make pyroutes compatible url definitions
70 """
70 """
71 _named_route_regex = re.compile(r'^[a-z-_0-9A-Z]+$')
71 _named_route_regex = re.compile(r'^[a-z-_0-9A-Z]+$')
72 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
72 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
73 def __init__(self, *args, **kw):
73 def __init__(self, *args, **kw):
74 super(JSRoutesMapper, self).__init__(*args, **kw)
74 super(JSRoutesMapper, self).__init__(*args, **kw)
75 self._jsroutes = []
75 self._jsroutes = []
76
76
77 def connect(self, *args, **kw):
77 def connect(self, *args, **kw):
78 """
78 """
79 Wrapper for connect to take an extra argument jsroute=True
79 Wrapper for connect to take an extra argument jsroute=True
80
80
81 :param jsroute: boolean, if True will add the route to the pyroutes list
81 :param jsroute: boolean, if True will add the route to the pyroutes list
82 """
82 """
83 if kw.pop('jsroute', False):
83 if kw.pop('jsroute', False):
84 if not self._named_route_regex.match(args[0]):
84 if not self._named_route_regex.match(args[0]):
85 raise Exception('only named routes can be added to pyroutes')
85 raise Exception('only named routes can be added to pyroutes')
86 self._jsroutes.append(args[0])
86 self._jsroutes.append(args[0])
87
87
88 super(JSRoutesMapper, self).connect(*args, **kw)
88 super(JSRoutesMapper, self).connect(*args, **kw)
89
89
90 def _extract_route_information(self, route):
90 def _extract_route_information(self, route):
91 """
91 """
92 Convert a route into tuple(name, path, args), eg:
92 Convert a route into tuple(name, path, args), eg:
93 ('show_user', '/profile/%(username)s', ['username'])
93 ('show_user', '/profile/%(username)s', ['username'])
94 """
94 """
95 routepath = route.routepath
95 routepath = route.routepath
96 def replace(matchobj):
96 def replace(matchobj):
97 if matchobj.group(1):
97 if matchobj.group(1):
98 return "%%(%s)s" % matchobj.group(1).split(':')[0]
98 return "%%(%s)s" % matchobj.group(1).split(':')[0]
99 else:
99 else:
100 return "%%(%s)s" % matchobj.group(2)
100 return "%%(%s)s" % matchobj.group(2)
101
101
102 routepath = self._argument_prog.sub(replace, routepath)
102 routepath = self._argument_prog.sub(replace, routepath)
103 return (
103 return (
104 route.name,
104 route.name,
105 routepath,
105 routepath,
106 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
106 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
107 for arg in self._argument_prog.findall(route.routepath)]
107 for arg in self._argument_prog.findall(route.routepath)]
108 )
108 )
109
109
110 def jsroutes(self):
110 def jsroutes(self):
111 """
111 """
112 Return a list of pyroutes.js compatible routes
112 Return a list of pyroutes.js compatible routes
113 """
113 """
114 for route_name in self._jsroutes:
114 for route_name in self._jsroutes:
115 yield self._extract_route_information(self._routenames[route_name])
115 yield self._extract_route_information(self._routenames[route_name])
116
116
117
117
118 def make_map(config):
118 def make_map(config):
119 """Create, configure and return the routes Mapper"""
119 """Create, configure and return the routes Mapper"""
120 rmap = JSRoutesMapper(directory=config['pylons.paths']['controllers'],
120 rmap = JSRoutesMapper(directory=config['pylons.paths']['controllers'],
121 always_scan=config['debug'])
121 always_scan=config['debug'])
122 rmap.minimization = False
122 rmap.minimization = False
123 rmap.explicit = False
123 rmap.explicit = False
124
124
125 from rhodecode.lib.utils2 import str2bool
125 from rhodecode.lib.utils2 import str2bool
126 from rhodecode.model import repo, repo_group
126 from rhodecode.model import repo, repo_group
127
127
128 def check_repo(environ, match_dict):
128 def check_repo(environ, match_dict):
129 """
129 """
130 check for valid repository for proper 404 handling
130 check for valid repository for proper 404 handling
131
131
132 :param environ:
132 :param environ:
133 :param match_dict:
133 :param match_dict:
134 """
134 """
135 repo_name = match_dict.get('repo_name')
135 repo_name = match_dict.get('repo_name')
136
136
137 if match_dict.get('f_path'):
137 if match_dict.get('f_path'):
138 # fix for multiple initial slashes that causes errors
138 # fix for multiple initial slashes that causes errors
139 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
139 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
140 repo_model = repo.RepoModel()
140 repo_model = repo.RepoModel()
141 by_name_match = repo_model.get_by_repo_name(repo_name)
141 by_name_match = repo_model.get_by_repo_name(repo_name)
142 # if we match quickly from database, short circuit the operation,
142 # if we match quickly from database, short circuit the operation,
143 # and validate repo based on the type.
143 # and validate repo based on the type.
144 if by_name_match:
144 if by_name_match:
145 return True
145 return True
146
146
147 by_id_match = repo_model.get_repo_by_id(repo_name)
147 by_id_match = repo_model.get_repo_by_id(repo_name)
148 if by_id_match:
148 if by_id_match:
149 repo_name = by_id_match.repo_name
149 repo_name = by_id_match.repo_name
150 match_dict['repo_name'] = repo_name
150 match_dict['repo_name'] = repo_name
151 return True
151 return True
152
152
153 return False
153 return False
154
154
155 def check_group(environ, match_dict):
155 def check_group(environ, match_dict):
156 """
156 """
157 check for valid repository group path for proper 404 handling
157 check for valid repository group path for proper 404 handling
158
158
159 :param environ:
159 :param environ:
160 :param match_dict:
160 :param match_dict:
161 """
161 """
162 repo_group_name = match_dict.get('group_name')
162 repo_group_name = match_dict.get('group_name')
163 repo_group_model = repo_group.RepoGroupModel()
163 repo_group_model = repo_group.RepoGroupModel()
164 by_name_match = repo_group_model.get_by_group_name(repo_group_name)
164 by_name_match = repo_group_model.get_by_group_name(repo_group_name)
165 if by_name_match:
165 if by_name_match:
166 return True
166 return True
167
167
168 return False
168 return False
169
169
170 def check_user_group(environ, match_dict):
170 def check_user_group(environ, match_dict):
171 """
171 """
172 check for valid user group for proper 404 handling
172 check for valid user group for proper 404 handling
173
173
174 :param environ:
174 :param environ:
175 :param match_dict:
175 :param match_dict:
176 """
176 """
177 return True
177 return True
178
178
179 def check_int(environ, match_dict):
179 def check_int(environ, match_dict):
180 return match_dict.get('id').isdigit()
180 return match_dict.get('id').isdigit()
181
181
182
182
183 #==========================================================================
183 #==========================================================================
184 # CUSTOM ROUTES HERE
184 # CUSTOM ROUTES HERE
185 #==========================================================================
185 #==========================================================================
186
186
187 # MAIN PAGE
187 # MAIN PAGE
188 rmap.connect('home', '/', controller='home', action='index')
188 rmap.connect('home', '/', controller='home', action='index')
189
189
190 # ping and pylons error test
190 # ping and pylons error test
191 rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping')
191 rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping')
192 rmap.connect('error_test', '%s/error_test' % (ADMIN_PREFIX,), controller='home', action='error_test')
192 rmap.connect('error_test', '%s/error_test' % (ADMIN_PREFIX,), controller='home', action='error_test')
193
193
194 # ADMIN REPOSITORY ROUTES
194 # ADMIN REPOSITORY ROUTES
195 with rmap.submapper(path_prefix=ADMIN_PREFIX,
195 with rmap.submapper(path_prefix=ADMIN_PREFIX,
196 controller='admin/repos') as m:
196 controller='admin/repos') as m:
197 m.connect('repos', '/repos',
197 m.connect('repos', '/repos',
198 action='create', conditions={'method': ['POST']})
198 action='create', conditions={'method': ['POST']})
199 m.connect('repos', '/repos',
199 m.connect('repos', '/repos',
200 action='index', conditions={'method': ['GET']})
200 action='index', conditions={'method': ['GET']})
201 m.connect('new_repo', '/create_repository', jsroute=True,
201 m.connect('new_repo', '/create_repository', jsroute=True,
202 action='create_repository', conditions={'method': ['GET']})
202 action='create_repository', conditions={'method': ['GET']})
203 m.connect('delete_repo', '/repos/{repo_name}',
203 m.connect('delete_repo', '/repos/{repo_name}',
204 action='delete', conditions={'method': ['DELETE']},
204 action='delete', conditions={'method': ['DELETE']},
205 requirements=URL_NAME_REQUIREMENTS)
205 requirements=URL_NAME_REQUIREMENTS)
206 m.connect('repo', '/repos/{repo_name}',
206 m.connect('repo', '/repos/{repo_name}',
207 action='show', conditions={'method': ['GET'],
207 action='show', conditions={'method': ['GET'],
208 'function': check_repo},
208 'function': check_repo},
209 requirements=URL_NAME_REQUIREMENTS)
209 requirements=URL_NAME_REQUIREMENTS)
210
210
211 # ADMIN REPOSITORY GROUPS ROUTES
211 # ADMIN REPOSITORY GROUPS ROUTES
212 with rmap.submapper(path_prefix=ADMIN_PREFIX,
212 with rmap.submapper(path_prefix=ADMIN_PREFIX,
213 controller='admin/repo_groups') as m:
213 controller='admin/repo_groups') as m:
214 m.connect('repo_groups', '/repo_groups',
214 m.connect('repo_groups', '/repo_groups',
215 action='create', conditions={'method': ['POST']})
215 action='create', conditions={'method': ['POST']})
216 m.connect('repo_groups', '/repo_groups',
216 m.connect('repo_groups', '/repo_groups',
217 action='index', conditions={'method': ['GET']})
217 action='index', conditions={'method': ['GET']})
218 m.connect('new_repo_group', '/repo_groups/new',
218 m.connect('new_repo_group', '/repo_groups/new',
219 action='new', conditions={'method': ['GET']})
219 action='new', conditions={'method': ['GET']})
220 m.connect('update_repo_group', '/repo_groups/{group_name}',
220 m.connect('update_repo_group', '/repo_groups/{group_name}',
221 action='update', conditions={'method': ['PUT'],
221 action='update', conditions={'method': ['PUT'],
222 'function': check_group},
222 'function': check_group},
223 requirements=URL_NAME_REQUIREMENTS)
223 requirements=URL_NAME_REQUIREMENTS)
224
224
225 # EXTRAS REPO GROUP ROUTES
225 # EXTRAS REPO GROUP ROUTES
226 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
226 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
227 action='edit',
227 action='edit',
228 conditions={'method': ['GET'], 'function': check_group},
228 conditions={'method': ['GET'], 'function': check_group},
229 requirements=URL_NAME_REQUIREMENTS)
229 requirements=URL_NAME_REQUIREMENTS)
230 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
230 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
231 action='edit',
231 action='edit',
232 conditions={'method': ['PUT'], 'function': check_group},
232 conditions={'method': ['PUT'], 'function': check_group},
233 requirements=URL_NAME_REQUIREMENTS)
233 requirements=URL_NAME_REQUIREMENTS)
234
234
235 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
235 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
236 action='edit_repo_group_advanced',
236 action='edit_repo_group_advanced',
237 conditions={'method': ['GET'], 'function': check_group},
237 conditions={'method': ['GET'], 'function': check_group},
238 requirements=URL_NAME_REQUIREMENTS)
238 requirements=URL_NAME_REQUIREMENTS)
239 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
239 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
240 action='edit_repo_group_advanced',
240 action='edit_repo_group_advanced',
241 conditions={'method': ['PUT'], 'function': check_group},
241 conditions={'method': ['PUT'], 'function': check_group},
242 requirements=URL_NAME_REQUIREMENTS)
242 requirements=URL_NAME_REQUIREMENTS)
243
243
244 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
244 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
245 action='edit_repo_group_perms',
245 action='edit_repo_group_perms',
246 conditions={'method': ['GET'], 'function': check_group},
246 conditions={'method': ['GET'], 'function': check_group},
247 requirements=URL_NAME_REQUIREMENTS)
247 requirements=URL_NAME_REQUIREMENTS)
248 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
248 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
249 action='update_perms',
249 action='update_perms',
250 conditions={'method': ['PUT'], 'function': check_group},
250 conditions={'method': ['PUT'], 'function': check_group},
251 requirements=URL_NAME_REQUIREMENTS)
251 requirements=URL_NAME_REQUIREMENTS)
252
252
253 m.connect('delete_repo_group', '/repo_groups/{group_name}',
253 m.connect('delete_repo_group', '/repo_groups/{group_name}',
254 action='delete', conditions={'method': ['DELETE'],
254 action='delete', conditions={'method': ['DELETE'],
255 'function': check_group},
255 'function': check_group},
256 requirements=URL_NAME_REQUIREMENTS)
256 requirements=URL_NAME_REQUIREMENTS)
257
257
258 # ADMIN USER ROUTES
258 # ADMIN USER ROUTES
259 with rmap.submapper(path_prefix=ADMIN_PREFIX,
259 with rmap.submapper(path_prefix=ADMIN_PREFIX,
260 controller='admin/users') as m:
260 controller='admin/users') as m:
261 m.connect('users', '/users',
261 m.connect('users', '/users',
262 action='create', conditions={'method': ['POST']})
262 action='create', conditions={'method': ['POST']})
263 m.connect('new_user', '/users/new',
263 m.connect('new_user', '/users/new',
264 action='new', conditions={'method': ['GET']})
264 action='new', conditions={'method': ['GET']})
265 m.connect('update_user', '/users/{user_id}',
265 m.connect('update_user', '/users/{user_id}',
266 action='update', conditions={'method': ['PUT']})
266 action='update', conditions={'method': ['PUT']})
267 m.connect('delete_user', '/users/{user_id}',
267 m.connect('delete_user', '/users/{user_id}',
268 action='delete', conditions={'method': ['DELETE']})
268 action='delete', conditions={'method': ['DELETE']})
269 m.connect('edit_user', '/users/{user_id}/edit',
269 m.connect('edit_user', '/users/{user_id}/edit',
270 action='edit', conditions={'method': ['GET']}, jsroute=True)
270 action='edit', conditions={'method': ['GET']}, jsroute=True)
271 m.connect('user', '/users/{user_id}',
271 m.connect('user', '/users/{user_id}',
272 action='show', conditions={'method': ['GET']})
272 action='show', conditions={'method': ['GET']})
273 m.connect('force_password_reset_user', '/users/{user_id}/password_reset',
273 m.connect('force_password_reset_user', '/users/{user_id}/password_reset',
274 action='reset_password', conditions={'method': ['POST']})
274 action='reset_password', conditions={'method': ['POST']})
275 m.connect('create_personal_repo_group', '/users/{user_id}/create_repo_group',
275 m.connect('create_personal_repo_group', '/users/{user_id}/create_repo_group',
276 action='create_personal_repo_group', conditions={'method': ['POST']})
276 action='create_personal_repo_group', conditions={'method': ['POST']})
277
277
278 # EXTRAS USER ROUTES
278 # EXTRAS USER ROUTES
279 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
279 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
280 action='edit_advanced', conditions={'method': ['GET']})
280 action='edit_advanced', conditions={'method': ['GET']})
281 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
281 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
282 action='update_advanced', conditions={'method': ['PUT']})
282 action='update_advanced', conditions={'method': ['PUT']})
283
283
284 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
284 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
285 action='edit_global_perms', conditions={'method': ['GET']})
285 action='edit_global_perms', conditions={'method': ['GET']})
286 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
286 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
287 action='update_global_perms', conditions={'method': ['PUT']})
287 action='update_global_perms', conditions={'method': ['PUT']})
288
288
289 m.connect('edit_user_perms_summary', '/users/{user_id}/edit/permissions_summary',
289 m.connect('edit_user_perms_summary', '/users/{user_id}/edit/permissions_summary',
290 action='edit_perms_summary', conditions={'method': ['GET']})
290 action='edit_perms_summary', conditions={'method': ['GET']})
291
291
292 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
292 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
293 action='edit_emails', conditions={'method': ['GET']})
293 action='edit_emails', conditions={'method': ['GET']})
294 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
294 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
295 action='add_email', conditions={'method': ['PUT']})
295 action='add_email', conditions={'method': ['PUT']})
296 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
296 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
297 action='delete_email', conditions={'method': ['DELETE']})
297 action='delete_email', conditions={'method': ['DELETE']})
298
298
299 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
299 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
300 action='edit_ips', conditions={'method': ['GET']})
300 action='edit_ips', conditions={'method': ['GET']})
301 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
301 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
302 action='add_ip', conditions={'method': ['PUT']})
302 action='add_ip', conditions={'method': ['PUT']})
303 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
303 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
304 action='delete_ip', conditions={'method': ['DELETE']})
304 action='delete_ip', conditions={'method': ['DELETE']})
305
305
306 # ADMIN USER GROUPS REST ROUTES
306 # ADMIN USER GROUPS REST ROUTES
307 with rmap.submapper(path_prefix=ADMIN_PREFIX,
307 with rmap.submapper(path_prefix=ADMIN_PREFIX,
308 controller='admin/user_groups') as m:
308 controller='admin/user_groups') as m:
309 m.connect('users_groups', '/user_groups',
309 m.connect('users_groups', '/user_groups',
310 action='create', conditions={'method': ['POST']})
310 action='create', conditions={'method': ['POST']})
311 m.connect('users_groups', '/user_groups',
311 m.connect('users_groups', '/user_groups',
312 action='index', conditions={'method': ['GET']})
312 action='index', conditions={'method': ['GET']})
313 m.connect('new_users_group', '/user_groups/new',
313 m.connect('new_users_group', '/user_groups/new',
314 action='new', conditions={'method': ['GET']})
314 action='new', conditions={'method': ['GET']})
315 m.connect('update_users_group', '/user_groups/{user_group_id}',
315 m.connect('update_users_group', '/user_groups/{user_group_id}',
316 action='update', conditions={'method': ['PUT']})
316 action='update', conditions={'method': ['PUT']})
317 m.connect('delete_users_group', '/user_groups/{user_group_id}',
317 m.connect('delete_users_group', '/user_groups/{user_group_id}',
318 action='delete', conditions={'method': ['DELETE']})
318 action='delete', conditions={'method': ['DELETE']})
319 m.connect('edit_users_group', '/user_groups/{user_group_id}/edit',
319 m.connect('edit_users_group', '/user_groups/{user_group_id}/edit',
320 action='edit', conditions={'method': ['GET']},
320 action='edit', conditions={'method': ['GET']},
321 function=check_user_group)
321 function=check_user_group)
322
322
323 # EXTRAS USER GROUP ROUTES
323 # EXTRAS USER GROUP ROUTES
324 m.connect('edit_user_group_global_perms',
324 m.connect('edit_user_group_global_perms',
325 '/user_groups/{user_group_id}/edit/global_permissions',
325 '/user_groups/{user_group_id}/edit/global_permissions',
326 action='edit_global_perms', conditions={'method': ['GET']})
326 action='edit_global_perms', conditions={'method': ['GET']})
327 m.connect('edit_user_group_global_perms',
327 m.connect('edit_user_group_global_perms',
328 '/user_groups/{user_group_id}/edit/global_permissions',
328 '/user_groups/{user_group_id}/edit/global_permissions',
329 action='update_global_perms', conditions={'method': ['PUT']})
329 action='update_global_perms', conditions={'method': ['PUT']})
330 m.connect('edit_user_group_perms_summary',
330 m.connect('edit_user_group_perms_summary',
331 '/user_groups/{user_group_id}/edit/permissions_summary',
331 '/user_groups/{user_group_id}/edit/permissions_summary',
332 action='edit_perms_summary', conditions={'method': ['GET']})
332 action='edit_perms_summary', conditions={'method': ['GET']})
333
333
334 m.connect('edit_user_group_perms',
334 m.connect('edit_user_group_perms',
335 '/user_groups/{user_group_id}/edit/permissions',
335 '/user_groups/{user_group_id}/edit/permissions',
336 action='edit_perms', conditions={'method': ['GET']})
336 action='edit_perms', conditions={'method': ['GET']})
337 m.connect('edit_user_group_perms',
337 m.connect('edit_user_group_perms',
338 '/user_groups/{user_group_id}/edit/permissions',
338 '/user_groups/{user_group_id}/edit/permissions',
339 action='update_perms', conditions={'method': ['PUT']})
339 action='update_perms', conditions={'method': ['PUT']})
340
340
341 m.connect('edit_user_group_advanced',
341 m.connect('edit_user_group_advanced',
342 '/user_groups/{user_group_id}/edit/advanced',
342 '/user_groups/{user_group_id}/edit/advanced',
343 action='edit_advanced', conditions={'method': ['GET']})
343 action='edit_advanced', conditions={'method': ['GET']})
344
344
345 m.connect('edit_user_group_advanced_sync',
345 m.connect('edit_user_group_advanced_sync',
346 '/user_groups/{user_group_id}/edit/advanced/sync',
346 '/user_groups/{user_group_id}/edit/advanced/sync',
347 action='edit_advanced_set_synchronization', conditions={'method': ['POST']})
347 action='edit_advanced_set_synchronization', conditions={'method': ['POST']})
348
348
349 m.connect('edit_user_group_members',
349 m.connect('edit_user_group_members',
350 '/user_groups/{user_group_id}/edit/members', jsroute=True,
350 '/user_groups/{user_group_id}/edit/members', jsroute=True,
351 action='user_group_members', conditions={'method': ['GET']})
351 action='user_group_members', conditions={'method': ['GET']})
352
352
353 # ADMIN PERMISSIONS ROUTES
353 # ADMIN PERMISSIONS ROUTES
354 with rmap.submapper(path_prefix=ADMIN_PREFIX,
354 with rmap.submapper(path_prefix=ADMIN_PREFIX,
355 controller='admin/permissions') as m:
355 controller='admin/permissions') as m:
356 m.connect('admin_permissions_application', '/permissions/application',
356 m.connect('admin_permissions_application', '/permissions/application',
357 action='permission_application_update', conditions={'method': ['POST']})
357 action='permission_application_update', conditions={'method': ['POST']})
358 m.connect('admin_permissions_application', '/permissions/application',
358 m.connect('admin_permissions_application', '/permissions/application',
359 action='permission_application', conditions={'method': ['GET']})
359 action='permission_application', conditions={'method': ['GET']})
360
360
361 m.connect('admin_permissions_global', '/permissions/global',
361 m.connect('admin_permissions_global', '/permissions/global',
362 action='permission_global_update', conditions={'method': ['POST']})
362 action='permission_global_update', conditions={'method': ['POST']})
363 m.connect('admin_permissions_global', '/permissions/global',
363 m.connect('admin_permissions_global', '/permissions/global',
364 action='permission_global', conditions={'method': ['GET']})
364 action='permission_global', conditions={'method': ['GET']})
365
365
366 m.connect('admin_permissions_object', '/permissions/object',
366 m.connect('admin_permissions_object', '/permissions/object',
367 action='permission_objects_update', conditions={'method': ['POST']})
367 action='permission_objects_update', conditions={'method': ['POST']})
368 m.connect('admin_permissions_object', '/permissions/object',
368 m.connect('admin_permissions_object', '/permissions/object',
369 action='permission_objects', conditions={'method': ['GET']})
369 action='permission_objects', conditions={'method': ['GET']})
370
370
371 m.connect('admin_permissions_ips', '/permissions/ips',
371 m.connect('admin_permissions_ips', '/permissions/ips',
372 action='permission_ips', conditions={'method': ['POST']})
372 action='permission_ips', conditions={'method': ['POST']})
373 m.connect('admin_permissions_ips', '/permissions/ips',
373 m.connect('admin_permissions_ips', '/permissions/ips',
374 action='permission_ips', conditions={'method': ['GET']})
374 action='permission_ips', conditions={'method': ['GET']})
375
375
376 m.connect('admin_permissions_overview', '/permissions/overview',
376 m.connect('admin_permissions_overview', '/permissions/overview',
377 action='permission_perms', conditions={'method': ['GET']})
377 action='permission_perms', conditions={'method': ['GET']})
378
378
379 # ADMIN DEFAULTS REST ROUTES
379 # ADMIN DEFAULTS REST ROUTES
380 with rmap.submapper(path_prefix=ADMIN_PREFIX,
380 with rmap.submapper(path_prefix=ADMIN_PREFIX,
381 controller='admin/defaults') as m:
381 controller='admin/defaults') as m:
382 m.connect('admin_defaults_repositories', '/defaults/repositories',
382 m.connect('admin_defaults_repositories', '/defaults/repositories',
383 action='update_repository_defaults', conditions={'method': ['POST']})
383 action='update_repository_defaults', conditions={'method': ['POST']})
384 m.connect('admin_defaults_repositories', '/defaults/repositories',
384 m.connect('admin_defaults_repositories', '/defaults/repositories',
385 action='index', conditions={'method': ['GET']})
385 action='index', conditions={'method': ['GET']})
386
386
387 # ADMIN DEBUG STYLE ROUTES
387 # ADMIN DEBUG STYLE ROUTES
388 if str2bool(config.get('debug_style')):
388 if str2bool(config.get('debug_style')):
389 with rmap.submapper(path_prefix=ADMIN_PREFIX + '/debug_style',
389 with rmap.submapper(path_prefix=ADMIN_PREFIX + '/debug_style',
390 controller='debug_style') as m:
390 controller='debug_style') as m:
391 m.connect('debug_style_home', '',
391 m.connect('debug_style_home', '',
392 action='index', conditions={'method': ['GET']})
392 action='index', conditions={'method': ['GET']})
393 m.connect('debug_style_template', '/t/{t_path}',
393 m.connect('debug_style_template', '/t/{t_path}',
394 action='template', conditions={'method': ['GET']})
394 action='template', conditions={'method': ['GET']})
395
395
396 # ADMIN SETTINGS ROUTES
396 # ADMIN SETTINGS ROUTES
397 with rmap.submapper(path_prefix=ADMIN_PREFIX,
397 with rmap.submapper(path_prefix=ADMIN_PREFIX,
398 controller='admin/settings') as m:
398 controller='admin/settings') as m:
399
399
400 # default
400 # default
401 m.connect('admin_settings', '/settings',
401 m.connect('admin_settings', '/settings',
402 action='settings_global_update',
402 action='settings_global_update',
403 conditions={'method': ['POST']})
403 conditions={'method': ['POST']})
404 m.connect('admin_settings', '/settings',
404 m.connect('admin_settings', '/settings',
405 action='settings_global', conditions={'method': ['GET']})
405 action='settings_global', conditions={'method': ['GET']})
406
406
407 m.connect('admin_settings_vcs', '/settings/vcs',
407 m.connect('admin_settings_vcs', '/settings/vcs',
408 action='settings_vcs_update',
408 action='settings_vcs_update',
409 conditions={'method': ['POST']})
409 conditions={'method': ['POST']})
410 m.connect('admin_settings_vcs', '/settings/vcs',
410 m.connect('admin_settings_vcs', '/settings/vcs',
411 action='settings_vcs',
411 action='settings_vcs',
412 conditions={'method': ['GET']})
412 conditions={'method': ['GET']})
413 m.connect('admin_settings_vcs', '/settings/vcs',
413 m.connect('admin_settings_vcs', '/settings/vcs',
414 action='delete_svn_pattern',
414 action='delete_svn_pattern',
415 conditions={'method': ['DELETE']})
415 conditions={'method': ['DELETE']})
416
416
417 m.connect('admin_settings_mapping', '/settings/mapping',
417 m.connect('admin_settings_mapping', '/settings/mapping',
418 action='settings_mapping_update',
418 action='settings_mapping_update',
419 conditions={'method': ['POST']})
419 conditions={'method': ['POST']})
420 m.connect('admin_settings_mapping', '/settings/mapping',
420 m.connect('admin_settings_mapping', '/settings/mapping',
421 action='settings_mapping', conditions={'method': ['GET']})
421 action='settings_mapping', conditions={'method': ['GET']})
422
422
423 m.connect('admin_settings_global', '/settings/global',
423 m.connect('admin_settings_global', '/settings/global',
424 action='settings_global_update',
424 action='settings_global_update',
425 conditions={'method': ['POST']})
425 conditions={'method': ['POST']})
426 m.connect('admin_settings_global', '/settings/global',
426 m.connect('admin_settings_global', '/settings/global',
427 action='settings_global', conditions={'method': ['GET']})
427 action='settings_global', conditions={'method': ['GET']})
428
428
429 m.connect('admin_settings_visual', '/settings/visual',
429 m.connect('admin_settings_visual', '/settings/visual',
430 action='settings_visual_update',
430 action='settings_visual_update',
431 conditions={'method': ['POST']})
431 conditions={'method': ['POST']})
432 m.connect('admin_settings_visual', '/settings/visual',
432 m.connect('admin_settings_visual', '/settings/visual',
433 action='settings_visual', conditions={'method': ['GET']})
433 action='settings_visual', conditions={'method': ['GET']})
434
434
435 m.connect('admin_settings_issuetracker',
435 m.connect('admin_settings_issuetracker',
436 '/settings/issue-tracker', action='settings_issuetracker',
436 '/settings/issue-tracker', action='settings_issuetracker',
437 conditions={'method': ['GET']})
437 conditions={'method': ['GET']})
438 m.connect('admin_settings_issuetracker_save',
438 m.connect('admin_settings_issuetracker_save',
439 '/settings/issue-tracker/save',
439 '/settings/issue-tracker/save',
440 action='settings_issuetracker_save',
440 action='settings_issuetracker_save',
441 conditions={'method': ['POST']})
441 conditions={'method': ['POST']})
442 m.connect('admin_issuetracker_test', '/settings/issue-tracker/test',
442 m.connect('admin_issuetracker_test', '/settings/issue-tracker/test',
443 action='settings_issuetracker_test',
443 action='settings_issuetracker_test',
444 conditions={'method': ['POST']})
444 conditions={'method': ['POST']})
445 m.connect('admin_issuetracker_delete',
445 m.connect('admin_issuetracker_delete',
446 '/settings/issue-tracker/delete',
446 '/settings/issue-tracker/delete',
447 action='settings_issuetracker_delete',
447 action='settings_issuetracker_delete',
448 conditions={'method': ['DELETE']})
448 conditions={'method': ['DELETE']})
449
449
450 m.connect('admin_settings_email', '/settings/email',
450 m.connect('admin_settings_email', '/settings/email',
451 action='settings_email_update',
451 action='settings_email_update',
452 conditions={'method': ['POST']})
452 conditions={'method': ['POST']})
453 m.connect('admin_settings_email', '/settings/email',
453 m.connect('admin_settings_email', '/settings/email',
454 action='settings_email', conditions={'method': ['GET']})
454 action='settings_email', conditions={'method': ['GET']})
455
455
456 m.connect('admin_settings_hooks', '/settings/hooks',
456 m.connect('admin_settings_hooks', '/settings/hooks',
457 action='settings_hooks_update',
457 action='settings_hooks_update',
458 conditions={'method': ['POST', 'DELETE']})
458 conditions={'method': ['POST', 'DELETE']})
459 m.connect('admin_settings_hooks', '/settings/hooks',
459 m.connect('admin_settings_hooks', '/settings/hooks',
460 action='settings_hooks', conditions={'method': ['GET']})
460 action='settings_hooks', conditions={'method': ['GET']})
461
461
462 m.connect('admin_settings_search', '/settings/search',
462 m.connect('admin_settings_search', '/settings/search',
463 action='settings_search', conditions={'method': ['GET']})
463 action='settings_search', conditions={'method': ['GET']})
464
464
465 m.connect('admin_settings_supervisor', '/settings/supervisor',
465 m.connect('admin_settings_supervisor', '/settings/supervisor',
466 action='settings_supervisor', conditions={'method': ['GET']})
466 action='settings_supervisor', conditions={'method': ['GET']})
467 m.connect('admin_settings_supervisor_log', '/settings/supervisor/{procid}/log',
467 m.connect('admin_settings_supervisor_log', '/settings/supervisor/{procid}/log',
468 action='settings_supervisor_log', conditions={'method': ['GET']})
468 action='settings_supervisor_log', conditions={'method': ['GET']})
469
469
470 m.connect('admin_settings_labs', '/settings/labs',
470 m.connect('admin_settings_labs', '/settings/labs',
471 action='settings_labs_update',
471 action='settings_labs_update',
472 conditions={'method': ['POST']})
472 conditions={'method': ['POST']})
473 m.connect('admin_settings_labs', '/settings/labs',
473 m.connect('admin_settings_labs', '/settings/labs',
474 action='settings_labs', conditions={'method': ['GET']})
474 action='settings_labs', conditions={'method': ['GET']})
475
475
476 # ADMIN MY ACCOUNT
476 # ADMIN MY ACCOUNT
477 with rmap.submapper(path_prefix=ADMIN_PREFIX,
477 with rmap.submapper(path_prefix=ADMIN_PREFIX,
478 controller='admin/my_account') as m:
478 controller='admin/my_account') as m:
479
479
480 m.connect('my_account_edit', '/my_account/edit',
480 m.connect('my_account_edit', '/my_account/edit',
481 action='my_account_edit', conditions={'method': ['GET']})
481 action='my_account_edit', conditions={'method': ['GET']})
482 m.connect('my_account', '/my_account/update',
482 m.connect('my_account', '/my_account/update',
483 action='my_account_update', conditions={'method': ['POST']})
483 action='my_account_update', conditions={'method': ['POST']})
484
484
485 # NOTE(marcink): this needs to be kept for password force flag to be
485 # NOTE(marcink): this needs to be kept for password force flag to be
486 # handler, remove after migration to pyramid
486 # handler, remove after migration to pyramid
487 m.connect('my_account_password', '/my_account/password',
487 m.connect('my_account_password', '/my_account/password',
488 action='my_account_password', conditions={'method': ['GET']})
488 action='my_account_password', conditions={'method': ['GET']})
489
489
490 m.connect('my_account_repos', '/my_account/repos',
490 m.connect('my_account_repos', '/my_account/repos',
491 action='my_account_repos', conditions={'method': ['GET']})
491 action='my_account_repos', conditions={'method': ['GET']})
492
492
493 m.connect('my_account_watched', '/my_account/watched',
493 m.connect('my_account_watched', '/my_account/watched',
494 action='my_account_watched', conditions={'method': ['GET']})
494 action='my_account_watched', conditions={'method': ['GET']})
495
495
496 m.connect('my_account_pullrequests', '/my_account/pull_requests',
496 m.connect('my_account_pullrequests', '/my_account/pull_requests',
497 action='my_account_pullrequests', conditions={'method': ['GET']})
497 action='my_account_pullrequests', conditions={'method': ['GET']})
498
498
499 m.connect('my_account_perms', '/my_account/perms',
499 m.connect('my_account_perms', '/my_account/perms',
500 action='my_account_perms', conditions={'method': ['GET']})
500 action='my_account_perms', conditions={'method': ['GET']})
501
501
502 m.connect('my_account_emails', '/my_account/emails',
502 m.connect('my_account_emails', '/my_account/emails',
503 action='my_account_emails', conditions={'method': ['GET']})
503 action='my_account_emails', conditions={'method': ['GET']})
504 m.connect('my_account_emails', '/my_account/emails',
504 m.connect('my_account_emails', '/my_account/emails',
505 action='my_account_emails_add', conditions={'method': ['POST']})
505 action='my_account_emails_add', conditions={'method': ['POST']})
506 m.connect('my_account_emails', '/my_account/emails',
506 m.connect('my_account_emails', '/my_account/emails',
507 action='my_account_emails_delete', conditions={'method': ['DELETE']})
507 action='my_account_emails_delete', conditions={'method': ['DELETE']})
508
508
509 m.connect('my_account_notifications', '/my_account/notifications',
509 m.connect('my_account_notifications', '/my_account/notifications',
510 action='my_notifications',
510 action='my_notifications',
511 conditions={'method': ['GET']})
511 conditions={'method': ['GET']})
512 m.connect('my_account_notifications_toggle_visibility',
512 m.connect('my_account_notifications_toggle_visibility',
513 '/my_account/toggle_visibility',
513 '/my_account/toggle_visibility',
514 action='my_notifications_toggle_visibility',
514 action='my_notifications_toggle_visibility',
515 conditions={'method': ['POST']})
515 conditions={'method': ['POST']})
516
516
517 # NOTIFICATION REST ROUTES
517 # NOTIFICATION REST ROUTES
518 with rmap.submapper(path_prefix=ADMIN_PREFIX,
518 with rmap.submapper(path_prefix=ADMIN_PREFIX,
519 controller='admin/notifications') as m:
519 controller='admin/notifications') as m:
520 m.connect('notifications', '/notifications',
520 m.connect('notifications', '/notifications',
521 action='index', conditions={'method': ['GET']})
521 action='index', conditions={'method': ['GET']})
522 m.connect('notifications_mark_all_read', '/notifications/mark_all_read',
522 m.connect('notifications_mark_all_read', '/notifications/mark_all_read',
523 action='mark_all_read', conditions={'method': ['POST']})
523 action='mark_all_read', conditions={'method': ['POST']})
524 m.connect('/notifications/{notification_id}',
524 m.connect('/notifications/{notification_id}',
525 action='update', conditions={'method': ['PUT']})
525 action='update', conditions={'method': ['PUT']})
526 m.connect('/notifications/{notification_id}',
526 m.connect('/notifications/{notification_id}',
527 action='delete', conditions={'method': ['DELETE']})
527 action='delete', conditions={'method': ['DELETE']})
528 m.connect('notification', '/notifications/{notification_id}',
528 m.connect('notification', '/notifications/{notification_id}',
529 action='show', conditions={'method': ['GET']})
529 action='show', conditions={'method': ['GET']})
530
530
531 # ADMIN GIST
531 # ADMIN GIST
532 with rmap.submapper(path_prefix=ADMIN_PREFIX,
532 with rmap.submapper(path_prefix=ADMIN_PREFIX,
533 controller='admin/gists') as m:
533 controller='admin/gists') as m:
534 m.connect('gists', '/gists',
534 m.connect('gists', '/gists',
535 action='create', conditions={'method': ['POST']})
535 action='create', conditions={'method': ['POST']})
536 m.connect('gists', '/gists', jsroute=True,
536 m.connect('gists', '/gists', jsroute=True,
537 action='index', conditions={'method': ['GET']})
537 action='index', conditions={'method': ['GET']})
538 m.connect('new_gist', '/gists/new', jsroute=True,
538 m.connect('new_gist', '/gists/new', jsroute=True,
539 action='new', conditions={'method': ['GET']})
539 action='new', conditions={'method': ['GET']})
540
540
541 m.connect('/gists/{gist_id}',
541 m.connect('/gists/{gist_id}',
542 action='delete', conditions={'method': ['DELETE']})
542 action='delete', conditions={'method': ['DELETE']})
543 m.connect('edit_gist', '/gists/{gist_id}/edit',
543 m.connect('edit_gist', '/gists/{gist_id}/edit',
544 action='edit_form', conditions={'method': ['GET']})
544 action='edit_form', conditions={'method': ['GET']})
545 m.connect('edit_gist', '/gists/{gist_id}/edit',
545 m.connect('edit_gist', '/gists/{gist_id}/edit',
546 action='edit', conditions={'method': ['POST']})
546 action='edit', conditions={'method': ['POST']})
547 m.connect(
547 m.connect(
548 'edit_gist_check_revision', '/gists/{gist_id}/edit/check_revision',
548 'edit_gist_check_revision', '/gists/{gist_id}/edit/check_revision',
549 action='check_revision', conditions={'method': ['GET']})
549 action='check_revision', conditions={'method': ['GET']})
550
550
551 m.connect('gist', '/gists/{gist_id}',
551 m.connect('gist', '/gists/{gist_id}',
552 action='show', conditions={'method': ['GET']})
552 action='show', conditions={'method': ['GET']})
553 m.connect('gist_rev', '/gists/{gist_id}/{revision}',
553 m.connect('gist_rev', '/gists/{gist_id}/{revision}',
554 revision='tip',
554 revision='tip',
555 action='show', conditions={'method': ['GET']})
555 action='show', conditions={'method': ['GET']})
556 m.connect('formatted_gist', '/gists/{gist_id}/{revision}/{format}',
556 m.connect('formatted_gist', '/gists/{gist_id}/{revision}/{format}',
557 revision='tip',
557 revision='tip',
558 action='show', conditions={'method': ['GET']})
558 action='show', conditions={'method': ['GET']})
559 m.connect('formatted_gist_file', '/gists/{gist_id}/{revision}/{format}/{f_path}',
559 m.connect('formatted_gist_file', '/gists/{gist_id}/{revision}/{format}/{f_path}',
560 revision='tip',
560 revision='tip',
561 action='show', conditions={'method': ['GET']},
561 action='show', conditions={'method': ['GET']},
562 requirements=URL_NAME_REQUIREMENTS)
562 requirements=URL_NAME_REQUIREMENTS)
563
563
564 # USER JOURNAL
564 # USER JOURNAL
565 rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
565 rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
566 controller='journal', action='index')
566 controller='journal', action='index')
567 rmap.connect('journal_rss', '%s/journal/rss' % (ADMIN_PREFIX,),
567 rmap.connect('journal_rss', '%s/journal/rss' % (ADMIN_PREFIX,),
568 controller='journal', action='journal_rss')
568 controller='journal', action='journal_rss')
569 rmap.connect('journal_atom', '%s/journal/atom' % (ADMIN_PREFIX,),
569 rmap.connect('journal_atom', '%s/journal/atom' % (ADMIN_PREFIX,),
570 controller='journal', action='journal_atom')
570 controller='journal', action='journal_atom')
571
571
572 rmap.connect('public_journal', '%s/public_journal' % (ADMIN_PREFIX,),
572 rmap.connect('public_journal', '%s/public_journal' % (ADMIN_PREFIX,),
573 controller='journal', action='public_journal')
573 controller='journal', action='public_journal')
574
574
575 rmap.connect('public_journal_rss', '%s/public_journal/rss' % (ADMIN_PREFIX,),
575 rmap.connect('public_journal_rss', '%s/public_journal/rss' % (ADMIN_PREFIX,),
576 controller='journal', action='public_journal_rss')
576 controller='journal', action='public_journal_rss')
577
577
578 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % (ADMIN_PREFIX,),
578 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % (ADMIN_PREFIX,),
579 controller='journal', action='public_journal_rss')
579 controller='journal', action='public_journal_rss')
580
580
581 rmap.connect('public_journal_atom',
581 rmap.connect('public_journal_atom',
582 '%s/public_journal/atom' % (ADMIN_PREFIX,), controller='journal',
582 '%s/public_journal/atom' % (ADMIN_PREFIX,), controller='journal',
583 action='public_journal_atom')
583 action='public_journal_atom')
584
584
585 rmap.connect('public_journal_atom_old',
585 rmap.connect('public_journal_atom_old',
586 '%s/public_journal_atom' % (ADMIN_PREFIX,), controller='journal',
586 '%s/public_journal_atom' % (ADMIN_PREFIX,), controller='journal',
587 action='public_journal_atom')
587 action='public_journal_atom')
588
588
589 rmap.connect('toggle_following', '%s/toggle_following' % (ADMIN_PREFIX,),
589 rmap.connect('toggle_following', '%s/toggle_following' % (ADMIN_PREFIX,),
590 controller='journal', action='toggle_following', jsroute=True,
590 controller='journal', action='toggle_following', jsroute=True,
591 conditions={'method': ['POST']})
591 conditions={'method': ['POST']})
592
592
593 # FEEDS
593 # FEEDS
594 rmap.connect('rss_feed_home', '/{repo_name}/feed/rss',
594 rmap.connect('rss_feed_home', '/{repo_name}/feed/rss',
595 controller='feed', action='rss',
595 controller='feed', action='rss',
596 conditions={'function': check_repo},
596 conditions={'function': check_repo},
597 requirements=URL_NAME_REQUIREMENTS)
597 requirements=URL_NAME_REQUIREMENTS)
598
598
599 rmap.connect('atom_feed_home', '/{repo_name}/feed/atom',
599 rmap.connect('atom_feed_home', '/{repo_name}/feed/atom',
600 controller='feed', action='atom',
600 controller='feed', action='atom',
601 conditions={'function': check_repo},
601 conditions={'function': check_repo},
602 requirements=URL_NAME_REQUIREMENTS)
602 requirements=URL_NAME_REQUIREMENTS)
603
603
604 #==========================================================================
604 #==========================================================================
605 # REPOSITORY ROUTES
605 # REPOSITORY ROUTES
606 #==========================================================================
606 #==========================================================================
607
607
608 rmap.connect('repo_creating_home', '/{repo_name}/repo_creating',
608 rmap.connect('repo_creating_home', '/{repo_name}/repo_creating',
609 controller='admin/repos', action='repo_creating',
609 controller='admin/repos', action='repo_creating',
610 requirements=URL_NAME_REQUIREMENTS)
610 requirements=URL_NAME_REQUIREMENTS)
611 rmap.connect('repo_check_home', '/{repo_name}/crepo_check',
611 rmap.connect('repo_check_home', '/{repo_name}/crepo_check',
612 controller='admin/repos', action='repo_check',
612 controller='admin/repos', action='repo_check',
613 requirements=URL_NAME_REQUIREMENTS)
613 requirements=URL_NAME_REQUIREMENTS)
614
614
615 rmap.connect('repo_stats', '/{repo_name}/repo_stats/{commit_id}',
615 rmap.connect('repo_stats', '/{repo_name}/repo_stats/{commit_id}',
616 controller='summary', action='repo_stats',
616 controller='summary', action='repo_stats',
617 conditions={'function': check_repo},
617 conditions={'function': check_repo},
618 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
618 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
619
619
620 rmap.connect('repo_refs_data', '/{repo_name}/refs-data',
620 rmap.connect('repo_refs_data', '/{repo_name}/refs-data',
621 controller='summary', action='repo_refs_data',
621 controller='summary', action='repo_refs_data',
622 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
622 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
623 rmap.connect('repo_refs_changelog_data', '/{repo_name}/refs-data-changelog',
623 rmap.connect('repo_refs_changelog_data', '/{repo_name}/refs-data-changelog',
624 controller='summary', action='repo_refs_changelog_data',
624 controller='summary', action='repo_refs_changelog_data',
625 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
625 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
626 rmap.connect('repo_default_reviewers_data', '/{repo_name}/default-reviewers',
626 rmap.connect('repo_default_reviewers_data', '/{repo_name}/default-reviewers',
627 controller='summary', action='repo_default_reviewers_data',
627 controller='summary', action='repo_default_reviewers_data',
628 jsroute=True, requirements=URL_NAME_REQUIREMENTS)
628 jsroute=True, requirements=URL_NAME_REQUIREMENTS)
629
629
630 rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}',
630 rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}',
631 controller='changeset', revision='tip',
631 controller='changeset', revision='tip',
632 conditions={'function': check_repo},
632 conditions={'function': check_repo},
633 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
633 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
634 rmap.connect('changeset_children', '/{repo_name}/changeset_children/{revision}',
634 rmap.connect('changeset_children', '/{repo_name}/changeset_children/{revision}',
635 controller='changeset', revision='tip', action='changeset_children',
635 controller='changeset', revision='tip', action='changeset_children',
636 conditions={'function': check_repo},
636 conditions={'function': check_repo},
637 requirements=URL_NAME_REQUIREMENTS)
637 requirements=URL_NAME_REQUIREMENTS)
638 rmap.connect('changeset_parents', '/{repo_name}/changeset_parents/{revision}',
638 rmap.connect('changeset_parents', '/{repo_name}/changeset_parents/{revision}',
639 controller='changeset', revision='tip', action='changeset_parents',
639 controller='changeset', revision='tip', action='changeset_parents',
640 conditions={'function': check_repo},
640 conditions={'function': check_repo},
641 requirements=URL_NAME_REQUIREMENTS)
641 requirements=URL_NAME_REQUIREMENTS)
642
642
643 # repo edit options
643 # repo edit options
644 rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
644 rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
645 controller='admin/repos', action='edit_fields',
645 controller='admin/repos', action='edit_fields',
646 conditions={'method': ['GET'], 'function': check_repo},
646 conditions={'method': ['GET'], 'function': check_repo},
647 requirements=URL_NAME_REQUIREMENTS)
647 requirements=URL_NAME_REQUIREMENTS)
648 rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new',
648 rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new',
649 controller='admin/repos', action='create_repo_field',
649 controller='admin/repos', action='create_repo_field',
650 conditions={'method': ['PUT'], 'function': check_repo},
650 conditions={'method': ['PUT'], 'function': check_repo},
651 requirements=URL_NAME_REQUIREMENTS)
651 requirements=URL_NAME_REQUIREMENTS)
652 rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}',
652 rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}',
653 controller='admin/repos', action='delete_repo_field',
653 controller='admin/repos', action='delete_repo_field',
654 conditions={'method': ['DELETE'], 'function': check_repo},
654 conditions={'method': ['DELETE'], 'function': check_repo},
655 requirements=URL_NAME_REQUIREMENTS)
655 requirements=URL_NAME_REQUIREMENTS)
656
656
657 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
657 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
658 controller='admin/repos', action='toggle_locking',
658 controller='admin/repos', action='toggle_locking',
659 conditions={'method': ['GET'], 'function': check_repo},
659 conditions={'method': ['GET'], 'function': check_repo},
660 requirements=URL_NAME_REQUIREMENTS)
660 requirements=URL_NAME_REQUIREMENTS)
661
661
662 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
662 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
663 controller='admin/repos', action='edit_remote_form',
663 controller='admin/repos', action='edit_remote_form',
664 conditions={'method': ['GET'], 'function': check_repo},
664 conditions={'method': ['GET'], 'function': check_repo},
665 requirements=URL_NAME_REQUIREMENTS)
665 requirements=URL_NAME_REQUIREMENTS)
666 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
666 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
667 controller='admin/repos', action='edit_remote',
667 controller='admin/repos', action='edit_remote',
668 conditions={'method': ['PUT'], 'function': check_repo},
668 conditions={'method': ['PUT'], 'function': check_repo},
669 requirements=URL_NAME_REQUIREMENTS)
669 requirements=URL_NAME_REQUIREMENTS)
670
670
671 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
671 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
672 controller='admin/repos', action='edit_statistics_form',
672 controller='admin/repos', action='edit_statistics_form',
673 conditions={'method': ['GET'], 'function': check_repo},
673 conditions={'method': ['GET'], 'function': check_repo},
674 requirements=URL_NAME_REQUIREMENTS)
674 requirements=URL_NAME_REQUIREMENTS)
675 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
675 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
676 controller='admin/repos', action='edit_statistics',
676 controller='admin/repos', action='edit_statistics',
677 conditions={'method': ['PUT'], 'function': check_repo},
677 conditions={'method': ['PUT'], 'function': check_repo},
678 requirements=URL_NAME_REQUIREMENTS)
678 requirements=URL_NAME_REQUIREMENTS)
679 rmap.connect('repo_settings_issuetracker',
679 rmap.connect('repo_settings_issuetracker',
680 '/{repo_name}/settings/issue-tracker',
680 '/{repo_name}/settings/issue-tracker',
681 controller='admin/repos', action='repo_issuetracker',
681 controller='admin/repos', action='repo_issuetracker',
682 conditions={'method': ['GET'], 'function': check_repo},
682 conditions={'method': ['GET'], 'function': check_repo},
683 requirements=URL_NAME_REQUIREMENTS)
683 requirements=URL_NAME_REQUIREMENTS)
684 rmap.connect('repo_issuetracker_test',
684 rmap.connect('repo_issuetracker_test',
685 '/{repo_name}/settings/issue-tracker/test',
685 '/{repo_name}/settings/issue-tracker/test',
686 controller='admin/repos', action='repo_issuetracker_test',
686 controller='admin/repos', action='repo_issuetracker_test',
687 conditions={'method': ['POST'], 'function': check_repo},
687 conditions={'method': ['POST'], 'function': check_repo},
688 requirements=URL_NAME_REQUIREMENTS)
688 requirements=URL_NAME_REQUIREMENTS)
689 rmap.connect('repo_issuetracker_delete',
689 rmap.connect('repo_issuetracker_delete',
690 '/{repo_name}/settings/issue-tracker/delete',
690 '/{repo_name}/settings/issue-tracker/delete',
691 controller='admin/repos', action='repo_issuetracker_delete',
691 controller='admin/repos', action='repo_issuetracker_delete',
692 conditions={'method': ['DELETE'], 'function': check_repo},
692 conditions={'method': ['DELETE'], 'function': check_repo},
693 requirements=URL_NAME_REQUIREMENTS)
693 requirements=URL_NAME_REQUIREMENTS)
694 rmap.connect('repo_issuetracker_save',
694 rmap.connect('repo_issuetracker_save',
695 '/{repo_name}/settings/issue-tracker/save',
695 '/{repo_name}/settings/issue-tracker/save',
696 controller='admin/repos', action='repo_issuetracker_save',
696 controller='admin/repos', action='repo_issuetracker_save',
697 conditions={'method': ['POST'], 'function': check_repo},
697 conditions={'method': ['POST'], 'function': check_repo},
698 requirements=URL_NAME_REQUIREMENTS)
698 requirements=URL_NAME_REQUIREMENTS)
699 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
699 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
700 controller='admin/repos', action='repo_settings_vcs_update',
700 controller='admin/repos', action='repo_settings_vcs_update',
701 conditions={'method': ['POST'], 'function': check_repo},
701 conditions={'method': ['POST'], 'function': check_repo},
702 requirements=URL_NAME_REQUIREMENTS)
702 requirements=URL_NAME_REQUIREMENTS)
703 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
703 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
704 controller='admin/repos', action='repo_settings_vcs',
704 controller='admin/repos', action='repo_settings_vcs',
705 conditions={'method': ['GET'], 'function': check_repo},
705 conditions={'method': ['GET'], 'function': check_repo},
706 requirements=URL_NAME_REQUIREMENTS)
706 requirements=URL_NAME_REQUIREMENTS)
707 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
707 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
708 controller='admin/repos', action='repo_delete_svn_pattern',
708 controller='admin/repos', action='repo_delete_svn_pattern',
709 conditions={'method': ['DELETE'], 'function': check_repo},
709 conditions={'method': ['DELETE'], 'function': check_repo},
710 requirements=URL_NAME_REQUIREMENTS)
710 requirements=URL_NAME_REQUIREMENTS)
711 rmap.connect('repo_pullrequest_settings', '/{repo_name}/settings/pullrequest',
711 rmap.connect('repo_pullrequest_settings', '/{repo_name}/settings/pullrequest',
712 controller='admin/repos', action='repo_settings_pullrequest',
712 controller='admin/repos', action='repo_settings_pullrequest',
713 conditions={'method': ['GET', 'POST'], 'function': check_repo},
713 conditions={'method': ['GET', 'POST'], 'function': check_repo},
714 requirements=URL_NAME_REQUIREMENTS)
714 requirements=URL_NAME_REQUIREMENTS)
715
715
716 # still working url for backward compat.
716 # still working url for backward compat.
717 rmap.connect('raw_changeset_home_depraced',
717 rmap.connect('raw_changeset_home_depraced',
718 '/{repo_name}/raw-changeset/{revision}',
718 '/{repo_name}/raw-changeset/{revision}',
719 controller='changeset', action='changeset_raw',
719 controller='changeset', action='changeset_raw',
720 revision='tip', conditions={'function': check_repo},
720 revision='tip', conditions={'function': check_repo},
721 requirements=URL_NAME_REQUIREMENTS)
721 requirements=URL_NAME_REQUIREMENTS)
722
722
723 # new URLs
723 # new URLs
724 rmap.connect('changeset_raw_home',
724 rmap.connect('changeset_raw_home',
725 '/{repo_name}/changeset-diff/{revision}',
725 '/{repo_name}/changeset-diff/{revision}',
726 controller='changeset', action='changeset_raw',
726 controller='changeset', action='changeset_raw',
727 revision='tip', conditions={'function': check_repo},
727 revision='tip', conditions={'function': check_repo},
728 requirements=URL_NAME_REQUIREMENTS)
728 requirements=URL_NAME_REQUIREMENTS)
729
729
730 rmap.connect('changeset_patch_home',
730 rmap.connect('changeset_patch_home',
731 '/{repo_name}/changeset-patch/{revision}',
731 '/{repo_name}/changeset-patch/{revision}',
732 controller='changeset', action='changeset_patch',
732 controller='changeset', action='changeset_patch',
733 revision='tip', conditions={'function': check_repo},
733 revision='tip', conditions={'function': check_repo},
734 requirements=URL_NAME_REQUIREMENTS)
734 requirements=URL_NAME_REQUIREMENTS)
735
735
736 rmap.connect('changeset_download_home',
736 rmap.connect('changeset_download_home',
737 '/{repo_name}/changeset-download/{revision}',
737 '/{repo_name}/changeset-download/{revision}',
738 controller='changeset', action='changeset_download',
738 controller='changeset', action='changeset_download',
739 revision='tip', conditions={'function': check_repo},
739 revision='tip', conditions={'function': check_repo},
740 requirements=URL_NAME_REQUIREMENTS)
740 requirements=URL_NAME_REQUIREMENTS)
741
741
742 rmap.connect('changeset_comment',
742 rmap.connect('changeset_comment',
743 '/{repo_name}/changeset/{revision}/comment', jsroute=True,
743 '/{repo_name}/changeset/{revision}/comment', jsroute=True,
744 controller='changeset', revision='tip', action='comment',
744 controller='changeset', revision='tip', action='comment',
745 conditions={'function': check_repo},
745 conditions={'function': check_repo},
746 requirements=URL_NAME_REQUIREMENTS)
746 requirements=URL_NAME_REQUIREMENTS)
747
747
748 rmap.connect('changeset_comment_preview',
748 rmap.connect('changeset_comment_preview',
749 '/{repo_name}/changeset/comment/preview', jsroute=True,
749 '/{repo_name}/changeset/comment/preview', jsroute=True,
750 controller='changeset', action='preview_comment',
750 controller='changeset', action='preview_comment',
751 conditions={'function': check_repo, 'method': ['POST']},
751 conditions={'function': check_repo, 'method': ['POST']},
752 requirements=URL_NAME_REQUIREMENTS)
752 requirements=URL_NAME_REQUIREMENTS)
753
753
754 rmap.connect('changeset_comment_delete',
754 rmap.connect('changeset_comment_delete',
755 '/{repo_name}/changeset/comment/{comment_id}/delete',
755 '/{repo_name}/changeset/comment/{comment_id}/delete',
756 controller='changeset', action='delete_comment',
756 controller='changeset', action='delete_comment',
757 conditions={'function': check_repo, 'method': ['DELETE']},
757 conditions={'function': check_repo, 'method': ['DELETE']},
758 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
758 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
759
759
760 rmap.connect('changeset_info', '/{repo_name}/changeset_info/{revision}',
760 rmap.connect('changeset_info', '/{repo_name}/changeset_info/{revision}',
761 controller='changeset', action='changeset_info',
761 controller='changeset', action='changeset_info',
762 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
762 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
763
763
764 rmap.connect('compare_home',
764 rmap.connect('compare_home',
765 '/{repo_name}/compare',
765 '/{repo_name}/compare',
766 controller='compare', action='index',
766 controller='compare', action='index',
767 conditions={'function': check_repo},
767 conditions={'function': check_repo},
768 requirements=URL_NAME_REQUIREMENTS)
768 requirements=URL_NAME_REQUIREMENTS)
769
769
770 rmap.connect('compare_url',
770 rmap.connect('compare_url',
771 '/{repo_name}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}',
771 '/{repo_name}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}',
772 controller='compare', action='compare',
772 controller='compare', action='compare',
773 conditions={'function': check_repo},
773 conditions={'function': check_repo},
774 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
774 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
775
775
776 rmap.connect('pullrequest_home',
776 rmap.connect('pullrequest_home',
777 '/{repo_name}/pull-request/new', controller='pullrequests',
777 '/{repo_name}/pull-request/new', controller='pullrequests',
778 action='index', conditions={'function': check_repo,
778 action='index', conditions={'function': check_repo,
779 'method': ['GET']},
779 'method': ['GET']},
780 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
780 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
781
781
782 rmap.connect('pullrequest',
782 rmap.connect('pullrequest',
783 '/{repo_name}/pull-request/new', controller='pullrequests',
783 '/{repo_name}/pull-request/new', controller='pullrequests',
784 action='create', conditions={'function': check_repo,
784 action='create', conditions={'function': check_repo,
785 'method': ['POST']},
785 'method': ['POST']},
786 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
786 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
787
787
788 rmap.connect('pullrequest_repo_refs',
788 rmap.connect('pullrequest_repo_refs',
789 '/{repo_name}/pull-request/refs/{target_repo_name:.*?[^/]}',
789 '/{repo_name}/pull-request/refs/{target_repo_name:.*?[^/]}',
790 controller='pullrequests',
790 controller='pullrequests',
791 action='get_repo_refs',
791 action='get_repo_refs',
792 conditions={'function': check_repo, 'method': ['GET']},
792 conditions={'function': check_repo, 'method': ['GET']},
793 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
793 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
794
794
795 rmap.connect('pullrequest_repo_destinations',
795 rmap.connect('pullrequest_repo_destinations',
796 '/{repo_name}/pull-request/repo-destinations',
796 '/{repo_name}/pull-request/repo-destinations',
797 controller='pullrequests',
797 controller='pullrequests',
798 action='get_repo_destinations',
798 action='get_repo_destinations',
799 conditions={'function': check_repo, 'method': ['GET']},
799 conditions={'function': check_repo, 'method': ['GET']},
800 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
800 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
801
801
802 rmap.connect('pullrequest_show',
802 rmap.connect('pullrequest_show',
803 '/{repo_name}/pull-request/{pull_request_id}',
803 '/{repo_name}/pull-request/{pull_request_id}',
804 controller='pullrequests',
804 controller='pullrequests',
805 action='show', conditions={'function': check_repo,
805 action='show', conditions={'function': check_repo,
806 'method': ['GET']},
806 'method': ['GET']},
807 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
807 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
808
808
809 rmap.connect('pullrequest_update',
809 rmap.connect('pullrequest_update',
810 '/{repo_name}/pull-request/{pull_request_id}',
810 '/{repo_name}/pull-request/{pull_request_id}',
811 controller='pullrequests',
811 controller='pullrequests',
812 action='update', conditions={'function': check_repo,
812 action='update', conditions={'function': check_repo,
813 'method': ['PUT']},
813 'method': ['PUT']},
814 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
814 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
815
815
816 rmap.connect('pullrequest_merge',
816 rmap.connect('pullrequest_merge',
817 '/{repo_name}/pull-request/{pull_request_id}',
817 '/{repo_name}/pull-request/{pull_request_id}',
818 controller='pullrequests',
818 controller='pullrequests',
819 action='merge', conditions={'function': check_repo,
819 action='merge', conditions={'function': check_repo,
820 'method': ['POST']},
820 'method': ['POST']},
821 requirements=URL_NAME_REQUIREMENTS)
821 requirements=URL_NAME_REQUIREMENTS)
822
822
823 rmap.connect('pullrequest_delete',
823 rmap.connect('pullrequest_delete',
824 '/{repo_name}/pull-request/{pull_request_id}',
824 '/{repo_name}/pull-request/{pull_request_id}',
825 controller='pullrequests',
825 controller='pullrequests',
826 action='delete', conditions={'function': check_repo,
826 action='delete', conditions={'function': check_repo,
827 'method': ['DELETE']},
827 'method': ['DELETE']},
828 requirements=URL_NAME_REQUIREMENTS)
828 requirements=URL_NAME_REQUIREMENTS)
829
829
830 rmap.connect('pullrequest_show_all',
831 '/{repo_name}/pull-request',
832 controller='pullrequests',
833 action='show_all', conditions={'function': check_repo,
834 'method': ['GET']},
835 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
836
837 rmap.connect('pullrequest_comment',
830 rmap.connect('pullrequest_comment',
838 '/{repo_name}/pull-request-comment/{pull_request_id}',
831 '/{repo_name}/pull-request-comment/{pull_request_id}',
839 controller='pullrequests',
832 controller='pullrequests',
840 action='comment', conditions={'function': check_repo,
833 action='comment', conditions={'function': check_repo,
841 'method': ['POST']},
834 'method': ['POST']},
842 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
835 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
843
836
844 rmap.connect('pullrequest_comment_delete',
837 rmap.connect('pullrequest_comment_delete',
845 '/{repo_name}/pull-request-comment/{comment_id}/delete',
838 '/{repo_name}/pull-request-comment/{comment_id}/delete',
846 controller='pullrequests', action='delete_comment',
839 controller='pullrequests', action='delete_comment',
847 conditions={'function': check_repo, 'method': ['DELETE']},
840 conditions={'function': check_repo, 'method': ['DELETE']},
848 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
841 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
849
842
850 rmap.connect('summary_home_explicit', '/{repo_name}/summary',
843 rmap.connect('summary_home_explicit', '/{repo_name}/summary',
851 controller='summary', conditions={'function': check_repo},
844 controller='summary', conditions={'function': check_repo},
852 requirements=URL_NAME_REQUIREMENTS)
845 requirements=URL_NAME_REQUIREMENTS)
853
846
854 rmap.connect('changelog_home', '/{repo_name}/changelog', jsroute=True,
847 rmap.connect('changelog_home', '/{repo_name}/changelog', jsroute=True,
855 controller='changelog', conditions={'function': check_repo},
848 controller='changelog', conditions={'function': check_repo},
856 requirements=URL_NAME_REQUIREMENTS)
849 requirements=URL_NAME_REQUIREMENTS)
857
850
858 rmap.connect('changelog_summary_home', '/{repo_name}/changelog_summary',
851 rmap.connect('changelog_summary_home', '/{repo_name}/changelog_summary',
859 controller='changelog', action='changelog_summary',
852 controller='changelog', action='changelog_summary',
860 conditions={'function': check_repo},
853 conditions={'function': check_repo},
861 requirements=URL_NAME_REQUIREMENTS)
854 requirements=URL_NAME_REQUIREMENTS)
862
855
863 rmap.connect('changelog_file_home',
856 rmap.connect('changelog_file_home',
864 '/{repo_name}/changelog/{revision}/{f_path}',
857 '/{repo_name}/changelog/{revision}/{f_path}',
865 controller='changelog', f_path=None,
858 controller='changelog', f_path=None,
866 conditions={'function': check_repo},
859 conditions={'function': check_repo},
867 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
860 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
868
861
869 rmap.connect('changelog_elements', '/{repo_name}/changelog_details',
862 rmap.connect('changelog_elements', '/{repo_name}/changelog_details',
870 controller='changelog', action='changelog_elements',
863 controller='changelog', action='changelog_elements',
871 conditions={'function': check_repo},
864 conditions={'function': check_repo},
872 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
865 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
873
866
874 rmap.connect('files_home', '/{repo_name}/files/{revision}/{f_path}',
867 rmap.connect('files_home', '/{repo_name}/files/{revision}/{f_path}',
875 controller='files', revision='tip', f_path='',
868 controller='files', revision='tip', f_path='',
876 conditions={'function': check_repo},
869 conditions={'function': check_repo},
877 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
870 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
878
871
879 rmap.connect('files_home_simple_catchrev',
872 rmap.connect('files_home_simple_catchrev',
880 '/{repo_name}/files/{revision}',
873 '/{repo_name}/files/{revision}',
881 controller='files', revision='tip', f_path='',
874 controller='files', revision='tip', f_path='',
882 conditions={'function': check_repo},
875 conditions={'function': check_repo},
883 requirements=URL_NAME_REQUIREMENTS)
876 requirements=URL_NAME_REQUIREMENTS)
884
877
885 rmap.connect('files_home_simple_catchall',
878 rmap.connect('files_home_simple_catchall',
886 '/{repo_name}/files',
879 '/{repo_name}/files',
887 controller='files', revision='tip', f_path='',
880 controller='files', revision='tip', f_path='',
888 conditions={'function': check_repo},
881 conditions={'function': check_repo},
889 requirements=URL_NAME_REQUIREMENTS)
882 requirements=URL_NAME_REQUIREMENTS)
890
883
891 rmap.connect('files_history_home',
884 rmap.connect('files_history_home',
892 '/{repo_name}/history/{revision}/{f_path}',
885 '/{repo_name}/history/{revision}/{f_path}',
893 controller='files', action='history', revision='tip', f_path='',
886 controller='files', action='history', revision='tip', f_path='',
894 conditions={'function': check_repo},
887 conditions={'function': check_repo},
895 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
888 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
896
889
897 rmap.connect('files_authors_home',
890 rmap.connect('files_authors_home',
898 '/{repo_name}/authors/{revision}/{f_path}',
891 '/{repo_name}/authors/{revision}/{f_path}',
899 controller='files', action='authors', revision='tip', f_path='',
892 controller='files', action='authors', revision='tip', f_path='',
900 conditions={'function': check_repo},
893 conditions={'function': check_repo},
901 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
894 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
902
895
903 rmap.connect('files_diff_home', '/{repo_name}/diff/{f_path}',
896 rmap.connect('files_diff_home', '/{repo_name}/diff/{f_path}',
904 controller='files', action='diff', f_path='',
897 controller='files', action='diff', f_path='',
905 conditions={'function': check_repo},
898 conditions={'function': check_repo},
906 requirements=URL_NAME_REQUIREMENTS)
899 requirements=URL_NAME_REQUIREMENTS)
907
900
908 rmap.connect('files_diff_2way_home',
901 rmap.connect('files_diff_2way_home',
909 '/{repo_name}/diff-2way/{f_path}',
902 '/{repo_name}/diff-2way/{f_path}',
910 controller='files', action='diff_2way', f_path='',
903 controller='files', action='diff_2way', f_path='',
911 conditions={'function': check_repo},
904 conditions={'function': check_repo},
912 requirements=URL_NAME_REQUIREMENTS)
905 requirements=URL_NAME_REQUIREMENTS)
913
906
914 rmap.connect('files_rawfile_home',
907 rmap.connect('files_rawfile_home',
915 '/{repo_name}/rawfile/{revision}/{f_path}',
908 '/{repo_name}/rawfile/{revision}/{f_path}',
916 controller='files', action='rawfile', revision='tip',
909 controller='files', action='rawfile', revision='tip',
917 f_path='', conditions={'function': check_repo},
910 f_path='', conditions={'function': check_repo},
918 requirements=URL_NAME_REQUIREMENTS)
911 requirements=URL_NAME_REQUIREMENTS)
919
912
920 rmap.connect('files_raw_home',
913 rmap.connect('files_raw_home',
921 '/{repo_name}/raw/{revision}/{f_path}',
914 '/{repo_name}/raw/{revision}/{f_path}',
922 controller='files', action='raw', revision='tip', f_path='',
915 controller='files', action='raw', revision='tip', f_path='',
923 conditions={'function': check_repo},
916 conditions={'function': check_repo},
924 requirements=URL_NAME_REQUIREMENTS)
917 requirements=URL_NAME_REQUIREMENTS)
925
918
926 rmap.connect('files_render_home',
919 rmap.connect('files_render_home',
927 '/{repo_name}/render/{revision}/{f_path}',
920 '/{repo_name}/render/{revision}/{f_path}',
928 controller='files', action='index', revision='tip', f_path='',
921 controller='files', action='index', revision='tip', f_path='',
929 rendered=True, conditions={'function': check_repo},
922 rendered=True, conditions={'function': check_repo},
930 requirements=URL_NAME_REQUIREMENTS)
923 requirements=URL_NAME_REQUIREMENTS)
931
924
932 rmap.connect('files_annotate_home',
925 rmap.connect('files_annotate_home',
933 '/{repo_name}/annotate/{revision}/{f_path}',
926 '/{repo_name}/annotate/{revision}/{f_path}',
934 controller='files', action='index', revision='tip',
927 controller='files', action='index', revision='tip',
935 f_path='', annotate=True, conditions={'function': check_repo},
928 f_path='', annotate=True, conditions={'function': check_repo},
936 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
929 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
937
930
938 rmap.connect('files_annotate_previous',
931 rmap.connect('files_annotate_previous',
939 '/{repo_name}/annotate-previous/{revision}/{f_path}',
932 '/{repo_name}/annotate-previous/{revision}/{f_path}',
940 controller='files', action='annotate_previous', revision='tip',
933 controller='files', action='annotate_previous', revision='tip',
941 f_path='', annotate=True, conditions={'function': check_repo},
934 f_path='', annotate=True, conditions={'function': check_repo},
942 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
935 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
943
936
944 rmap.connect('files_edit',
937 rmap.connect('files_edit',
945 '/{repo_name}/edit/{revision}/{f_path}',
938 '/{repo_name}/edit/{revision}/{f_path}',
946 controller='files', action='edit', revision='tip',
939 controller='files', action='edit', revision='tip',
947 f_path='',
940 f_path='',
948 conditions={'function': check_repo, 'method': ['POST']},
941 conditions={'function': check_repo, 'method': ['POST']},
949 requirements=URL_NAME_REQUIREMENTS)
942 requirements=URL_NAME_REQUIREMENTS)
950
943
951 rmap.connect('files_edit_home',
944 rmap.connect('files_edit_home',
952 '/{repo_name}/edit/{revision}/{f_path}',
945 '/{repo_name}/edit/{revision}/{f_path}',
953 controller='files', action='edit_home', revision='tip',
946 controller='files', action='edit_home', revision='tip',
954 f_path='', conditions={'function': check_repo},
947 f_path='', conditions={'function': check_repo},
955 requirements=URL_NAME_REQUIREMENTS)
948 requirements=URL_NAME_REQUIREMENTS)
956
949
957 rmap.connect('files_add',
950 rmap.connect('files_add',
958 '/{repo_name}/add/{revision}/{f_path}',
951 '/{repo_name}/add/{revision}/{f_path}',
959 controller='files', action='add', revision='tip',
952 controller='files', action='add', revision='tip',
960 f_path='',
953 f_path='',
961 conditions={'function': check_repo, 'method': ['POST']},
954 conditions={'function': check_repo, 'method': ['POST']},
962 requirements=URL_NAME_REQUIREMENTS)
955 requirements=URL_NAME_REQUIREMENTS)
963
956
964 rmap.connect('files_add_home',
957 rmap.connect('files_add_home',
965 '/{repo_name}/add/{revision}/{f_path}',
958 '/{repo_name}/add/{revision}/{f_path}',
966 controller='files', action='add_home', revision='tip',
959 controller='files', action='add_home', revision='tip',
967 f_path='', conditions={'function': check_repo},
960 f_path='', conditions={'function': check_repo},
968 requirements=URL_NAME_REQUIREMENTS)
961 requirements=URL_NAME_REQUIREMENTS)
969
962
970 rmap.connect('files_delete',
963 rmap.connect('files_delete',
971 '/{repo_name}/delete/{revision}/{f_path}',
964 '/{repo_name}/delete/{revision}/{f_path}',
972 controller='files', action='delete', revision='tip',
965 controller='files', action='delete', revision='tip',
973 f_path='',
966 f_path='',
974 conditions={'function': check_repo, 'method': ['POST']},
967 conditions={'function': check_repo, 'method': ['POST']},
975 requirements=URL_NAME_REQUIREMENTS)
968 requirements=URL_NAME_REQUIREMENTS)
976
969
977 rmap.connect('files_delete_home',
970 rmap.connect('files_delete_home',
978 '/{repo_name}/delete/{revision}/{f_path}',
971 '/{repo_name}/delete/{revision}/{f_path}',
979 controller='files', action='delete_home', revision='tip',
972 controller='files', action='delete_home', revision='tip',
980 f_path='', conditions={'function': check_repo},
973 f_path='', conditions={'function': check_repo},
981 requirements=URL_NAME_REQUIREMENTS)
974 requirements=URL_NAME_REQUIREMENTS)
982
975
983 rmap.connect('files_archive_home', '/{repo_name}/archive/{fname}',
976 rmap.connect('files_archive_home', '/{repo_name}/archive/{fname}',
984 controller='files', action='archivefile',
977 controller='files', action='archivefile',
985 conditions={'function': check_repo},
978 conditions={'function': check_repo},
986 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
979 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
987
980
988 rmap.connect('files_nodelist_home',
981 rmap.connect('files_nodelist_home',
989 '/{repo_name}/nodelist/{revision}/{f_path}',
982 '/{repo_name}/nodelist/{revision}/{f_path}',
990 controller='files', action='nodelist',
983 controller='files', action='nodelist',
991 conditions={'function': check_repo},
984 conditions={'function': check_repo},
992 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
985 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
993
986
994 rmap.connect('files_nodetree_full',
987 rmap.connect('files_nodetree_full',
995 '/{repo_name}/nodetree_full/{commit_id}/{f_path}',
988 '/{repo_name}/nodetree_full/{commit_id}/{f_path}',
996 controller='files', action='nodetree_full',
989 controller='files', action='nodetree_full',
997 conditions={'function': check_repo},
990 conditions={'function': check_repo},
998 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
991 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
999
992
1000 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
993 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
1001 controller='forks', action='fork_create',
994 controller='forks', action='fork_create',
1002 conditions={'function': check_repo, 'method': ['POST']},
995 conditions={'function': check_repo, 'method': ['POST']},
1003 requirements=URL_NAME_REQUIREMENTS)
996 requirements=URL_NAME_REQUIREMENTS)
1004
997
1005 rmap.connect('repo_fork_home', '/{repo_name}/fork',
998 rmap.connect('repo_fork_home', '/{repo_name}/fork',
1006 controller='forks', action='fork',
999 controller='forks', action='fork',
1007 conditions={'function': check_repo},
1000 conditions={'function': check_repo},
1008 requirements=URL_NAME_REQUIREMENTS)
1001 requirements=URL_NAME_REQUIREMENTS)
1009
1002
1010 rmap.connect('repo_forks_home', '/{repo_name}/forks',
1003 rmap.connect('repo_forks_home', '/{repo_name}/forks',
1011 controller='forks', action='forks',
1004 controller='forks', action='forks',
1012 conditions={'function': check_repo},
1005 conditions={'function': check_repo},
1013 requirements=URL_NAME_REQUIREMENTS)
1006 requirements=URL_NAME_REQUIREMENTS)
1014
1007
1015 # must be here for proper group/repo catching pattern
1008 # must be here for proper group/repo catching pattern
1016 _connect_with_slash(
1009 _connect_with_slash(
1017 rmap, 'repo_group_home', '/{group_name}',
1010 rmap, 'repo_group_home', '/{group_name}',
1018 controller='home', action='index_repo_group',
1011 controller='home', action='index_repo_group',
1019 conditions={'function': check_group},
1012 conditions={'function': check_group},
1020 requirements=URL_NAME_REQUIREMENTS)
1013 requirements=URL_NAME_REQUIREMENTS)
1021
1014
1022 # catch all, at the end
1015 # catch all, at the end
1023 _connect_with_slash(
1016 _connect_with_slash(
1024 rmap, 'summary_home', '/{repo_name}', jsroute=True,
1017 rmap, 'summary_home', '/{repo_name}', jsroute=True,
1025 controller='summary', action='index',
1018 controller='summary', action='index',
1026 conditions={'function': check_repo},
1019 conditions={'function': check_repo},
1027 requirements=URL_NAME_REQUIREMENTS)
1020 requirements=URL_NAME_REQUIREMENTS)
1028
1021
1029 return rmap
1022 return rmap
1030
1023
1031
1024
1032 def _connect_with_slash(mapper, name, path, *args, **kwargs):
1025 def _connect_with_slash(mapper, name, path, *args, **kwargs):
1033 """
1026 """
1034 Connect a route with an optional trailing slash in `path`.
1027 Connect a route with an optional trailing slash in `path`.
1035 """
1028 """
1036 mapper.connect(name + '_slash', path + '/', *args, **kwargs)
1029 mapper.connect(name + '_slash', path + '/', *args, **kwargs)
1037 mapper.connect(name, path, *args, **kwargs)
1030 mapper.connect(name, path, *args, **kwargs)
@@ -1,1096 +1,978 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2017 RhodeCode GmbH
3 # Copyright (C) 2012-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
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
20
21 """
21 """
22 pull requests controller for rhodecode for initializing pull requests
22 pull requests controller for rhodecode for initializing pull requests
23 """
23 """
24 import types
24 import types
25
25
26 import peppercorn
26 import peppercorn
27 import formencode
27 import formencode
28 import logging
28 import logging
29 import collections
29 import collections
30
30
31 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPBadRequest
31 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPBadRequest
32 from pylons import request, tmpl_context as c, url
32 from pylons import request, tmpl_context as c, url
33 from pylons.controllers.util import redirect
33 from pylons.controllers.util import redirect
34 from pylons.i18n.translation import _
34 from pylons.i18n.translation import _
35 from pyramid.threadlocal import get_current_registry
35 from pyramid.threadlocal import get_current_registry
36 from sqlalchemy.sql import func
36 from sqlalchemy.sql import func
37 from sqlalchemy.sql.expression import or_
37 from sqlalchemy.sql.expression import or_
38
38
39 from rhodecode import events
39 from rhodecode import events
40 from rhodecode.lib import auth, diffs, helpers as h, codeblocks
40 from rhodecode.lib import auth, diffs, helpers as h, codeblocks
41 from rhodecode.lib.ext_json import json
41 from rhodecode.lib.ext_json import json
42 from rhodecode.lib.base import (
42 from rhodecode.lib.base import (
43 BaseRepoController, render, vcs_operation_context)
43 BaseRepoController, render, vcs_operation_context)
44 from rhodecode.lib.auth import (
44 from rhodecode.lib.auth import (
45 LoginRequired, HasRepoPermissionAnyDecorator, NotAnonymous,
45 LoginRequired, HasRepoPermissionAnyDecorator, NotAnonymous,
46 HasAcceptedRepoType, XHRRequired)
46 HasAcceptedRepoType, XHRRequired)
47 from rhodecode.lib.channelstream import channelstream_request
47 from rhodecode.lib.channelstream import channelstream_request
48 from rhodecode.lib.utils import jsonify
48 from rhodecode.lib.utils import jsonify
49 from rhodecode.lib.utils2 import (
49 from rhodecode.lib.utils2 import (
50 safe_int, safe_str, str2bool, safe_unicode)
50 safe_int, safe_str, str2bool, safe_unicode)
51 from rhodecode.lib.vcs.backends.base import (
51 from rhodecode.lib.vcs.backends.base import (
52 EmptyCommit, UpdateFailureReason, EmptyRepository)
52 EmptyCommit, UpdateFailureReason, EmptyRepository)
53 from rhodecode.lib.vcs.exceptions import (
53 from rhodecode.lib.vcs.exceptions import (
54 EmptyRepositoryError, CommitDoesNotExistError, RepositoryRequirementError,
54 EmptyRepositoryError, CommitDoesNotExistError, RepositoryRequirementError,
55 NodeDoesNotExistError)
55 NodeDoesNotExistError)
56
56
57 from rhodecode.model.changeset_status import ChangesetStatusModel
57 from rhodecode.model.changeset_status import ChangesetStatusModel
58 from rhodecode.model.comment import CommentsModel
58 from rhodecode.model.comment import CommentsModel
59 from rhodecode.model.db import (PullRequest, ChangesetStatus, ChangesetComment,
59 from rhodecode.model.db import (PullRequest, ChangesetStatus, ChangesetComment,
60 Repository, PullRequestVersion)
60 Repository, PullRequestVersion)
61 from rhodecode.model.forms import PullRequestForm
61 from rhodecode.model.forms import PullRequestForm
62 from rhodecode.model.meta import Session
62 from rhodecode.model.meta import Session
63 from rhodecode.model.pull_request import PullRequestModel, MergeCheck
63 from rhodecode.model.pull_request import PullRequestModel, MergeCheck
64
64
65 log = logging.getLogger(__name__)
65 log = logging.getLogger(__name__)
66
66
67
67
68 class PullrequestsController(BaseRepoController):
68 class PullrequestsController(BaseRepoController):
69
69
70 def __before__(self):
70 def __before__(self):
71 super(PullrequestsController, self).__before__()
71 super(PullrequestsController, self).__before__()
72 c.REVIEW_STATUS_APPROVED = ChangesetStatus.STATUS_APPROVED
72 c.REVIEW_STATUS_APPROVED = ChangesetStatus.STATUS_APPROVED
73 c.REVIEW_STATUS_REJECTED = ChangesetStatus.STATUS_REJECTED
73 c.REVIEW_STATUS_REJECTED = ChangesetStatus.STATUS_REJECTED
74
74
75 def _extract_ordering(self, request):
76 column_index = safe_int(request.GET.get('order[0][column]'))
77 order_dir = request.GET.get('order[0][dir]', 'desc')
78 order_by = request.GET.get(
79 'columns[%s][data][sort]' % column_index, 'name_raw')
80 return order_by, order_dir
81
82 @LoginRequired()
83 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
84 'repository.admin')
85 @HasAcceptedRepoType('git', 'hg')
86 def show_all(self, repo_name):
87 # filter types
88 c.active = 'open'
89 c.source = str2bool(request.GET.get('source'))
90 c.closed = str2bool(request.GET.get('closed'))
91 c.my = str2bool(request.GET.get('my'))
92 c.awaiting_review = str2bool(request.GET.get('awaiting_review'))
93 c.awaiting_my_review = str2bool(request.GET.get('awaiting_my_review'))
94 c.repo_name = repo_name
95
96 opened_by = None
97 if c.my:
98 c.active = 'my'
99 opened_by = [c.rhodecode_user.user_id]
100
101 statuses = [PullRequest.STATUS_NEW, PullRequest.STATUS_OPEN]
102 if c.closed:
103 c.active = 'closed'
104 statuses = [PullRequest.STATUS_CLOSED]
105
106 if c.awaiting_review and not c.source:
107 c.active = 'awaiting'
108 if c.source and not c.awaiting_review:
109 c.active = 'source'
110 if c.awaiting_my_review:
111 c.active = 'awaiting_my'
112
113 data = self._get_pull_requests_list(
114 repo_name=repo_name, opened_by=opened_by, statuses=statuses)
115 if not request.is_xhr:
116 c.data = json.dumps(data['data'])
117 c.records_total = data['recordsTotal']
118 return render('/pullrequests/pullrequests.mako')
119 else:
120 return json.dumps(data)
121
122 def _get_pull_requests_list(self, repo_name, opened_by, statuses):
123 # pagination
124 start = safe_int(request.GET.get('start'), 0)
125 length = safe_int(request.GET.get('length'), c.visual.dashboard_items)
126 order_by, order_dir = self._extract_ordering(request)
127
128 if c.awaiting_review:
129 pull_requests = PullRequestModel().get_awaiting_review(
130 repo_name, source=c.source, opened_by=opened_by,
131 statuses=statuses, offset=start, length=length,
132 order_by=order_by, order_dir=order_dir)
133 pull_requests_total_count = PullRequestModel(
134 ).count_awaiting_review(
135 repo_name, source=c.source, statuses=statuses,
136 opened_by=opened_by)
137 elif c.awaiting_my_review:
138 pull_requests = PullRequestModel().get_awaiting_my_review(
139 repo_name, source=c.source, opened_by=opened_by,
140 user_id=c.rhodecode_user.user_id, statuses=statuses,
141 offset=start, length=length, order_by=order_by,
142 order_dir=order_dir)
143 pull_requests_total_count = PullRequestModel(
144 ).count_awaiting_my_review(
145 repo_name, source=c.source, user_id=c.rhodecode_user.user_id,
146 statuses=statuses, opened_by=opened_by)
147 else:
148 pull_requests = PullRequestModel().get_all(
149 repo_name, source=c.source, opened_by=opened_by,
150 statuses=statuses, offset=start, length=length,
151 order_by=order_by, order_dir=order_dir)
152 pull_requests_total_count = PullRequestModel().count_all(
153 repo_name, source=c.source, statuses=statuses,
154 opened_by=opened_by)
155
156 from rhodecode.lib.utils import PartialRenderer
157 _render = PartialRenderer('data_table/_dt_elements.mako')
158 data = []
159 for pr in pull_requests:
160 comments = CommentsModel().get_all_comments(
161 c.rhodecode_db_repo.repo_id, pull_request=pr)
162
163 data.append({
164 'name': _render('pullrequest_name',
165 pr.pull_request_id, pr.target_repo.repo_name),
166 'name_raw': pr.pull_request_id,
167 'status': _render('pullrequest_status',
168 pr.calculated_review_status()),
169 'title': _render(
170 'pullrequest_title', pr.title, pr.description),
171 'description': h.escape(pr.description),
172 'updated_on': _render('pullrequest_updated_on',
173 h.datetime_to_time(pr.updated_on)),
174 'updated_on_raw': h.datetime_to_time(pr.updated_on),
175 'created_on': _render('pullrequest_updated_on',
176 h.datetime_to_time(pr.created_on)),
177 'created_on_raw': h.datetime_to_time(pr.created_on),
178 'author': _render('pullrequest_author',
179 pr.author.full_contact, ),
180 'author_raw': pr.author.full_name,
181 'comments': _render('pullrequest_comments', len(comments)),
182 'comments_raw': len(comments),
183 'closed': pr.is_closed(),
184 })
185 # json used to render the grid
186 data = ({
187 'data': data,
188 'recordsTotal': pull_requests_total_count,
189 'recordsFiltered': pull_requests_total_count,
190 })
191 return data
192
193 @LoginRequired()
75 @LoginRequired()
194 @NotAnonymous()
76 @NotAnonymous()
195 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
77 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
196 'repository.admin')
78 'repository.admin')
197 @HasAcceptedRepoType('git', 'hg')
79 @HasAcceptedRepoType('git', 'hg')
198 def index(self):
80 def index(self):
199 source_repo = c.rhodecode_db_repo
81 source_repo = c.rhodecode_db_repo
200
82
201 try:
83 try:
202 source_repo.scm_instance().get_commit()
84 source_repo.scm_instance().get_commit()
203 except EmptyRepositoryError:
85 except EmptyRepositoryError:
204 h.flash(h.literal(_('There are no commits yet')),
86 h.flash(h.literal(_('There are no commits yet')),
205 category='warning')
87 category='warning')
206 redirect(url('summary_home', repo_name=source_repo.repo_name))
88 redirect(url('summary_home', repo_name=source_repo.repo_name))
207
89
208 commit_id = request.GET.get('commit')
90 commit_id = request.GET.get('commit')
209 branch_ref = request.GET.get('branch')
91 branch_ref = request.GET.get('branch')
210 bookmark_ref = request.GET.get('bookmark')
92 bookmark_ref = request.GET.get('bookmark')
211
93
212 try:
94 try:
213 source_repo_data = PullRequestModel().generate_repo_data(
95 source_repo_data = PullRequestModel().generate_repo_data(
214 source_repo, commit_id=commit_id,
96 source_repo, commit_id=commit_id,
215 branch=branch_ref, bookmark=bookmark_ref)
97 branch=branch_ref, bookmark=bookmark_ref)
216 except CommitDoesNotExistError as e:
98 except CommitDoesNotExistError as e:
217 log.exception(e)
99 log.exception(e)
218 h.flash(_('Commit does not exist'), 'error')
100 h.flash(_('Commit does not exist'), 'error')
219 redirect(url('pullrequest_home', repo_name=source_repo.repo_name))
101 redirect(url('pullrequest_home', repo_name=source_repo.repo_name))
220
102
221 default_target_repo = source_repo
103 default_target_repo = source_repo
222
104
223 if source_repo.parent:
105 if source_repo.parent:
224 parent_vcs_obj = source_repo.parent.scm_instance()
106 parent_vcs_obj = source_repo.parent.scm_instance()
225 if parent_vcs_obj and not parent_vcs_obj.is_empty():
107 if parent_vcs_obj and not parent_vcs_obj.is_empty():
226 # change default if we have a parent repo
108 # change default if we have a parent repo
227 default_target_repo = source_repo.parent
109 default_target_repo = source_repo.parent
228
110
229 target_repo_data = PullRequestModel().generate_repo_data(
111 target_repo_data = PullRequestModel().generate_repo_data(
230 default_target_repo)
112 default_target_repo)
231
113
232 selected_source_ref = source_repo_data['refs']['selected_ref']
114 selected_source_ref = source_repo_data['refs']['selected_ref']
233
115
234 title_source_ref = selected_source_ref.split(':', 2)[1]
116 title_source_ref = selected_source_ref.split(':', 2)[1]
235 c.default_title = PullRequestModel().generate_pullrequest_title(
117 c.default_title = PullRequestModel().generate_pullrequest_title(
236 source=source_repo.repo_name,
118 source=source_repo.repo_name,
237 source_ref=title_source_ref,
119 source_ref=title_source_ref,
238 target=default_target_repo.repo_name
120 target=default_target_repo.repo_name
239 )
121 )
240
122
241 c.default_repo_data = {
123 c.default_repo_data = {
242 'source_repo_name': source_repo.repo_name,
124 'source_repo_name': source_repo.repo_name,
243 'source_refs_json': json.dumps(source_repo_data),
125 'source_refs_json': json.dumps(source_repo_data),
244 'target_repo_name': default_target_repo.repo_name,
126 'target_repo_name': default_target_repo.repo_name,
245 'target_refs_json': json.dumps(target_repo_data),
127 'target_refs_json': json.dumps(target_repo_data),
246 }
128 }
247 c.default_source_ref = selected_source_ref
129 c.default_source_ref = selected_source_ref
248
130
249 return render('/pullrequests/pullrequest.mako')
131 return render('/pullrequests/pullrequest.mako')
250
132
251 @LoginRequired()
133 @LoginRequired()
252 @NotAnonymous()
134 @NotAnonymous()
253 @XHRRequired()
135 @XHRRequired()
254 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
136 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
255 'repository.admin')
137 'repository.admin')
256 @jsonify
138 @jsonify
257 def get_repo_refs(self, repo_name, target_repo_name):
139 def get_repo_refs(self, repo_name, target_repo_name):
258 repo = Repository.get_by_repo_name(target_repo_name)
140 repo = Repository.get_by_repo_name(target_repo_name)
259 if not repo:
141 if not repo:
260 raise HTTPNotFound
142 raise HTTPNotFound
261 return PullRequestModel().generate_repo_data(repo)
143 return PullRequestModel().generate_repo_data(repo)
262
144
263 @LoginRequired()
145 @LoginRequired()
264 @NotAnonymous()
146 @NotAnonymous()
265 @XHRRequired()
147 @XHRRequired()
266 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
148 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
267 'repository.admin')
149 'repository.admin')
268 @jsonify
150 @jsonify
269 def get_repo_destinations(self, repo_name):
151 def get_repo_destinations(self, repo_name):
270 repo = Repository.get_by_repo_name(repo_name)
152 repo = Repository.get_by_repo_name(repo_name)
271 if not repo:
153 if not repo:
272 raise HTTPNotFound
154 raise HTTPNotFound
273 filter_query = request.GET.get('query')
155 filter_query = request.GET.get('query')
274
156
275 query = Repository.query() \
157 query = Repository.query() \
276 .order_by(func.length(Repository.repo_name)) \
158 .order_by(func.length(Repository.repo_name)) \
277 .filter(or_(
159 .filter(or_(
278 Repository.repo_name == repo.repo_name,
160 Repository.repo_name == repo.repo_name,
279 Repository.fork_id == repo.repo_id))
161 Repository.fork_id == repo.repo_id))
280
162
281 if filter_query:
163 if filter_query:
282 ilike_expression = u'%{}%'.format(safe_unicode(filter_query))
164 ilike_expression = u'%{}%'.format(safe_unicode(filter_query))
283 query = query.filter(
165 query = query.filter(
284 Repository.repo_name.ilike(ilike_expression))
166 Repository.repo_name.ilike(ilike_expression))
285
167
286 add_parent = False
168 add_parent = False
287 if repo.parent:
169 if repo.parent:
288 if filter_query in repo.parent.repo_name:
170 if filter_query in repo.parent.repo_name:
289 parent_vcs_obj = repo.parent.scm_instance()
171 parent_vcs_obj = repo.parent.scm_instance()
290 if parent_vcs_obj and not parent_vcs_obj.is_empty():
172 if parent_vcs_obj and not parent_vcs_obj.is_empty():
291 add_parent = True
173 add_parent = True
292
174
293 limit = 20 - 1 if add_parent else 20
175 limit = 20 - 1 if add_parent else 20
294 all_repos = query.limit(limit).all()
176 all_repos = query.limit(limit).all()
295 if add_parent:
177 if add_parent:
296 all_repos += [repo.parent]
178 all_repos += [repo.parent]
297
179
298 repos = []
180 repos = []
299 for obj in self.scm_model.get_repos(all_repos):
181 for obj in self.scm_model.get_repos(all_repos):
300 repos.append({
182 repos.append({
301 'id': obj['name'],
183 'id': obj['name'],
302 'text': obj['name'],
184 'text': obj['name'],
303 'type': 'repo',
185 'type': 'repo',
304 'obj': obj['dbrepo']
186 'obj': obj['dbrepo']
305 })
187 })
306
188
307 data = {
189 data = {
308 'more': False,
190 'more': False,
309 'results': [{
191 'results': [{
310 'text': _('Repositories'),
192 'text': _('Repositories'),
311 'children': repos
193 'children': repos
312 }] if repos else []
194 }] if repos else []
313 }
195 }
314 return data
196 return data
315
197
316 @LoginRequired()
198 @LoginRequired()
317 @NotAnonymous()
199 @NotAnonymous()
318 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
200 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
319 'repository.admin')
201 'repository.admin')
320 @HasAcceptedRepoType('git', 'hg')
202 @HasAcceptedRepoType('git', 'hg')
321 @auth.CSRFRequired()
203 @auth.CSRFRequired()
322 def create(self, repo_name):
204 def create(self, repo_name):
323 repo = Repository.get_by_repo_name(repo_name)
205 repo = Repository.get_by_repo_name(repo_name)
324 if not repo:
206 if not repo:
325 raise HTTPNotFound
207 raise HTTPNotFound
326
208
327 controls = peppercorn.parse(request.POST.items())
209 controls = peppercorn.parse(request.POST.items())
328
210
329 try:
211 try:
330 _form = PullRequestForm(repo.repo_id)().to_python(controls)
212 _form = PullRequestForm(repo.repo_id)().to_python(controls)
331 except formencode.Invalid as errors:
213 except formencode.Invalid as errors:
332 if errors.error_dict.get('revisions'):
214 if errors.error_dict.get('revisions'):
333 msg = 'Revisions: %s' % errors.error_dict['revisions']
215 msg = 'Revisions: %s' % errors.error_dict['revisions']
334 elif errors.error_dict.get('pullrequest_title'):
216 elif errors.error_dict.get('pullrequest_title'):
335 msg = _('Pull request requires a title with min. 3 chars')
217 msg = _('Pull request requires a title with min. 3 chars')
336 else:
218 else:
337 msg = _('Error creating pull request: {}').format(errors)
219 msg = _('Error creating pull request: {}').format(errors)
338 log.exception(msg)
220 log.exception(msg)
339 h.flash(msg, 'error')
221 h.flash(msg, 'error')
340
222
341 # would rather just go back to form ...
223 # would rather just go back to form ...
342 return redirect(url('pullrequest_home', repo_name=repo_name))
224 return redirect(url('pullrequest_home', repo_name=repo_name))
343
225
344 source_repo = _form['source_repo']
226 source_repo = _form['source_repo']
345 source_ref = _form['source_ref']
227 source_ref = _form['source_ref']
346 target_repo = _form['target_repo']
228 target_repo = _form['target_repo']
347 target_ref = _form['target_ref']
229 target_ref = _form['target_ref']
348 commit_ids = _form['revisions'][::-1]
230 commit_ids = _form['revisions'][::-1]
349 reviewers = [
231 reviewers = [
350 (r['user_id'], r['reasons']) for r in _form['review_members']]
232 (r['user_id'], r['reasons']) for r in _form['review_members']]
351
233
352 # find the ancestor for this pr
234 # find the ancestor for this pr
353 source_db_repo = Repository.get_by_repo_name(_form['source_repo'])
235 source_db_repo = Repository.get_by_repo_name(_form['source_repo'])
354 target_db_repo = Repository.get_by_repo_name(_form['target_repo'])
236 target_db_repo = Repository.get_by_repo_name(_form['target_repo'])
355
237
356 source_scm = source_db_repo.scm_instance()
238 source_scm = source_db_repo.scm_instance()
357 target_scm = target_db_repo.scm_instance()
239 target_scm = target_db_repo.scm_instance()
358
240
359 source_commit = source_scm.get_commit(source_ref.split(':')[-1])
241 source_commit = source_scm.get_commit(source_ref.split(':')[-1])
360 target_commit = target_scm.get_commit(target_ref.split(':')[-1])
242 target_commit = target_scm.get_commit(target_ref.split(':')[-1])
361
243
362 ancestor = source_scm.get_common_ancestor(
244 ancestor = source_scm.get_common_ancestor(
363 source_commit.raw_id, target_commit.raw_id, target_scm)
245 source_commit.raw_id, target_commit.raw_id, target_scm)
364
246
365 target_ref_type, target_ref_name, __ = _form['target_ref'].split(':')
247 target_ref_type, target_ref_name, __ = _form['target_ref'].split(':')
366 target_ref = ':'.join((target_ref_type, target_ref_name, ancestor))
248 target_ref = ':'.join((target_ref_type, target_ref_name, ancestor))
367
249
368 pullrequest_title = _form['pullrequest_title']
250 pullrequest_title = _form['pullrequest_title']
369 title_source_ref = source_ref.split(':', 2)[1]
251 title_source_ref = source_ref.split(':', 2)[1]
370 if not pullrequest_title:
252 if not pullrequest_title:
371 pullrequest_title = PullRequestModel().generate_pullrequest_title(
253 pullrequest_title = PullRequestModel().generate_pullrequest_title(
372 source=source_repo,
254 source=source_repo,
373 source_ref=title_source_ref,
255 source_ref=title_source_ref,
374 target=target_repo
256 target=target_repo
375 )
257 )
376
258
377 description = _form['pullrequest_desc']
259 description = _form['pullrequest_desc']
378 try:
260 try:
379 pull_request = PullRequestModel().create(
261 pull_request = PullRequestModel().create(
380 c.rhodecode_user.user_id, source_repo, source_ref, target_repo,
262 c.rhodecode_user.user_id, source_repo, source_ref, target_repo,
381 target_ref, commit_ids, reviewers, pullrequest_title,
263 target_ref, commit_ids, reviewers, pullrequest_title,
382 description
264 description
383 )
265 )
384 Session().commit()
266 Session().commit()
385 h.flash(_('Successfully opened new pull request'),
267 h.flash(_('Successfully opened new pull request'),
386 category='success')
268 category='success')
387 except Exception as e:
269 except Exception as e:
388 msg = _('Error occurred during sending pull request')
270 msg = _('Error occurred during sending pull request')
389 log.exception(msg)
271 log.exception(msg)
390 h.flash(msg, category='error')
272 h.flash(msg, category='error')
391 return redirect(url('pullrequest_home', repo_name=repo_name))
273 return redirect(url('pullrequest_home', repo_name=repo_name))
392
274
393 return redirect(url('pullrequest_show', repo_name=target_repo,
275 return redirect(url('pullrequest_show', repo_name=target_repo,
394 pull_request_id=pull_request.pull_request_id))
276 pull_request_id=pull_request.pull_request_id))
395
277
396 @LoginRequired()
278 @LoginRequired()
397 @NotAnonymous()
279 @NotAnonymous()
398 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
280 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
399 'repository.admin')
281 'repository.admin')
400 @auth.CSRFRequired()
282 @auth.CSRFRequired()
401 @jsonify
283 @jsonify
402 def update(self, repo_name, pull_request_id):
284 def update(self, repo_name, pull_request_id):
403 pull_request_id = safe_int(pull_request_id)
285 pull_request_id = safe_int(pull_request_id)
404 pull_request = PullRequest.get_or_404(pull_request_id)
286 pull_request = PullRequest.get_or_404(pull_request_id)
405 # only owner or admin can update it
287 # only owner or admin can update it
406 allowed_to_update = PullRequestModel().check_user_update(
288 allowed_to_update = PullRequestModel().check_user_update(
407 pull_request, c.rhodecode_user)
289 pull_request, c.rhodecode_user)
408 if allowed_to_update:
290 if allowed_to_update:
409 controls = peppercorn.parse(request.POST.items())
291 controls = peppercorn.parse(request.POST.items())
410
292
411 if 'review_members' in controls:
293 if 'review_members' in controls:
412 self._update_reviewers(
294 self._update_reviewers(
413 pull_request_id, controls['review_members'])
295 pull_request_id, controls['review_members'])
414 elif str2bool(request.POST.get('update_commits', 'false')):
296 elif str2bool(request.POST.get('update_commits', 'false')):
415 self._update_commits(pull_request)
297 self._update_commits(pull_request)
416 elif str2bool(request.POST.get('close_pull_request', 'false')):
298 elif str2bool(request.POST.get('close_pull_request', 'false')):
417 self._reject_close(pull_request)
299 self._reject_close(pull_request)
418 elif str2bool(request.POST.get('edit_pull_request', 'false')):
300 elif str2bool(request.POST.get('edit_pull_request', 'false')):
419 self._edit_pull_request(pull_request)
301 self._edit_pull_request(pull_request)
420 else:
302 else:
421 raise HTTPBadRequest()
303 raise HTTPBadRequest()
422 return True
304 return True
423 raise HTTPForbidden()
305 raise HTTPForbidden()
424
306
425 def _edit_pull_request(self, pull_request):
307 def _edit_pull_request(self, pull_request):
426 try:
308 try:
427 PullRequestModel().edit(
309 PullRequestModel().edit(
428 pull_request, request.POST.get('title'),
310 pull_request, request.POST.get('title'),
429 request.POST.get('description'))
311 request.POST.get('description'))
430 except ValueError:
312 except ValueError:
431 msg = _(u'Cannot update closed pull requests.')
313 msg = _(u'Cannot update closed pull requests.')
432 h.flash(msg, category='error')
314 h.flash(msg, category='error')
433 return
315 return
434 else:
316 else:
435 Session().commit()
317 Session().commit()
436
318
437 msg = _(u'Pull request title & description updated.')
319 msg = _(u'Pull request title & description updated.')
438 h.flash(msg, category='success')
320 h.flash(msg, category='success')
439 return
321 return
440
322
441 def _update_commits(self, pull_request):
323 def _update_commits(self, pull_request):
442 resp = PullRequestModel().update_commits(pull_request)
324 resp = PullRequestModel().update_commits(pull_request)
443
325
444 if resp.executed:
326 if resp.executed:
445
327
446 if resp.target_changed and resp.source_changed:
328 if resp.target_changed and resp.source_changed:
447 changed = 'target and source repositories'
329 changed = 'target and source repositories'
448 elif resp.target_changed and not resp.source_changed:
330 elif resp.target_changed and not resp.source_changed:
449 changed = 'target repository'
331 changed = 'target repository'
450 elif not resp.target_changed and resp.source_changed:
332 elif not resp.target_changed and resp.source_changed:
451 changed = 'source repository'
333 changed = 'source repository'
452 else:
334 else:
453 changed = 'nothing'
335 changed = 'nothing'
454
336
455 msg = _(
337 msg = _(
456 u'Pull request updated to "{source_commit_id}" with '
338 u'Pull request updated to "{source_commit_id}" with '
457 u'{count_added} added, {count_removed} removed commits. '
339 u'{count_added} added, {count_removed} removed commits. '
458 u'Source of changes: {change_source}')
340 u'Source of changes: {change_source}')
459 msg = msg.format(
341 msg = msg.format(
460 source_commit_id=pull_request.source_ref_parts.commit_id,
342 source_commit_id=pull_request.source_ref_parts.commit_id,
461 count_added=len(resp.changes.added),
343 count_added=len(resp.changes.added),
462 count_removed=len(resp.changes.removed),
344 count_removed=len(resp.changes.removed),
463 change_source=changed)
345 change_source=changed)
464 h.flash(msg, category='success')
346 h.flash(msg, category='success')
465
347
466 registry = get_current_registry()
348 registry = get_current_registry()
467 rhodecode_plugins = getattr(registry, 'rhodecode_plugins', {})
349 rhodecode_plugins = getattr(registry, 'rhodecode_plugins', {})
468 channelstream_config = rhodecode_plugins.get('channelstream', {})
350 channelstream_config = rhodecode_plugins.get('channelstream', {})
469 if channelstream_config.get('enabled'):
351 if channelstream_config.get('enabled'):
470 message = msg + (
352 message = msg + (
471 ' - <a onclick="window.location.reload()">'
353 ' - <a onclick="window.location.reload()">'
472 '<strong>{}</strong></a>'.format(_('Reload page')))
354 '<strong>{}</strong></a>'.format(_('Reload page')))
473 channel = '/repo${}$/pr/{}'.format(
355 channel = '/repo${}$/pr/{}'.format(
474 pull_request.target_repo.repo_name,
356 pull_request.target_repo.repo_name,
475 pull_request.pull_request_id
357 pull_request.pull_request_id
476 )
358 )
477 payload = {
359 payload = {
478 'type': 'message',
360 'type': 'message',
479 'user': 'system',
361 'user': 'system',
480 'exclude_users': [request.user.username],
362 'exclude_users': [request.user.username],
481 'channel': channel,
363 'channel': channel,
482 'message': {
364 'message': {
483 'message': message,
365 'message': message,
484 'level': 'success',
366 'level': 'success',
485 'topic': '/notifications'
367 'topic': '/notifications'
486 }
368 }
487 }
369 }
488 channelstream_request(
370 channelstream_request(
489 channelstream_config, [payload], '/message',
371 channelstream_config, [payload], '/message',
490 raise_exc=False)
372 raise_exc=False)
491 else:
373 else:
492 msg = PullRequestModel.UPDATE_STATUS_MESSAGES[resp.reason]
374 msg = PullRequestModel.UPDATE_STATUS_MESSAGES[resp.reason]
493 warning_reasons = [
375 warning_reasons = [
494 UpdateFailureReason.NO_CHANGE,
376 UpdateFailureReason.NO_CHANGE,
495 UpdateFailureReason.WRONG_REF_TYPE,
377 UpdateFailureReason.WRONG_REF_TYPE,
496 ]
378 ]
497 category = 'warning' if resp.reason in warning_reasons else 'error'
379 category = 'warning' if resp.reason in warning_reasons else 'error'
498 h.flash(msg, category=category)
380 h.flash(msg, category=category)
499
381
500 @auth.CSRFRequired()
382 @auth.CSRFRequired()
501 @LoginRequired()
383 @LoginRequired()
502 @NotAnonymous()
384 @NotAnonymous()
503 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
385 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
504 'repository.admin')
386 'repository.admin')
505 def merge(self, repo_name, pull_request_id):
387 def merge(self, repo_name, pull_request_id):
506 """
388 """
507 POST /{repo_name}/pull-request/{pull_request_id}
389 POST /{repo_name}/pull-request/{pull_request_id}
508
390
509 Merge will perform a server-side merge of the specified
391 Merge will perform a server-side merge of the specified
510 pull request, if the pull request is approved and mergeable.
392 pull request, if the pull request is approved and mergeable.
511 After successful merging, the pull request is automatically
393 After successful merging, the pull request is automatically
512 closed, with a relevant comment.
394 closed, with a relevant comment.
513 """
395 """
514 pull_request_id = safe_int(pull_request_id)
396 pull_request_id = safe_int(pull_request_id)
515 pull_request = PullRequest.get_or_404(pull_request_id)
397 pull_request = PullRequest.get_or_404(pull_request_id)
516 user = c.rhodecode_user
398 user = c.rhodecode_user
517
399
518 check = MergeCheck.validate(pull_request, user)
400 check = MergeCheck.validate(pull_request, user)
519 merge_possible = not check.failed
401 merge_possible = not check.failed
520
402
521 for err_type, error_msg in check.errors:
403 for err_type, error_msg in check.errors:
522 h.flash(error_msg, category=err_type)
404 h.flash(error_msg, category=err_type)
523
405
524 if merge_possible:
406 if merge_possible:
525 log.debug("Pre-conditions checked, trying to merge.")
407 log.debug("Pre-conditions checked, trying to merge.")
526 extras = vcs_operation_context(
408 extras = vcs_operation_context(
527 request.environ, repo_name=pull_request.target_repo.repo_name,
409 request.environ, repo_name=pull_request.target_repo.repo_name,
528 username=user.username, action='push',
410 username=user.username, action='push',
529 scm=pull_request.target_repo.repo_type)
411 scm=pull_request.target_repo.repo_type)
530 self._merge_pull_request(pull_request, user, extras)
412 self._merge_pull_request(pull_request, user, extras)
531
413
532 return redirect(url(
414 return redirect(url(
533 'pullrequest_show',
415 'pullrequest_show',
534 repo_name=pull_request.target_repo.repo_name,
416 repo_name=pull_request.target_repo.repo_name,
535 pull_request_id=pull_request.pull_request_id))
417 pull_request_id=pull_request.pull_request_id))
536
418
537 def _merge_pull_request(self, pull_request, user, extras):
419 def _merge_pull_request(self, pull_request, user, extras):
538 merge_resp = PullRequestModel().merge(
420 merge_resp = PullRequestModel().merge(
539 pull_request, user, extras=extras)
421 pull_request, user, extras=extras)
540
422
541 if merge_resp.executed:
423 if merge_resp.executed:
542 log.debug("The merge was successful, closing the pull request.")
424 log.debug("The merge was successful, closing the pull request.")
543 PullRequestModel().close_pull_request(
425 PullRequestModel().close_pull_request(
544 pull_request.pull_request_id, user)
426 pull_request.pull_request_id, user)
545 Session().commit()
427 Session().commit()
546 msg = _('Pull request was successfully merged and closed.')
428 msg = _('Pull request was successfully merged and closed.')
547 h.flash(msg, category='success')
429 h.flash(msg, category='success')
548 else:
430 else:
549 log.debug(
431 log.debug(
550 "The merge was not successful. Merge response: %s",
432 "The merge was not successful. Merge response: %s",
551 merge_resp)
433 merge_resp)
552 msg = PullRequestModel().merge_status_message(
434 msg = PullRequestModel().merge_status_message(
553 merge_resp.failure_reason)
435 merge_resp.failure_reason)
554 h.flash(msg, category='error')
436 h.flash(msg, category='error')
555
437
556 def _update_reviewers(self, pull_request_id, review_members):
438 def _update_reviewers(self, pull_request_id, review_members):
557 reviewers = [
439 reviewers = [
558 (int(r['user_id']), r['reasons']) for r in review_members]
440 (int(r['user_id']), r['reasons']) for r in review_members]
559 PullRequestModel().update_reviewers(pull_request_id, reviewers)
441 PullRequestModel().update_reviewers(pull_request_id, reviewers)
560 Session().commit()
442 Session().commit()
561
443
562 def _reject_close(self, pull_request):
444 def _reject_close(self, pull_request):
563 if pull_request.is_closed():
445 if pull_request.is_closed():
564 raise HTTPForbidden()
446 raise HTTPForbidden()
565
447
566 PullRequestModel().close_pull_request_with_comment(
448 PullRequestModel().close_pull_request_with_comment(
567 pull_request, c.rhodecode_user, c.rhodecode_db_repo)
449 pull_request, c.rhodecode_user, c.rhodecode_db_repo)
568 Session().commit()
450 Session().commit()
569
451
570 @LoginRequired()
452 @LoginRequired()
571 @NotAnonymous()
453 @NotAnonymous()
572 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
454 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
573 'repository.admin')
455 'repository.admin')
574 @auth.CSRFRequired()
456 @auth.CSRFRequired()
575 @jsonify
457 @jsonify
576 def delete(self, repo_name, pull_request_id):
458 def delete(self, repo_name, pull_request_id):
577 pull_request_id = safe_int(pull_request_id)
459 pull_request_id = safe_int(pull_request_id)
578 pull_request = PullRequest.get_or_404(pull_request_id)
460 pull_request = PullRequest.get_or_404(pull_request_id)
579
461
580 pr_closed = pull_request.is_closed()
462 pr_closed = pull_request.is_closed()
581 allowed_to_delete = PullRequestModel().check_user_delete(
463 allowed_to_delete = PullRequestModel().check_user_delete(
582 pull_request, c.rhodecode_user) and not pr_closed
464 pull_request, c.rhodecode_user) and not pr_closed
583
465
584 # only owner can delete it !
466 # only owner can delete it !
585 if allowed_to_delete:
467 if allowed_to_delete:
586 PullRequestModel().delete(pull_request)
468 PullRequestModel().delete(pull_request)
587 Session().commit()
469 Session().commit()
588 h.flash(_('Successfully deleted pull request'),
470 h.flash(_('Successfully deleted pull request'),
589 category='success')
471 category='success')
590 return redirect(url('my_account_pullrequests'))
472 return redirect(url('my_account_pullrequests'))
591
473
592 h.flash(_('Your are not allowed to delete this pull request'),
474 h.flash(_('Your are not allowed to delete this pull request'),
593 category='error')
475 category='error')
594 raise HTTPForbidden()
476 raise HTTPForbidden()
595
477
596 def _get_pr_version(self, pull_request_id, version=None):
478 def _get_pr_version(self, pull_request_id, version=None):
597 pull_request_id = safe_int(pull_request_id)
479 pull_request_id = safe_int(pull_request_id)
598 at_version = None
480 at_version = None
599
481
600 if version and version == 'latest':
482 if version and version == 'latest':
601 pull_request_ver = PullRequest.get(pull_request_id)
483 pull_request_ver = PullRequest.get(pull_request_id)
602 pull_request_obj = pull_request_ver
484 pull_request_obj = pull_request_ver
603 _org_pull_request_obj = pull_request_obj
485 _org_pull_request_obj = pull_request_obj
604 at_version = 'latest'
486 at_version = 'latest'
605 elif version:
487 elif version:
606 pull_request_ver = PullRequestVersion.get_or_404(version)
488 pull_request_ver = PullRequestVersion.get_or_404(version)
607 pull_request_obj = pull_request_ver
489 pull_request_obj = pull_request_ver
608 _org_pull_request_obj = pull_request_ver.pull_request
490 _org_pull_request_obj = pull_request_ver.pull_request
609 at_version = pull_request_ver.pull_request_version_id
491 at_version = pull_request_ver.pull_request_version_id
610 else:
492 else:
611 _org_pull_request_obj = pull_request_obj = PullRequest.get_or_404(pull_request_id)
493 _org_pull_request_obj = pull_request_obj = PullRequest.get_or_404(pull_request_id)
612
494
613 pull_request_display_obj = PullRequest.get_pr_display_object(
495 pull_request_display_obj = PullRequest.get_pr_display_object(
614 pull_request_obj, _org_pull_request_obj)
496 pull_request_obj, _org_pull_request_obj)
615
497
616 return _org_pull_request_obj, pull_request_obj, \
498 return _org_pull_request_obj, pull_request_obj, \
617 pull_request_display_obj, at_version
499 pull_request_display_obj, at_version
618
500
619 def _get_diffset(
501 def _get_diffset(
620 self, source_repo, source_ref_id, target_ref_id, target_commit,
502 self, source_repo, source_ref_id, target_ref_id, target_commit,
621 source_commit, diff_limit, file_limit, display_inline_comments):
503 source_commit, diff_limit, file_limit, display_inline_comments):
622 vcs_diff = PullRequestModel().get_diff(
504 vcs_diff = PullRequestModel().get_diff(
623 source_repo, source_ref_id, target_ref_id)
505 source_repo, source_ref_id, target_ref_id)
624
506
625 diff_processor = diffs.DiffProcessor(
507 diff_processor = diffs.DiffProcessor(
626 vcs_diff, format='newdiff', diff_limit=diff_limit,
508 vcs_diff, format='newdiff', diff_limit=diff_limit,
627 file_limit=file_limit, show_full_diff=c.fulldiff)
509 file_limit=file_limit, show_full_diff=c.fulldiff)
628
510
629 _parsed = diff_processor.prepare()
511 _parsed = diff_processor.prepare()
630
512
631 def _node_getter(commit):
513 def _node_getter(commit):
632 def get_node(fname):
514 def get_node(fname):
633 try:
515 try:
634 return commit.get_node(fname)
516 return commit.get_node(fname)
635 except NodeDoesNotExistError:
517 except NodeDoesNotExistError:
636 return None
518 return None
637
519
638 return get_node
520 return get_node
639
521
640 diffset = codeblocks.DiffSet(
522 diffset = codeblocks.DiffSet(
641 repo_name=c.repo_name,
523 repo_name=c.repo_name,
642 source_repo_name=c.source_repo.repo_name,
524 source_repo_name=c.source_repo.repo_name,
643 source_node_getter=_node_getter(target_commit),
525 source_node_getter=_node_getter(target_commit),
644 target_node_getter=_node_getter(source_commit),
526 target_node_getter=_node_getter(source_commit),
645 comments=display_inline_comments
527 comments=display_inline_comments
646 )
528 )
647 diffset = diffset.render_patchset(
529 diffset = diffset.render_patchset(
648 _parsed, target_commit.raw_id, source_commit.raw_id)
530 _parsed, target_commit.raw_id, source_commit.raw_id)
649
531
650 return diffset
532 return diffset
651
533
652 @LoginRequired()
534 @LoginRequired()
653 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
535 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
654 'repository.admin')
536 'repository.admin')
655 def show(self, repo_name, pull_request_id):
537 def show(self, repo_name, pull_request_id):
656 pull_request_id = safe_int(pull_request_id)
538 pull_request_id = safe_int(pull_request_id)
657 version = request.GET.get('version')
539 version = request.GET.get('version')
658 from_version = request.GET.get('from_version') or version
540 from_version = request.GET.get('from_version') or version
659 merge_checks = request.GET.get('merge_checks')
541 merge_checks = request.GET.get('merge_checks')
660 c.fulldiff = str2bool(request.GET.get('fulldiff'))
542 c.fulldiff = str2bool(request.GET.get('fulldiff'))
661
543
662 (pull_request_latest,
544 (pull_request_latest,
663 pull_request_at_ver,
545 pull_request_at_ver,
664 pull_request_display_obj,
546 pull_request_display_obj,
665 at_version) = self._get_pr_version(
547 at_version) = self._get_pr_version(
666 pull_request_id, version=version)
548 pull_request_id, version=version)
667 pr_closed = pull_request_latest.is_closed()
549 pr_closed = pull_request_latest.is_closed()
668
550
669 if pr_closed and (version or from_version):
551 if pr_closed and (version or from_version):
670 # not allow to browse versions
552 # not allow to browse versions
671 return redirect(h.url('pullrequest_show', repo_name=repo_name,
553 return redirect(h.url('pullrequest_show', repo_name=repo_name,
672 pull_request_id=pull_request_id))
554 pull_request_id=pull_request_id))
673
555
674 versions = pull_request_display_obj.versions()
556 versions = pull_request_display_obj.versions()
675
557
676 c.at_version = at_version
558 c.at_version = at_version
677 c.at_version_num = (at_version
559 c.at_version_num = (at_version
678 if at_version and at_version != 'latest'
560 if at_version and at_version != 'latest'
679 else None)
561 else None)
680 c.at_version_pos = ChangesetComment.get_index_from_version(
562 c.at_version_pos = ChangesetComment.get_index_from_version(
681 c.at_version_num, versions)
563 c.at_version_num, versions)
682
564
683 (prev_pull_request_latest,
565 (prev_pull_request_latest,
684 prev_pull_request_at_ver,
566 prev_pull_request_at_ver,
685 prev_pull_request_display_obj,
567 prev_pull_request_display_obj,
686 prev_at_version) = self._get_pr_version(
568 prev_at_version) = self._get_pr_version(
687 pull_request_id, version=from_version)
569 pull_request_id, version=from_version)
688
570
689 c.from_version = prev_at_version
571 c.from_version = prev_at_version
690 c.from_version_num = (prev_at_version
572 c.from_version_num = (prev_at_version
691 if prev_at_version and prev_at_version != 'latest'
573 if prev_at_version and prev_at_version != 'latest'
692 else None)
574 else None)
693 c.from_version_pos = ChangesetComment.get_index_from_version(
575 c.from_version_pos = ChangesetComment.get_index_from_version(
694 c.from_version_num, versions)
576 c.from_version_num, versions)
695
577
696 # define if we're in COMPARE mode or VIEW at version mode
578 # define if we're in COMPARE mode or VIEW at version mode
697 compare = at_version != prev_at_version
579 compare = at_version != prev_at_version
698
580
699 # pull_requests repo_name we opened it against
581 # pull_requests repo_name we opened it against
700 # ie. target_repo must match
582 # ie. target_repo must match
701 if repo_name != pull_request_at_ver.target_repo.repo_name:
583 if repo_name != pull_request_at_ver.target_repo.repo_name:
702 raise HTTPNotFound
584 raise HTTPNotFound
703
585
704 c.shadow_clone_url = PullRequestModel().get_shadow_clone_url(
586 c.shadow_clone_url = PullRequestModel().get_shadow_clone_url(
705 pull_request_at_ver)
587 pull_request_at_ver)
706
588
707 c.pull_request = pull_request_display_obj
589 c.pull_request = pull_request_display_obj
708 c.pull_request_latest = pull_request_latest
590 c.pull_request_latest = pull_request_latest
709
591
710 if compare or (at_version and not at_version == 'latest'):
592 if compare or (at_version and not at_version == 'latest'):
711 c.allowed_to_change_status = False
593 c.allowed_to_change_status = False
712 c.allowed_to_update = False
594 c.allowed_to_update = False
713 c.allowed_to_merge = False
595 c.allowed_to_merge = False
714 c.allowed_to_delete = False
596 c.allowed_to_delete = False
715 c.allowed_to_comment = False
597 c.allowed_to_comment = False
716 c.allowed_to_close = False
598 c.allowed_to_close = False
717 else:
599 else:
718 c.allowed_to_change_status = PullRequestModel(). \
600 c.allowed_to_change_status = PullRequestModel(). \
719 check_user_change_status(pull_request_at_ver, c.rhodecode_user) \
601 check_user_change_status(pull_request_at_ver, c.rhodecode_user) \
720 and not pr_closed
602 and not pr_closed
721
603
722 c.allowed_to_update = PullRequestModel().check_user_update(
604 c.allowed_to_update = PullRequestModel().check_user_update(
723 pull_request_latest, c.rhodecode_user) and not pr_closed
605 pull_request_latest, c.rhodecode_user) and not pr_closed
724 c.allowed_to_merge = PullRequestModel().check_user_merge(
606 c.allowed_to_merge = PullRequestModel().check_user_merge(
725 pull_request_latest, c.rhodecode_user) and not pr_closed
607 pull_request_latest, c.rhodecode_user) and not pr_closed
726 c.allowed_to_delete = PullRequestModel().check_user_delete(
608 c.allowed_to_delete = PullRequestModel().check_user_delete(
727 pull_request_latest, c.rhodecode_user) and not pr_closed
609 pull_request_latest, c.rhodecode_user) and not pr_closed
728 c.allowed_to_comment = not pr_closed
610 c.allowed_to_comment = not pr_closed
729 c.allowed_to_close = c.allowed_to_merge and not pr_closed
611 c.allowed_to_close = c.allowed_to_merge and not pr_closed
730
612
731 # check merge capabilities
613 # check merge capabilities
732 _merge_check = MergeCheck.validate(
614 _merge_check = MergeCheck.validate(
733 pull_request_latest, user=c.rhodecode_user)
615 pull_request_latest, user=c.rhodecode_user)
734 c.pr_merge_errors = _merge_check.error_details
616 c.pr_merge_errors = _merge_check.error_details
735 c.pr_merge_possible = not _merge_check.failed
617 c.pr_merge_possible = not _merge_check.failed
736 c.pr_merge_message = _merge_check.merge_msg
618 c.pr_merge_message = _merge_check.merge_msg
737
619
738 c.pull_request_review_status = _merge_check.review_status
620 c.pull_request_review_status = _merge_check.review_status
739 if merge_checks:
621 if merge_checks:
740 return render('/pullrequests/pullrequest_merge_checks.mako')
622 return render('/pullrequests/pullrequest_merge_checks.mako')
741
623
742 comments_model = CommentsModel()
624 comments_model = CommentsModel()
743
625
744 # reviewers and statuses
626 # reviewers and statuses
745 c.pull_request_reviewers = pull_request_at_ver.reviewers_statuses()
627 c.pull_request_reviewers = pull_request_at_ver.reviewers_statuses()
746 allowed_reviewers = [x[0].user_id for x in c.pull_request_reviewers]
628 allowed_reviewers = [x[0].user_id for x in c.pull_request_reviewers]
747
629
748 # GENERAL COMMENTS with versions #
630 # GENERAL COMMENTS with versions #
749 q = comments_model._all_general_comments_of_pull_request(pull_request_latest)
631 q = comments_model._all_general_comments_of_pull_request(pull_request_latest)
750 q = q.order_by(ChangesetComment.comment_id.asc())
632 q = q.order_by(ChangesetComment.comment_id.asc())
751 general_comments = q
633 general_comments = q
752
634
753 # pick comments we want to render at current version
635 # pick comments we want to render at current version
754 c.comment_versions = comments_model.aggregate_comments(
636 c.comment_versions = comments_model.aggregate_comments(
755 general_comments, versions, c.at_version_num)
637 general_comments, versions, c.at_version_num)
756 c.comments = c.comment_versions[c.at_version_num]['until']
638 c.comments = c.comment_versions[c.at_version_num]['until']
757
639
758 # INLINE COMMENTS with versions #
640 # INLINE COMMENTS with versions #
759 q = comments_model._all_inline_comments_of_pull_request(pull_request_latest)
641 q = comments_model._all_inline_comments_of_pull_request(pull_request_latest)
760 q = q.order_by(ChangesetComment.comment_id.asc())
642 q = q.order_by(ChangesetComment.comment_id.asc())
761 inline_comments = q
643 inline_comments = q
762
644
763 c.inline_versions = comments_model.aggregate_comments(
645 c.inline_versions = comments_model.aggregate_comments(
764 inline_comments, versions, c.at_version_num, inline=True)
646 inline_comments, versions, c.at_version_num, inline=True)
765
647
766 # inject latest version
648 # inject latest version
767 latest_ver = PullRequest.get_pr_display_object(
649 latest_ver = PullRequest.get_pr_display_object(
768 pull_request_latest, pull_request_latest)
650 pull_request_latest, pull_request_latest)
769
651
770 c.versions = versions + [latest_ver]
652 c.versions = versions + [latest_ver]
771
653
772 # if we use version, then do not show later comments
654 # if we use version, then do not show later comments
773 # than current version
655 # than current version
774 display_inline_comments = collections.defaultdict(
656 display_inline_comments = collections.defaultdict(
775 lambda: collections.defaultdict(list))
657 lambda: collections.defaultdict(list))
776 for co in inline_comments:
658 for co in inline_comments:
777 if c.at_version_num:
659 if c.at_version_num:
778 # pick comments that are at least UPTO given version, so we
660 # pick comments that are at least UPTO given version, so we
779 # don't render comments for higher version
661 # don't render comments for higher version
780 should_render = co.pull_request_version_id and \
662 should_render = co.pull_request_version_id and \
781 co.pull_request_version_id <= c.at_version_num
663 co.pull_request_version_id <= c.at_version_num
782 else:
664 else:
783 # showing all, for 'latest'
665 # showing all, for 'latest'
784 should_render = True
666 should_render = True
785
667
786 if should_render:
668 if should_render:
787 display_inline_comments[co.f_path][co.line_no].append(co)
669 display_inline_comments[co.f_path][co.line_no].append(co)
788
670
789 # load diff data into template context, if we use compare mode then
671 # load diff data into template context, if we use compare mode then
790 # diff is calculated based on changes between versions of PR
672 # diff is calculated based on changes between versions of PR
791
673
792 source_repo = pull_request_at_ver.source_repo
674 source_repo = pull_request_at_ver.source_repo
793 source_ref_id = pull_request_at_ver.source_ref_parts.commit_id
675 source_ref_id = pull_request_at_ver.source_ref_parts.commit_id
794
676
795 target_repo = pull_request_at_ver.target_repo
677 target_repo = pull_request_at_ver.target_repo
796 target_ref_id = pull_request_at_ver.target_ref_parts.commit_id
678 target_ref_id = pull_request_at_ver.target_ref_parts.commit_id
797
679
798 if compare:
680 if compare:
799 # in compare switch the diff base to latest commit from prev version
681 # in compare switch the diff base to latest commit from prev version
800 target_ref_id = prev_pull_request_display_obj.revisions[0]
682 target_ref_id = prev_pull_request_display_obj.revisions[0]
801
683
802 # despite opening commits for bookmarks/branches/tags, we always
684 # despite opening commits for bookmarks/branches/tags, we always
803 # convert this to rev to prevent changes after bookmark or branch change
685 # convert this to rev to prevent changes after bookmark or branch change
804 c.source_ref_type = 'rev'
686 c.source_ref_type = 'rev'
805 c.source_ref = source_ref_id
687 c.source_ref = source_ref_id
806
688
807 c.target_ref_type = 'rev'
689 c.target_ref_type = 'rev'
808 c.target_ref = target_ref_id
690 c.target_ref = target_ref_id
809
691
810 c.source_repo = source_repo
692 c.source_repo = source_repo
811 c.target_repo = target_repo
693 c.target_repo = target_repo
812
694
813 # diff_limit is the old behavior, will cut off the whole diff
695 # diff_limit is the old behavior, will cut off the whole diff
814 # if the limit is applied otherwise will just hide the
696 # if the limit is applied otherwise will just hide the
815 # big files from the front-end
697 # big files from the front-end
816 diff_limit = self.cut_off_limit_diff
698 diff_limit = self.cut_off_limit_diff
817 file_limit = self.cut_off_limit_file
699 file_limit = self.cut_off_limit_file
818
700
819 c.commit_ranges = []
701 c.commit_ranges = []
820 source_commit = EmptyCommit()
702 source_commit = EmptyCommit()
821 target_commit = EmptyCommit()
703 target_commit = EmptyCommit()
822 c.missing_requirements = False
704 c.missing_requirements = False
823
705
824 source_scm = source_repo.scm_instance()
706 source_scm = source_repo.scm_instance()
825 target_scm = target_repo.scm_instance()
707 target_scm = target_repo.scm_instance()
826
708
827 # try first shadow repo, fallback to regular repo
709 # try first shadow repo, fallback to regular repo
828 try:
710 try:
829 commits_source_repo = pull_request_latest.get_shadow_repo()
711 commits_source_repo = pull_request_latest.get_shadow_repo()
830 except Exception:
712 except Exception:
831 log.debug('Failed to get shadow repo', exc_info=True)
713 log.debug('Failed to get shadow repo', exc_info=True)
832 commits_source_repo = source_scm
714 commits_source_repo = source_scm
833
715
834 c.commits_source_repo = commits_source_repo
716 c.commits_source_repo = commits_source_repo
835 commit_cache = {}
717 commit_cache = {}
836 try:
718 try:
837 pre_load = ["author", "branch", "date", "message"]
719 pre_load = ["author", "branch", "date", "message"]
838 show_revs = pull_request_at_ver.revisions
720 show_revs = pull_request_at_ver.revisions
839 for rev in show_revs:
721 for rev in show_revs:
840 comm = commits_source_repo.get_commit(
722 comm = commits_source_repo.get_commit(
841 commit_id=rev, pre_load=pre_load)
723 commit_id=rev, pre_load=pre_load)
842 c.commit_ranges.append(comm)
724 c.commit_ranges.append(comm)
843 commit_cache[comm.raw_id] = comm
725 commit_cache[comm.raw_id] = comm
844
726
845 target_commit = commits_source_repo.get_commit(
727 target_commit = commits_source_repo.get_commit(
846 commit_id=safe_str(target_ref_id))
728 commit_id=safe_str(target_ref_id))
847 source_commit = commits_source_repo.get_commit(
729 source_commit = commits_source_repo.get_commit(
848 commit_id=safe_str(source_ref_id))
730 commit_id=safe_str(source_ref_id))
849 except CommitDoesNotExistError:
731 except CommitDoesNotExistError:
850 pass
732 pass
851 except RepositoryRequirementError:
733 except RepositoryRequirementError:
852 log.warning(
734 log.warning(
853 'Failed to get all required data from repo', exc_info=True)
735 'Failed to get all required data from repo', exc_info=True)
854 c.missing_requirements = True
736 c.missing_requirements = True
855
737
856 c.ancestor = None # set it to None, to hide it from PR view
738 c.ancestor = None # set it to None, to hide it from PR view
857
739
858 try:
740 try:
859 ancestor_id = source_scm.get_common_ancestor(
741 ancestor_id = source_scm.get_common_ancestor(
860 source_commit.raw_id, target_commit.raw_id, target_scm)
742 source_commit.raw_id, target_commit.raw_id, target_scm)
861 c.ancestor_commit = source_scm.get_commit(ancestor_id)
743 c.ancestor_commit = source_scm.get_commit(ancestor_id)
862 except Exception:
744 except Exception:
863 c.ancestor_commit = None
745 c.ancestor_commit = None
864
746
865 c.statuses = source_repo.statuses(
747 c.statuses = source_repo.statuses(
866 [x.raw_id for x in c.commit_ranges])
748 [x.raw_id for x in c.commit_ranges])
867
749
868 # auto collapse if we have more than limit
750 # auto collapse if we have more than limit
869 collapse_limit = diffs.DiffProcessor._collapse_commits_over
751 collapse_limit = diffs.DiffProcessor._collapse_commits_over
870 c.collapse_all_commits = len(c.commit_ranges) > collapse_limit
752 c.collapse_all_commits = len(c.commit_ranges) > collapse_limit
871 c.compare_mode = compare
753 c.compare_mode = compare
872
754
873 c.missing_commits = False
755 c.missing_commits = False
874 if (c.missing_requirements or isinstance(source_commit, EmptyCommit)
756 if (c.missing_requirements or isinstance(source_commit, EmptyCommit)
875 or source_commit == target_commit):
757 or source_commit == target_commit):
876
758
877 c.missing_commits = True
759 c.missing_commits = True
878 else:
760 else:
879
761
880 c.diffset = self._get_diffset(
762 c.diffset = self._get_diffset(
881 commits_source_repo, source_ref_id, target_ref_id,
763 commits_source_repo, source_ref_id, target_ref_id,
882 target_commit, source_commit,
764 target_commit, source_commit,
883 diff_limit, file_limit, display_inline_comments)
765 diff_limit, file_limit, display_inline_comments)
884
766
885 c.limited_diff = c.diffset.limited_diff
767 c.limited_diff = c.diffset.limited_diff
886
768
887 # calculate removed files that are bound to comments
769 # calculate removed files that are bound to comments
888 comment_deleted_files = [
770 comment_deleted_files = [
889 fname for fname in display_inline_comments
771 fname for fname in display_inline_comments
890 if fname not in c.diffset.file_stats]
772 if fname not in c.diffset.file_stats]
891
773
892 c.deleted_files_comments = collections.defaultdict(dict)
774 c.deleted_files_comments = collections.defaultdict(dict)
893 for fname, per_line_comments in display_inline_comments.items():
775 for fname, per_line_comments in display_inline_comments.items():
894 if fname in comment_deleted_files:
776 if fname in comment_deleted_files:
895 c.deleted_files_comments[fname]['stats'] = 0
777 c.deleted_files_comments[fname]['stats'] = 0
896 c.deleted_files_comments[fname]['comments'] = list()
778 c.deleted_files_comments[fname]['comments'] = list()
897 for lno, comments in per_line_comments.items():
779 for lno, comments in per_line_comments.items():
898 c.deleted_files_comments[fname]['comments'].extend(
780 c.deleted_files_comments[fname]['comments'].extend(
899 comments)
781 comments)
900
782
901 # this is a hack to properly display links, when creating PR, the
783 # this is a hack to properly display links, when creating PR, the
902 # compare view and others uses different notation, and
784 # compare view and others uses different notation, and
903 # compare_commits.mako renders links based on the target_repo.
785 # compare_commits.mako renders links based on the target_repo.
904 # We need to swap that here to generate it properly on the html side
786 # We need to swap that here to generate it properly on the html side
905 c.target_repo = c.source_repo
787 c.target_repo = c.source_repo
906
788
907 c.commit_statuses = ChangesetStatus.STATUSES
789 c.commit_statuses = ChangesetStatus.STATUSES
908
790
909 c.show_version_changes = not pr_closed
791 c.show_version_changes = not pr_closed
910 if c.show_version_changes:
792 if c.show_version_changes:
911 cur_obj = pull_request_at_ver
793 cur_obj = pull_request_at_ver
912 prev_obj = prev_pull_request_at_ver
794 prev_obj = prev_pull_request_at_ver
913
795
914 old_commit_ids = prev_obj.revisions
796 old_commit_ids = prev_obj.revisions
915 new_commit_ids = cur_obj.revisions
797 new_commit_ids = cur_obj.revisions
916 commit_changes = PullRequestModel()._calculate_commit_id_changes(
798 commit_changes = PullRequestModel()._calculate_commit_id_changes(
917 old_commit_ids, new_commit_ids)
799 old_commit_ids, new_commit_ids)
918 c.commit_changes_summary = commit_changes
800 c.commit_changes_summary = commit_changes
919
801
920 # calculate the diff for commits between versions
802 # calculate the diff for commits between versions
921 c.commit_changes = []
803 c.commit_changes = []
922 mark = lambda cs, fw: list(
804 mark = lambda cs, fw: list(
923 h.itertools.izip_longest([], cs, fillvalue=fw))
805 h.itertools.izip_longest([], cs, fillvalue=fw))
924 for c_type, raw_id in mark(commit_changes.added, 'a') \
806 for c_type, raw_id in mark(commit_changes.added, 'a') \
925 + mark(commit_changes.removed, 'r') \
807 + mark(commit_changes.removed, 'r') \
926 + mark(commit_changes.common, 'c'):
808 + mark(commit_changes.common, 'c'):
927
809
928 if raw_id in commit_cache:
810 if raw_id in commit_cache:
929 commit = commit_cache[raw_id]
811 commit = commit_cache[raw_id]
930 else:
812 else:
931 try:
813 try:
932 commit = commits_source_repo.get_commit(raw_id)
814 commit = commits_source_repo.get_commit(raw_id)
933 except CommitDoesNotExistError:
815 except CommitDoesNotExistError:
934 # in case we fail extracting still use "dummy" commit
816 # in case we fail extracting still use "dummy" commit
935 # for display in commit diff
817 # for display in commit diff
936 commit = h.AttributeDict(
818 commit = h.AttributeDict(
937 {'raw_id': raw_id,
819 {'raw_id': raw_id,
938 'message': 'EMPTY or MISSING COMMIT'})
820 'message': 'EMPTY or MISSING COMMIT'})
939 c.commit_changes.append([c_type, commit])
821 c.commit_changes.append([c_type, commit])
940
822
941 # current user review statuses for each version
823 # current user review statuses for each version
942 c.review_versions = {}
824 c.review_versions = {}
943 if c.rhodecode_user.user_id in allowed_reviewers:
825 if c.rhodecode_user.user_id in allowed_reviewers:
944 for co in general_comments:
826 for co in general_comments:
945 if co.author.user_id == c.rhodecode_user.user_id:
827 if co.author.user_id == c.rhodecode_user.user_id:
946 # each comment has a status change
828 # each comment has a status change
947 status = co.status_change
829 status = co.status_change
948 if status:
830 if status:
949 _ver_pr = status[0].comment.pull_request_version_id
831 _ver_pr = status[0].comment.pull_request_version_id
950 c.review_versions[_ver_pr] = status[0]
832 c.review_versions[_ver_pr] = status[0]
951
833
952 return render('/pullrequests/pullrequest_show.mako')
834 return render('/pullrequests/pullrequest_show.mako')
953
835
954 @LoginRequired()
836 @LoginRequired()
955 @NotAnonymous()
837 @NotAnonymous()
956 @HasRepoPermissionAnyDecorator(
838 @HasRepoPermissionAnyDecorator(
957 'repository.read', 'repository.write', 'repository.admin')
839 'repository.read', 'repository.write', 'repository.admin')
958 @auth.CSRFRequired()
840 @auth.CSRFRequired()
959 @jsonify
841 @jsonify
960 def comment(self, repo_name, pull_request_id):
842 def comment(self, repo_name, pull_request_id):
961 pull_request_id = safe_int(pull_request_id)
843 pull_request_id = safe_int(pull_request_id)
962 pull_request = PullRequest.get_or_404(pull_request_id)
844 pull_request = PullRequest.get_or_404(pull_request_id)
963 if pull_request.is_closed():
845 if pull_request.is_closed():
964 raise HTTPForbidden()
846 raise HTTPForbidden()
965
847
966 status = request.POST.get('changeset_status', None)
848 status = request.POST.get('changeset_status', None)
967 text = request.POST.get('text')
849 text = request.POST.get('text')
968 comment_type = request.POST.get('comment_type')
850 comment_type = request.POST.get('comment_type')
969 resolves_comment_id = request.POST.get('resolves_comment_id', None)
851 resolves_comment_id = request.POST.get('resolves_comment_id', None)
970 close_pull_request = request.POST.get('close_pull_request')
852 close_pull_request = request.POST.get('close_pull_request')
971
853
972 close_pr = False
854 close_pr = False
973 # only owner or admin or person with write permissions
855 # only owner or admin or person with write permissions
974 allowed_to_close = PullRequestModel().check_user_update(
856 allowed_to_close = PullRequestModel().check_user_update(
975 pull_request, c.rhodecode_user)
857 pull_request, c.rhodecode_user)
976
858
977 if close_pull_request and allowed_to_close:
859 if close_pull_request and allowed_to_close:
978 close_pr = True
860 close_pr = True
979 pull_request_review_status = pull_request.calculated_review_status()
861 pull_request_review_status = pull_request.calculated_review_status()
980 if pull_request_review_status == ChangesetStatus.STATUS_APPROVED:
862 if pull_request_review_status == ChangesetStatus.STATUS_APPROVED:
981 # approved only if we have voting consent
863 # approved only if we have voting consent
982 status = ChangesetStatus.STATUS_APPROVED
864 status = ChangesetStatus.STATUS_APPROVED
983 else:
865 else:
984 status = ChangesetStatus.STATUS_REJECTED
866 status = ChangesetStatus.STATUS_REJECTED
985
867
986 allowed_to_change_status = PullRequestModel().check_user_change_status(
868 allowed_to_change_status = PullRequestModel().check_user_change_status(
987 pull_request, c.rhodecode_user)
869 pull_request, c.rhodecode_user)
988
870
989 if status and allowed_to_change_status:
871 if status and allowed_to_change_status:
990 message = (_('Status change %(transition_icon)s %(status)s')
872 message = (_('Status change %(transition_icon)s %(status)s')
991 % {'transition_icon': '>',
873 % {'transition_icon': '>',
992 'status': ChangesetStatus.get_status_lbl(status)})
874 'status': ChangesetStatus.get_status_lbl(status)})
993 if close_pr:
875 if close_pr:
994 message = _('Closing with') + ' ' + message
876 message = _('Closing with') + ' ' + message
995 text = text or message
877 text = text or message
996 comm = CommentsModel().create(
878 comm = CommentsModel().create(
997 text=text,
879 text=text,
998 repo=c.rhodecode_db_repo.repo_id,
880 repo=c.rhodecode_db_repo.repo_id,
999 user=c.rhodecode_user.user_id,
881 user=c.rhodecode_user.user_id,
1000 pull_request=pull_request_id,
882 pull_request=pull_request_id,
1001 f_path=request.POST.get('f_path'),
883 f_path=request.POST.get('f_path'),
1002 line_no=request.POST.get('line'),
884 line_no=request.POST.get('line'),
1003 status_change=(ChangesetStatus.get_status_lbl(status)
885 status_change=(ChangesetStatus.get_status_lbl(status)
1004 if status and allowed_to_change_status else None),
886 if status and allowed_to_change_status else None),
1005 status_change_type=(status
887 status_change_type=(status
1006 if status and allowed_to_change_status else None),
888 if status and allowed_to_change_status else None),
1007 closing_pr=close_pr,
889 closing_pr=close_pr,
1008 comment_type=comment_type,
890 comment_type=comment_type,
1009 resolves_comment_id=resolves_comment_id
891 resolves_comment_id=resolves_comment_id
1010 )
892 )
1011
893
1012 if allowed_to_change_status:
894 if allowed_to_change_status:
1013 old_calculated_status = pull_request.calculated_review_status()
895 old_calculated_status = pull_request.calculated_review_status()
1014 # get status if set !
896 # get status if set !
1015 if status:
897 if status:
1016 ChangesetStatusModel().set_status(
898 ChangesetStatusModel().set_status(
1017 c.rhodecode_db_repo.repo_id,
899 c.rhodecode_db_repo.repo_id,
1018 status,
900 status,
1019 c.rhodecode_user.user_id,
901 c.rhodecode_user.user_id,
1020 comm,
902 comm,
1021 pull_request=pull_request_id
903 pull_request=pull_request_id
1022 )
904 )
1023
905
1024 Session().flush()
906 Session().flush()
1025 events.trigger(events.PullRequestCommentEvent(pull_request, comm))
907 events.trigger(events.PullRequestCommentEvent(pull_request, comm))
1026 # we now calculate the status of pull request, and based on that
908 # we now calculate the status of pull request, and based on that
1027 # calculation we set the commits status
909 # calculation we set the commits status
1028 calculated_status = pull_request.calculated_review_status()
910 calculated_status = pull_request.calculated_review_status()
1029 if old_calculated_status != calculated_status:
911 if old_calculated_status != calculated_status:
1030 PullRequestModel()._trigger_pull_request_hook(
912 PullRequestModel()._trigger_pull_request_hook(
1031 pull_request, c.rhodecode_user, 'review_status_change')
913 pull_request, c.rhodecode_user, 'review_status_change')
1032
914
1033 calculated_status_lbl = ChangesetStatus.get_status_lbl(
915 calculated_status_lbl = ChangesetStatus.get_status_lbl(
1034 calculated_status)
916 calculated_status)
1035
917
1036 if close_pr:
918 if close_pr:
1037 status_completed = (
919 status_completed = (
1038 calculated_status in [ChangesetStatus.STATUS_APPROVED,
920 calculated_status in [ChangesetStatus.STATUS_APPROVED,
1039 ChangesetStatus.STATUS_REJECTED])
921 ChangesetStatus.STATUS_REJECTED])
1040 if close_pull_request or status_completed:
922 if close_pull_request or status_completed:
1041 PullRequestModel().close_pull_request(
923 PullRequestModel().close_pull_request(
1042 pull_request_id, c.rhodecode_user)
924 pull_request_id, c.rhodecode_user)
1043 else:
925 else:
1044 h.flash(_('Closing pull request on other statuses than '
926 h.flash(_('Closing pull request on other statuses than '
1045 'rejected or approved is forbidden. '
927 'rejected or approved is forbidden. '
1046 'Calculated status from all reviewers '
928 'Calculated status from all reviewers '
1047 'is currently: %s') % calculated_status_lbl,
929 'is currently: %s') % calculated_status_lbl,
1048 category='warning')
930 category='warning')
1049
931
1050 Session().commit()
932 Session().commit()
1051
933
1052 if not request.is_xhr:
934 if not request.is_xhr:
1053 return redirect(h.url('pullrequest_show', repo_name=repo_name,
935 return redirect(h.url('pullrequest_show', repo_name=repo_name,
1054 pull_request_id=pull_request_id))
936 pull_request_id=pull_request_id))
1055
937
1056 data = {
938 data = {
1057 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
939 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
1058 }
940 }
1059 if comm:
941 if comm:
1060 c.co = comm
942 c.co = comm
1061 c.inline_comment = True if comm.line_no else False
943 c.inline_comment = True if comm.line_no else False
1062 data.update(comm.get_dict())
944 data.update(comm.get_dict())
1063 data.update({'rendered_text':
945 data.update({'rendered_text':
1064 render('changeset/changeset_comment_block.mako')})
946 render('changeset/changeset_comment_block.mako')})
1065
947
1066 return data
948 return data
1067
949
1068 @LoginRequired()
950 @LoginRequired()
1069 @NotAnonymous()
951 @NotAnonymous()
1070 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
952 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
1071 'repository.admin')
953 'repository.admin')
1072 @auth.CSRFRequired()
954 @auth.CSRFRequired()
1073 @jsonify
955 @jsonify
1074 def delete_comment(self, repo_name, comment_id):
956 def delete_comment(self, repo_name, comment_id):
1075 return self._delete_comment(comment_id)
957 return self._delete_comment(comment_id)
1076
958
1077 def _delete_comment(self, comment_id):
959 def _delete_comment(self, comment_id):
1078 comment_id = safe_int(comment_id)
960 comment_id = safe_int(comment_id)
1079 co = ChangesetComment.get_or_404(comment_id)
961 co = ChangesetComment.get_or_404(comment_id)
1080 if co.pull_request.is_closed():
962 if co.pull_request.is_closed():
1081 # don't allow deleting comments on closed pull request
963 # don't allow deleting comments on closed pull request
1082 raise HTTPForbidden()
964 raise HTTPForbidden()
1083
965
1084 is_owner = co.author.user_id == c.rhodecode_user.user_id
966 is_owner = co.author.user_id == c.rhodecode_user.user_id
1085 is_repo_admin = h.HasRepoPermissionAny('repository.admin')(c.repo_name)
967 is_repo_admin = h.HasRepoPermissionAny('repository.admin')(c.repo_name)
1086 if h.HasPermissionAny('hg.admin')() or is_repo_admin or is_owner:
968 if h.HasPermissionAny('hg.admin')() or is_repo_admin or is_owner:
1087 old_calculated_status = co.pull_request.calculated_review_status()
969 old_calculated_status = co.pull_request.calculated_review_status()
1088 CommentsModel().delete(comment=co)
970 CommentsModel().delete(comment=co)
1089 Session().commit()
971 Session().commit()
1090 calculated_status = co.pull_request.calculated_review_status()
972 calculated_status = co.pull_request.calculated_review_status()
1091 if old_calculated_status != calculated_status:
973 if old_calculated_status != calculated_status:
1092 PullRequestModel()._trigger_pull_request_hook(
974 PullRequestModel()._trigger_pull_request_hook(
1093 co.pull_request, c.rhodecode_user, 'review_status_change')
975 co.pull_request, c.rhodecode_user, 'review_status_change')
1094 return True
976 return True
1095 else:
977 else:
1096 raise HTTPForbidden()
978 raise HTTPForbidden()
@@ -1,85 +1,89 b''
1 // See panels-bootstrap.less
1 // See panels-bootstrap.less
2 // These provide overrides for custom styling of Bootstrap panels
2 // These provide overrides for custom styling of Bootstrap panels
3
3
4 .panel {
4 .panel {
5 &:extend(.clearfix);
5 &:extend(.clearfix);
6
6
7 width: 100%;
7 width: 100%;
8 margin: 0 0 25px 0;
8 margin: 0 0 25px 0;
9 .border-radius(@border-radius);
9 .border-radius(@border-radius);
10 .box-shadow(none);
10 .box-shadow(none);
11
11
12 .permalink {
12 .permalink {
13 visibility: hidden;
13 visibility: hidden;
14 }
14 }
15
15
16 &:hover .permalink {
16 &:hover .permalink {
17 visibility: visible;
17 visibility: visible;
18 color: @rcblue;
18 color: @rcblue;
19 }
19 }
20
20
21 .panel-heading {
21 .panel-heading {
22 position: relative;
22 position: relative;
23 min-height: 1em;
23 min-height: 1em;
24 padding: @padding @panel-padding;
24 padding: @padding @panel-padding;
25 border-bottom: none;
25 border-bottom: none;
26
26
27 .panel-title,
27 .panel-title,
28 h3.panel-title {
28 h3.panel-title {
29 float: left;
29 float: left;
30 padding: 0 @padding 0 0;
30 padding: 0 @padding 0 0;
31 line-height: 1;
31 line-height: 1;
32 font-size: @panel-title;
32 font-size: @panel-title;
33 color: @grey1;
33 color: @grey1;
34 }
34 }
35
35
36 .panel-edit {
36 .panel-edit {
37 float: right;
37 float: right;
38 line-height: 1;
38 line-height: 1;
39 font-size: @panel-title;
39 font-size: @panel-title;
40 }
40 }
41 }
41 }
42
42
43 .panel-body {
43 .panel-body {
44 padding: @panel-padding;
44 padding: @panel-padding;
45
46 &.panel-body-min-height {
47 min-height: 150px
48 }
45 }
49 }
46
50
47 .panel-footer {
51 .panel-footer {
48 background-color: white;
52 background-color: white;
49 padding: .65em @panel-padding .5em;
53 padding: .65em @panel-padding .5em;
50 font-size: @panel-footer;
54 font-size: @panel-footer;
51 color: @text-muted;
55 color: @text-muted;
52 }
56 }
53
57
54 .q_filter_box {
58 .q_filter_box {
55 min-width: 40%;
59 min-width: 40%;
56 }
60 }
57
61
58 // special cases
62 // special cases
59 &.user-profile {
63 &.user-profile {
60 float: left;
64 float: left;
61
65
62 .panel-heading {
66 .panel-heading {
63 margin-bottom: @padding;
67 margin-bottom: @padding;
64 }
68 }
65
69
66 .panel-body {
70 .panel-body {
67 &:extend(.clearfix);
71 &:extend(.clearfix);
68 }
72 }
69 }
73 }
70 }
74 }
71
75
72 .main-content h3.panel-title {
76 .main-content h3.panel-title {
73 font-size: @panel-title;
77 font-size: @panel-title;
74 color: @grey1;
78 color: @grey1;
75 }
79 }
76
80
77 .panel-body-title-text {
81 .panel-body-title-text {
78 margin: 0 0 20px 0;
82 margin: 0 0 20px 0;
79 }
83 }
80
84
81 // play nice with the current form and field css
85 // play nice with the current form and field css
82 .field.panel-default,
86 .field.panel-default,
83 .form.panel-default {
87 .form.panel-default {
84 width: auto;
88 width: auto;
85 } No newline at end of file
89 }
@@ -1,134 +1,135 b''
1
1
2 /******************************************************************************
2 /******************************************************************************
3 * *
3 * *
4 * DO NOT CHANGE THIS FILE MANUALLY *
4 * DO NOT CHANGE THIS FILE MANUALLY *
5 * *
5 * *
6 * *
6 * *
7 * This file is automatically generated when the app starts up with *
7 * This file is automatically generated when the app starts up with *
8 * generate_js_files = true *
8 * generate_js_files = true *
9 * *
9 * *
10 * To add a route here pass jsroute=True to the route definition in the app *
10 * To add a route here pass jsroute=True to the route definition in the app *
11 * *
11 * *
12 ******************************************************************************/
12 ******************************************************************************/
13 function registerRCRoutes() {
13 function registerRCRoutes() {
14 // routes registration
14 // routes registration
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('gists', '/_admin/gists', []);
18 pyroutes.register('gists', '/_admin/gists', []);
19 pyroutes.register('new_gist', '/_admin/gists/new', []);
19 pyroutes.register('new_gist', '/_admin/gists/new', []);
20 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
20 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
21 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
21 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
22 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
22 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
23 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
23 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
24 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/default-reviewers', ['repo_name']);
24 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/default-reviewers', ['repo_name']);
25 pyroutes.register('changeset_home', '/%(repo_name)s/changeset/%(revision)s', ['repo_name', 'revision']);
25 pyroutes.register('changeset_home', '/%(repo_name)s/changeset/%(revision)s', ['repo_name', 'revision']);
26 pyroutes.register('changeset_comment', '/%(repo_name)s/changeset/%(revision)s/comment', ['repo_name', 'revision']);
26 pyroutes.register('changeset_comment', '/%(repo_name)s/changeset/%(revision)s/comment', ['repo_name', 'revision']);
27 pyroutes.register('changeset_comment_preview', '/%(repo_name)s/changeset/comment/preview', ['repo_name']);
27 pyroutes.register('changeset_comment_preview', '/%(repo_name)s/changeset/comment/preview', ['repo_name']);
28 pyroutes.register('changeset_comment_delete', '/%(repo_name)s/changeset/comment/%(comment_id)s/delete', ['repo_name', 'comment_id']);
28 pyroutes.register('changeset_comment_delete', '/%(repo_name)s/changeset/comment/%(comment_id)s/delete', ['repo_name', 'comment_id']);
29 pyroutes.register('changeset_info', '/%(repo_name)s/changeset_info/%(revision)s', ['repo_name', 'revision']);
29 pyroutes.register('changeset_info', '/%(repo_name)s/changeset_info/%(revision)s', ['repo_name', 'revision']);
30 pyroutes.register('compare_url', '/%(repo_name)s/compare/%(source_ref_type)s@%(source_ref)s...%(target_ref_type)s@%(target_ref)s', ['repo_name', 'source_ref_type', 'source_ref', 'target_ref_type', 'target_ref']);
30 pyroutes.register('compare_url', '/%(repo_name)s/compare/%(source_ref_type)s@%(source_ref)s...%(target_ref_type)s@%(target_ref)s', ['repo_name', 'source_ref_type', 'source_ref', 'target_ref_type', 'target_ref']);
31 pyroutes.register('pullrequest_home', '/%(repo_name)s/pull-request/new', ['repo_name']);
31 pyroutes.register('pullrequest_home', '/%(repo_name)s/pull-request/new', ['repo_name']);
32 pyroutes.register('pullrequest', '/%(repo_name)s/pull-request/new', ['repo_name']);
32 pyroutes.register('pullrequest', '/%(repo_name)s/pull-request/new', ['repo_name']);
33 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
33 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
34 pyroutes.register('pullrequest_repo_destinations', '/%(repo_name)s/pull-request/repo-destinations', ['repo_name']);
34 pyroutes.register('pullrequest_repo_destinations', '/%(repo_name)s/pull-request/repo-destinations', ['repo_name']);
35 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
35 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
36 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
36 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
37 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
38 pyroutes.register('pullrequest_comment', '/%(repo_name)s/pull-request-comment/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
37 pyroutes.register('pullrequest_comment', '/%(repo_name)s/pull-request-comment/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
39 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request-comment/%(comment_id)s/delete', ['repo_name', 'comment_id']);
38 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request-comment/%(comment_id)s/delete', ['repo_name', 'comment_id']);
40 pyroutes.register('changelog_home', '/%(repo_name)s/changelog', ['repo_name']);
39 pyroutes.register('changelog_home', '/%(repo_name)s/changelog', ['repo_name']);
41 pyroutes.register('changelog_file_home', '/%(repo_name)s/changelog/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
40 pyroutes.register('changelog_file_home', '/%(repo_name)s/changelog/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
42 pyroutes.register('changelog_elements', '/%(repo_name)s/changelog_details', ['repo_name']);
41 pyroutes.register('changelog_elements', '/%(repo_name)s/changelog_details', ['repo_name']);
43 pyroutes.register('files_home', '/%(repo_name)s/files/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
42 pyroutes.register('files_home', '/%(repo_name)s/files/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
44 pyroutes.register('files_history_home', '/%(repo_name)s/history/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
43 pyroutes.register('files_history_home', '/%(repo_name)s/history/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
45 pyroutes.register('files_authors_home', '/%(repo_name)s/authors/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
44 pyroutes.register('files_authors_home', '/%(repo_name)s/authors/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
46 pyroutes.register('files_annotate_home', '/%(repo_name)s/annotate/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
45 pyroutes.register('files_annotate_home', '/%(repo_name)s/annotate/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
47 pyroutes.register('files_annotate_previous', '/%(repo_name)s/annotate-previous/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
46 pyroutes.register('files_annotate_previous', '/%(repo_name)s/annotate-previous/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
48 pyroutes.register('files_archive_home', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
47 pyroutes.register('files_archive_home', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
49 pyroutes.register('files_nodelist_home', '/%(repo_name)s/nodelist/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
48 pyroutes.register('files_nodelist_home', '/%(repo_name)s/nodelist/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
50 pyroutes.register('files_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
49 pyroutes.register('files_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
51 pyroutes.register('summary_home_slash', '/%(repo_name)s/', ['repo_name']);
50 pyroutes.register('summary_home_slash', '/%(repo_name)s/', ['repo_name']);
52 pyroutes.register('summary_home', '/%(repo_name)s', ['repo_name']);
51 pyroutes.register('summary_home', '/%(repo_name)s', ['repo_name']);
53 pyroutes.register('favicon', '/favicon.ico', []);
52 pyroutes.register('favicon', '/favicon.ico', []);
54 pyroutes.register('robots', '/robots.txt', []);
53 pyroutes.register('robots', '/robots.txt', []);
55 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
54 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
56 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
55 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
57 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
56 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
58 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
57 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
59 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
58 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
60 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
59 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
61 pyroutes.register('repo_group_integrations_home', '%(repo_group_name)s/settings/integrations', ['repo_group_name']);
60 pyroutes.register('repo_group_integrations_home', '%(repo_group_name)s/settings/integrations', ['repo_group_name']);
62 pyroutes.register('repo_group_integrations_list', '%(repo_group_name)s/settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
61 pyroutes.register('repo_group_integrations_list', '%(repo_group_name)s/settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
63 pyroutes.register('repo_group_integrations_new', '%(repo_group_name)s/settings/integrations/new', ['repo_group_name']);
62 pyroutes.register('repo_group_integrations_new', '%(repo_group_name)s/settings/integrations/new', ['repo_group_name']);
64 pyroutes.register('repo_group_integrations_create', '%(repo_group_name)s/settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
63 pyroutes.register('repo_group_integrations_create', '%(repo_group_name)s/settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
65 pyroutes.register('repo_group_integrations_edit', '%(repo_group_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
64 pyroutes.register('repo_group_integrations_edit', '%(repo_group_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
66 pyroutes.register('repo_integrations_home', '%(repo_name)s/settings/integrations', ['repo_name']);
65 pyroutes.register('repo_integrations_home', '%(repo_name)s/settings/integrations', ['repo_name']);
67 pyroutes.register('repo_integrations_list', '%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
66 pyroutes.register('repo_integrations_list', '%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
68 pyroutes.register('repo_integrations_new', '%(repo_name)s/settings/integrations/new', ['repo_name']);
67 pyroutes.register('repo_integrations_new', '%(repo_name)s/settings/integrations/new', ['repo_name']);
69 pyroutes.register('repo_integrations_create', '%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
68 pyroutes.register('repo_integrations_create', '%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
70 pyroutes.register('repo_integrations_edit', '%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
69 pyroutes.register('repo_integrations_edit', '%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
71 pyroutes.register('ops_ping', '_admin/ops/ping', []);
70 pyroutes.register('ops_ping', '_admin/ops/ping', []);
72 pyroutes.register('admin_home', '/_admin', []);
71 pyroutes.register('admin_home', '/_admin', []);
73 pyroutes.register('admin_audit_logs', '_admin/audit_logs', []);
72 pyroutes.register('admin_audit_logs', '_admin/audit_logs', []);
74 pyroutes.register('pull_requests_global_0', '_admin/pull_requests/%(pull_request_id)s', ['pull_request_id']);
73 pyroutes.register('pull_requests_global_0', '_admin/pull_requests/%(pull_request_id)s', ['pull_request_id']);
75 pyroutes.register('pull_requests_global_1', '_admin/pull-requests/%(pull_request_id)s', ['pull_request_id']);
74 pyroutes.register('pull_requests_global_1', '_admin/pull-requests/%(pull_request_id)s', ['pull_request_id']);
76 pyroutes.register('pull_requests_global', '_admin/pull-request/%(pull_request_id)s', ['pull_request_id']);
75 pyroutes.register('pull_requests_global', '_admin/pull-request/%(pull_request_id)s', ['pull_request_id']);
77 pyroutes.register('admin_settings_open_source', '_admin/settings/open_source', []);
76 pyroutes.register('admin_settings_open_source', '_admin/settings/open_source', []);
78 pyroutes.register('admin_settings_vcs_svn_generate_cfg', '_admin/settings/vcs/svn_generate_cfg', []);
77 pyroutes.register('admin_settings_vcs_svn_generate_cfg', '_admin/settings/vcs/svn_generate_cfg', []);
79 pyroutes.register('admin_settings_system', '_admin/settings/system', []);
78 pyroutes.register('admin_settings_system', '_admin/settings/system', []);
80 pyroutes.register('admin_settings_system_update', '_admin/settings/system/updates', []);
79 pyroutes.register('admin_settings_system_update', '_admin/settings/system/updates', []);
81 pyroutes.register('admin_settings_sessions', '_admin/settings/sessions', []);
80 pyroutes.register('admin_settings_sessions', '_admin/settings/sessions', []);
82 pyroutes.register('admin_settings_sessions_cleanup', '_admin/settings/sessions/cleanup', []);
81 pyroutes.register('admin_settings_sessions_cleanup', '_admin/settings/sessions/cleanup', []);
83 pyroutes.register('users', '_admin/users', []);
82 pyroutes.register('users', '_admin/users', []);
84 pyroutes.register('users_data', '_admin/users_data', []);
83 pyroutes.register('users_data', '_admin/users_data', []);
85 pyroutes.register('edit_user_auth_tokens', '_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
84 pyroutes.register('edit_user_auth_tokens', '_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
86 pyroutes.register('edit_user_auth_tokens_add', '_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
85 pyroutes.register('edit_user_auth_tokens_add', '_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
87 pyroutes.register('edit_user_auth_tokens_delete', '_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
86 pyroutes.register('edit_user_auth_tokens_delete', '_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
88 pyroutes.register('edit_user_groups_management', '_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
87 pyroutes.register('edit_user_groups_management', '_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
89 pyroutes.register('edit_user_groups_management_updates', '_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
88 pyroutes.register('edit_user_groups_management_updates', '_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
90 pyroutes.register('edit_user_audit_logs', '_admin/users/%(user_id)s/edit/audit', ['user_id']);
89 pyroutes.register('edit_user_audit_logs', '_admin/users/%(user_id)s/edit/audit', ['user_id']);
91 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
90 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
92 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
91 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
93 pyroutes.register('channelstream_proxy', '/_channelstream', []);
92 pyroutes.register('channelstream_proxy', '/_channelstream', []);
94 pyroutes.register('login', '/_admin/login', []);
93 pyroutes.register('login', '/_admin/login', []);
95 pyroutes.register('logout', '/_admin/logout', []);
94 pyroutes.register('logout', '/_admin/logout', []);
96 pyroutes.register('register', '/_admin/register', []);
95 pyroutes.register('register', '/_admin/register', []);
97 pyroutes.register('reset_password', '/_admin/password_reset', []);
96 pyroutes.register('reset_password', '/_admin/password_reset', []);
98 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
97 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
99 pyroutes.register('home', '/', []);
98 pyroutes.register('home', '/', []);
100 pyroutes.register('user_autocomplete_data', '/_users', []);
99 pyroutes.register('user_autocomplete_data', '/_users', []);
101 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
100 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
102 pyroutes.register('repo_list_data', '/_repos', []);
101 pyroutes.register('repo_list_data', '/_repos', []);
103 pyroutes.register('goto_switcher_data', '/_goto_data', []);
102 pyroutes.register('goto_switcher_data', '/_goto_data', []);
104 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
103 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
105 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
104 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
106 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
105 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
107 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
106 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
108 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
107 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
108 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
109 pyroutes.register('pullrequest_show_all_data', '/%(repo_name)s/pull-request-data', ['repo_name']);
109 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
110 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
110 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
111 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
111 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
112 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
112 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
113 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
113 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
114 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
114 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
115 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
115 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
116 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
116 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
117 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
117 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
118 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
118 pyroutes.register('repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
119 pyroutes.register('repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
119 pyroutes.register('repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
120 pyroutes.register('repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
120 pyroutes.register('strip', '/%(repo_name)s/settings/strip', ['repo_name']);
121 pyroutes.register('strip', '/%(repo_name)s/settings/strip', ['repo_name']);
121 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
122 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
122 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
123 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
123 pyroutes.register('search', '/_admin/search', []);
124 pyroutes.register('search', '/_admin/search', []);
124 pyroutes.register('search_repo', '/%(repo_name)s/search', ['repo_name']);
125 pyroutes.register('search_repo', '/%(repo_name)s/search', ['repo_name']);
125 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
126 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
126 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
127 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
127 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
128 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
128 pyroutes.register('my_account_password_update', '/_admin/my_account/password', []);
129 pyroutes.register('my_account_password_update', '/_admin/my_account/password', []);
129 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
130 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
130 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
131 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
131 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
132 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
132 pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []);
133 pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []);
133 pyroutes.register('apiv2', '/_admin/api', []);
134 pyroutes.register('apiv2', '/_admin/api', []);
134 }
135 }
@@ -1,602 +1,602 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="root.mako"/>
2 <%inherit file="root.mako"/>
3
3
4 <div class="outerwrapper">
4 <div class="outerwrapper">
5 <!-- HEADER -->
5 <!-- HEADER -->
6 <div class="header">
6 <div class="header">
7 <div id="header-inner" class="wrapper">
7 <div id="header-inner" class="wrapper">
8 <div id="logo">
8 <div id="logo">
9 <div class="logo-wrapper">
9 <div class="logo-wrapper">
10 <a href="${h.url('home')}"><img src="${h.asset('images/rhodecode-logo-white-216x60.png')}" alt="RhodeCode"/></a>
10 <a href="${h.url('home')}"><img src="${h.asset('images/rhodecode-logo-white-216x60.png')}" alt="RhodeCode"/></a>
11 </div>
11 </div>
12 %if c.rhodecode_name:
12 %if c.rhodecode_name:
13 <div class="branding">- ${h.branding(c.rhodecode_name)}</div>
13 <div class="branding">- ${h.branding(c.rhodecode_name)}</div>
14 %endif
14 %endif
15 </div>
15 </div>
16 <!-- MENU BAR NAV -->
16 <!-- MENU BAR NAV -->
17 ${self.menu_bar_nav()}
17 ${self.menu_bar_nav()}
18 <!-- END MENU BAR NAV -->
18 <!-- END MENU BAR NAV -->
19 </div>
19 </div>
20 </div>
20 </div>
21 ${self.menu_bar_subnav()}
21 ${self.menu_bar_subnav()}
22 <!-- END HEADER -->
22 <!-- END HEADER -->
23
23
24 <!-- CONTENT -->
24 <!-- CONTENT -->
25 <div id="content" class="wrapper">
25 <div id="content" class="wrapper">
26
26
27 <rhodecode-toast id="notifications"></rhodecode-toast>
27 <rhodecode-toast id="notifications"></rhodecode-toast>
28
28
29 <div class="main">
29 <div class="main">
30 ${next.main()}
30 ${next.main()}
31 </div>
31 </div>
32 </div>
32 </div>
33 <!-- END CONTENT -->
33 <!-- END CONTENT -->
34
34
35 </div>
35 </div>
36 <!-- FOOTER -->
36 <!-- FOOTER -->
37 <div id="footer">
37 <div id="footer">
38 <div id="footer-inner" class="title wrapper">
38 <div id="footer-inner" class="title wrapper">
39 <div>
39 <div>
40 <p class="footer-link-right">
40 <p class="footer-link-right">
41 % if c.visual.show_version:
41 % if c.visual.show_version:
42 RhodeCode Enterprise ${c.rhodecode_version} ${c.rhodecode_edition}
42 RhodeCode Enterprise ${c.rhodecode_version} ${c.rhodecode_edition}
43 % endif
43 % endif
44 &copy; 2010-${h.datetime.today().year}, <a href="${h.route_url('rhodecode_official')}" target="_blank">RhodeCode GmbH</a>. All rights reserved.
44 &copy; 2010-${h.datetime.today().year}, <a href="${h.route_url('rhodecode_official')}" target="_blank">RhodeCode GmbH</a>. All rights reserved.
45 % if c.visual.rhodecode_support_url:
45 % if c.visual.rhodecode_support_url:
46 <a href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a>
46 <a href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a>
47 % endif
47 % endif
48 </p>
48 </p>
49 <% sid = 'block' if request.GET.get('showrcid') else 'none' %>
49 <% sid = 'block' if request.GET.get('showrcid') else 'none' %>
50 <p class="server-instance" style="display:${sid}">
50 <p class="server-instance" style="display:${sid}">
51 ## display hidden instance ID if specially defined
51 ## display hidden instance ID if specially defined
52 % if c.rhodecode_instanceid:
52 % if c.rhodecode_instanceid:
53 ${_('RhodeCode instance id: %s') % c.rhodecode_instanceid}
53 ${_('RhodeCode instance id: %s') % c.rhodecode_instanceid}
54 % endif
54 % endif
55 </p>
55 </p>
56 </div>
56 </div>
57 </div>
57 </div>
58 </div>
58 </div>
59
59
60 <!-- END FOOTER -->
60 <!-- END FOOTER -->
61
61
62 ### MAKO DEFS ###
62 ### MAKO DEFS ###
63
63
64 <%def name="menu_bar_subnav()">
64 <%def name="menu_bar_subnav()">
65 </%def>
65 </%def>
66
66
67 <%def name="breadcrumbs(class_='breadcrumbs')">
67 <%def name="breadcrumbs(class_='breadcrumbs')">
68 <div class="${class_}">
68 <div class="${class_}">
69 ${self.breadcrumbs_links()}
69 ${self.breadcrumbs_links()}
70 </div>
70 </div>
71 </%def>
71 </%def>
72
72
73 <%def name="admin_menu()">
73 <%def name="admin_menu()">
74 <ul class="admin_menu submenu">
74 <ul class="admin_menu submenu">
75 <li><a href="${h.route_path('admin_audit_logs')}">${_('Admin audit logs')}</a></li>
75 <li><a href="${h.route_path('admin_audit_logs')}">${_('Admin audit logs')}</a></li>
76 <li><a href="${h.url('repos')}">${_('Repositories')}</a></li>
76 <li><a href="${h.url('repos')}">${_('Repositories')}</a></li>
77 <li><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
77 <li><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
78 <li><a href="${h.route_path('users')}">${_('Users')}</a></li>
78 <li><a href="${h.route_path('users')}">${_('Users')}</a></li>
79 <li><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
79 <li><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
80 <li><a href="${h.url('admin_permissions_application')}">${_('Permissions')}</a></li>
80 <li><a href="${h.url('admin_permissions_application')}">${_('Permissions')}</a></li>
81 <li><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li>
81 <li><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li>
82 <li><a href="${h.route_path('global_integrations_home')}">${_('Integrations')}</a></li>
82 <li><a href="${h.route_path('global_integrations_home')}">${_('Integrations')}</a></li>
83 <li><a href="${h.url('admin_defaults_repositories')}">${_('Defaults')}</a></li>
83 <li><a href="${h.url('admin_defaults_repositories')}">${_('Defaults')}</a></li>
84 <li class="last"><a href="${h.url('admin_settings')}">${_('Settings')}</a></li>
84 <li class="last"><a href="${h.url('admin_settings')}">${_('Settings')}</a></li>
85 </ul>
85 </ul>
86 </%def>
86 </%def>
87
87
88
88
89 <%def name="dt_info_panel(elements)">
89 <%def name="dt_info_panel(elements)">
90 <dl class="dl-horizontal">
90 <dl class="dl-horizontal">
91 %for dt, dd, title, show_items in elements:
91 %for dt, dd, title, show_items in elements:
92 <dt>${dt}:</dt>
92 <dt>${dt}:</dt>
93 <dd title="${title}">
93 <dd title="${title}">
94 %if callable(dd):
94 %if callable(dd):
95 ## allow lazy evaluation of elements
95 ## allow lazy evaluation of elements
96 ${dd()}
96 ${dd()}
97 %else:
97 %else:
98 ${dd}
98 ${dd}
99 %endif
99 %endif
100 %if show_items:
100 %if show_items:
101 <span class="btn-collapse" data-toggle="item-${h.md5_safe(dt)[:6]}-details">${_('Show More')} </span>
101 <span class="btn-collapse" data-toggle="item-${h.md5_safe(dt)[:6]}-details">${_('Show More')} </span>
102 %endif
102 %endif
103 </dd>
103 </dd>
104
104
105 %if show_items:
105 %if show_items:
106 <div class="collapsable-content" data-toggle="item-${h.md5_safe(dt)[:6]}-details" style="display: none">
106 <div class="collapsable-content" data-toggle="item-${h.md5_safe(dt)[:6]}-details" style="display: none">
107 %for item in show_items:
107 %for item in show_items:
108 <dt></dt>
108 <dt></dt>
109 <dd>${item}</dd>
109 <dd>${item}</dd>
110 %endfor
110 %endfor
111 </div>
111 </div>
112 %endif
112 %endif
113
113
114 %endfor
114 %endfor
115 </dl>
115 </dl>
116 </%def>
116 </%def>
117
117
118
118
119 <%def name="gravatar(email, size=16)">
119 <%def name="gravatar(email, size=16)">
120 <%
120 <%
121 if (size > 16):
121 if (size > 16):
122 gravatar_class = 'gravatar gravatar-large'
122 gravatar_class = 'gravatar gravatar-large'
123 else:
123 else:
124 gravatar_class = 'gravatar'
124 gravatar_class = 'gravatar'
125 %>
125 %>
126 <%doc>
126 <%doc>
127 TODO: johbo: For now we serve double size images to make it smooth
127 TODO: johbo: For now we serve double size images to make it smooth
128 for retina. This is how it worked until now. Should be replaced
128 for retina. This is how it worked until now. Should be replaced
129 with a better solution at some point.
129 with a better solution at some point.
130 </%doc>
130 </%doc>
131 <img class="${gravatar_class}" src="${h.gravatar_url(email, size * 2)}" height="${size}" width="${size}">
131 <img class="${gravatar_class}" src="${h.gravatar_url(email, size * 2)}" height="${size}" width="${size}">
132 </%def>
132 </%def>
133
133
134
134
135 <%def name="gravatar_with_user(contact, size=16, show_disabled=False)">
135 <%def name="gravatar_with_user(contact, size=16, show_disabled=False)">
136 <% email = h.email_or_none(contact) %>
136 <% email = h.email_or_none(contact) %>
137 <div class="rc-user tooltip" title="${h.author_string(email)}">
137 <div class="rc-user tooltip" title="${h.author_string(email)}">
138 ${self.gravatar(email, size)}
138 ${self.gravatar(email, size)}
139 <span class="${'user user-disabled' if show_disabled else 'user'}"> ${h.link_to_user(contact)}</span>
139 <span class="${'user user-disabled' if show_disabled else 'user'}"> ${h.link_to_user(contact)}</span>
140 </div>
140 </div>
141 </%def>
141 </%def>
142
142
143
143
144 ## admin menu used for people that have some admin resources
144 ## admin menu used for people that have some admin resources
145 <%def name="admin_menu_simple(repositories=None, repository_groups=None, user_groups=None)">
145 <%def name="admin_menu_simple(repositories=None, repository_groups=None, user_groups=None)">
146 <ul class="submenu">
146 <ul class="submenu">
147 %if repositories:
147 %if repositories:
148 <li class="local-admin-repos"><a href="${h.url('repos')}">${_('Repositories')}</a></li>
148 <li class="local-admin-repos"><a href="${h.url('repos')}">${_('Repositories')}</a></li>
149 %endif
149 %endif
150 %if repository_groups:
150 %if repository_groups:
151 <li class="local-admin-repo-groups"><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
151 <li class="local-admin-repo-groups"><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
152 %endif
152 %endif
153 %if user_groups:
153 %if user_groups:
154 <li class="local-admin-user-groups"><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
154 <li class="local-admin-user-groups"><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
155 %endif
155 %endif
156 </ul>
156 </ul>
157 </%def>
157 </%def>
158
158
159 <%def name="repo_page_title(repo_instance)">
159 <%def name="repo_page_title(repo_instance)">
160 <div class="title-content">
160 <div class="title-content">
161 <div class="title-main">
161 <div class="title-main">
162 ## SVN/HG/GIT icons
162 ## SVN/HG/GIT icons
163 %if h.is_hg(repo_instance):
163 %if h.is_hg(repo_instance):
164 <i class="icon-hg"></i>
164 <i class="icon-hg"></i>
165 %endif
165 %endif
166 %if h.is_git(repo_instance):
166 %if h.is_git(repo_instance):
167 <i class="icon-git"></i>
167 <i class="icon-git"></i>
168 %endif
168 %endif
169 %if h.is_svn(repo_instance):
169 %if h.is_svn(repo_instance):
170 <i class="icon-svn"></i>
170 <i class="icon-svn"></i>
171 %endif
171 %endif
172
172
173 ## public/private
173 ## public/private
174 %if repo_instance.private:
174 %if repo_instance.private:
175 <i class="icon-repo-private"></i>
175 <i class="icon-repo-private"></i>
176 %else:
176 %else:
177 <i class="icon-repo-public"></i>
177 <i class="icon-repo-public"></i>
178 %endif
178 %endif
179
179
180 ## repo name with group name
180 ## repo name with group name
181 ${h.breadcrumb_repo_link(c.rhodecode_db_repo)}
181 ${h.breadcrumb_repo_link(c.rhodecode_db_repo)}
182
182
183 </div>
183 </div>
184
184
185 ## FORKED
185 ## FORKED
186 %if repo_instance.fork:
186 %if repo_instance.fork:
187 <p>
187 <p>
188 <i class="icon-code-fork"></i> ${_('Fork of')}
188 <i class="icon-code-fork"></i> ${_('Fork of')}
189 <a href="${h.url('summary_home',repo_name=repo_instance.fork.repo_name)}">${repo_instance.fork.repo_name}</a>
189 <a href="${h.url('summary_home',repo_name=repo_instance.fork.repo_name)}">${repo_instance.fork.repo_name}</a>
190 </p>
190 </p>
191 %endif
191 %endif
192
192
193 ## IMPORTED FROM REMOTE
193 ## IMPORTED FROM REMOTE
194 %if repo_instance.clone_uri:
194 %if repo_instance.clone_uri:
195 <p>
195 <p>
196 <i class="icon-code-fork"></i> ${_('Clone from')}
196 <i class="icon-code-fork"></i> ${_('Clone from')}
197 <a href="${h.url(h.safe_str(h.hide_credentials(repo_instance.clone_uri)))}">${h.hide_credentials(repo_instance.clone_uri)}</a>
197 <a href="${h.url(h.safe_str(h.hide_credentials(repo_instance.clone_uri)))}">${h.hide_credentials(repo_instance.clone_uri)}</a>
198 </p>
198 </p>
199 %endif
199 %endif
200
200
201 ## LOCKING STATUS
201 ## LOCKING STATUS
202 %if repo_instance.locked[0]:
202 %if repo_instance.locked[0]:
203 <p class="locking_locked">
203 <p class="locking_locked">
204 <i class="icon-repo-lock"></i>
204 <i class="icon-repo-lock"></i>
205 ${_('Repository locked by %(user)s') % {'user': h.person_by_id(repo_instance.locked[0])}}
205 ${_('Repository locked by %(user)s') % {'user': h.person_by_id(repo_instance.locked[0])}}
206 </p>
206 </p>
207 %elif repo_instance.enable_locking:
207 %elif repo_instance.enable_locking:
208 <p class="locking_unlocked">
208 <p class="locking_unlocked">
209 <i class="icon-repo-unlock"></i>
209 <i class="icon-repo-unlock"></i>
210 ${_('Repository not locked. Pull repository to lock it.')}
210 ${_('Repository not locked. Pull repository to lock it.')}
211 </p>
211 </p>
212 %endif
212 %endif
213
213
214 </div>
214 </div>
215 </%def>
215 </%def>
216
216
217 <%def name="repo_menu(active=None)">
217 <%def name="repo_menu(active=None)">
218 <%
218 <%
219 def is_active(selected):
219 def is_active(selected):
220 if selected == active:
220 if selected == active:
221 return "active"
221 return "active"
222 %>
222 %>
223
223
224 <!--- CONTEXT BAR -->
224 <!--- CONTEXT BAR -->
225 <div id="context-bar">
225 <div id="context-bar">
226 <div class="wrapper">
226 <div class="wrapper">
227 <ul id="context-pages" class="horizontal-list navigation">
227 <ul id="context-pages" class="horizontal-list navigation">
228 <li class="${is_active('summary')}"><a class="menulink" href="${h.url('summary_home', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li>
228 <li class="${is_active('summary')}"><a class="menulink" href="${h.url('summary_home', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li>
229 <li class="${is_active('changelog')}"><a class="menulink" href="${h.url('changelog_home', repo_name=c.repo_name)}"><div class="menulabel">${_('Changelog')}</div></a></li>
229 <li class="${is_active('changelog')}"><a class="menulink" href="${h.url('changelog_home', repo_name=c.repo_name)}"><div class="menulabel">${_('Changelog')}</div></a></li>
230 <li class="${is_active('files')}"><a class="menulink" href="${h.url('files_home', repo_name=c.repo_name, revision=c.rhodecode_db_repo.landing_rev[1])}"><div class="menulabel">${_('Files')}</div></a></li>
230 <li class="${is_active('files')}"><a class="menulink" href="${h.url('files_home', repo_name=c.repo_name, revision=c.rhodecode_db_repo.landing_rev[1])}"><div class="menulabel">${_('Files')}</div></a></li>
231 <li class="${is_active('compare')}">
231 <li class="${is_active('compare')}">
232 <a class="menulink" href="${h.url('compare_home',repo_name=c.repo_name)}"><div class="menulabel">${_('Compare')}</div></a>
232 <a class="menulink" href="${h.url('compare_home',repo_name=c.repo_name)}"><div class="menulabel">${_('Compare')}</div></a>
233 </li>
233 </li>
234 ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()"
234 ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()"
235 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
235 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
236 <li class="${is_active('showpullrequest')}">
236 <li class="${is_active('showpullrequest')}">
237 <a class="menulink" href="${h.url('pullrequest_show_all',repo_name=c.repo_name)}" title="${_('Show Pull Requests for %s') % c.repo_name}">
237 <a class="menulink" href="${h.route_path('pullrequest_show_all', repo_name=c.repo_name)}" title="${_('Show Pull Requests for %s') % c.repo_name}">
238 %if c.repository_pull_requests:
238 %if c.repository_pull_requests:
239 <span class="pr_notifications">${c.repository_pull_requests}</span>
239 <span class="pr_notifications">${c.repository_pull_requests}</span>
240 %endif
240 %endif
241 <div class="menulabel">${_('Pull Requests')}</div>
241 <div class="menulabel">${_('Pull Requests')}</div>
242 </a>
242 </a>
243 </li>
243 </li>
244 %endif
244 %endif
245 <li class="${is_active('options')}">
245 <li class="${is_active('options')}">
246 <a class="menulink" href="#" class="dropdown"><div class="menulabel">${_('Options')} <div class="show_more"></div></div></a>
246 <a class="menulink" href="#" class="dropdown"><div class="menulabel">${_('Options')} <div class="show_more"></div></div></a>
247 <ul class="submenu">
247 <ul class="submenu">
248 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
248 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
249 <li><a href="${h.route_path('edit_repo',repo_name=c.repo_name)}">${_('Settings')}</a></li>
249 <li><a href="${h.route_path('edit_repo',repo_name=c.repo_name)}">${_('Settings')}</a></li>
250 %endif
250 %endif
251 %if c.rhodecode_db_repo.fork:
251 %if c.rhodecode_db_repo.fork:
252 <li><a href="${h.url('compare_url',repo_name=c.rhodecode_db_repo.fork.repo_name,source_ref_type=c.rhodecode_db_repo.landing_rev[0],source_ref=c.rhodecode_db_repo.landing_rev[1], target_repo=c.repo_name,target_ref_type='branch' if request.GET.get('branch') else c.rhodecode_db_repo.landing_rev[0],target_ref=request.GET.get('branch') or c.rhodecode_db_repo.landing_rev[1], merge=1)}">
252 <li><a href="${h.url('compare_url',repo_name=c.rhodecode_db_repo.fork.repo_name,source_ref_type=c.rhodecode_db_repo.landing_rev[0],source_ref=c.rhodecode_db_repo.landing_rev[1], target_repo=c.repo_name,target_ref_type='branch' if request.GET.get('branch') else c.rhodecode_db_repo.landing_rev[0],target_ref=request.GET.get('branch') or c.rhodecode_db_repo.landing_rev[1], merge=1)}">
253 ${_('Compare fork')}</a></li>
253 ${_('Compare fork')}</a></li>
254 %endif
254 %endif
255
255
256 <li><a href="${h.route_path('search_repo',repo_name=c.repo_name)}">${_('Search')}</a></li>
256 <li><a href="${h.route_path('search_repo',repo_name=c.repo_name)}">${_('Search')}</a></li>
257
257
258 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking:
258 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking:
259 %if c.rhodecode_db_repo.locked[0]:
259 %if c.rhodecode_db_repo.locked[0]:
260 <li><a class="locking_del" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Unlock')}</a></li>
260 <li><a class="locking_del" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Unlock')}</a></li>
261 %else:
261 %else:
262 <li><a class="locking_add" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Lock')}</a></li>
262 <li><a class="locking_add" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Lock')}</a></li>
263 %endif
263 %endif
264 %endif
264 %endif
265 %if c.rhodecode_user.username != h.DEFAULT_USER:
265 %if c.rhodecode_user.username != h.DEFAULT_USER:
266 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
266 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
267 <li><a href="${h.url('repo_fork_home',repo_name=c.repo_name)}">${_('Fork')}</a></li>
267 <li><a href="${h.url('repo_fork_home',repo_name=c.repo_name)}">${_('Fork')}</a></li>
268 <li><a href="${h.url('pullrequest_home',repo_name=c.repo_name)}">${_('Create Pull Request')}</a></li>
268 <li><a href="${h.url('pullrequest_home',repo_name=c.repo_name)}">${_('Create Pull Request')}</a></li>
269 %endif
269 %endif
270 %endif
270 %endif
271 </ul>
271 </ul>
272 </li>
272 </li>
273 </ul>
273 </ul>
274 </div>
274 </div>
275 <div class="clear"></div>
275 <div class="clear"></div>
276 </div>
276 </div>
277 <!--- END CONTEXT BAR -->
277 <!--- END CONTEXT BAR -->
278
278
279 </%def>
279 </%def>
280
280
281 <%def name="usermenu(active=False)">
281 <%def name="usermenu(active=False)">
282 ## USER MENU
282 ## USER MENU
283 <li id="quick_login_li" class="${'active' if active else ''}">
283 <li id="quick_login_li" class="${'active' if active else ''}">
284 <a id="quick_login_link" class="menulink childs">
284 <a id="quick_login_link" class="menulink childs">
285 ${gravatar(c.rhodecode_user.email, 20)}
285 ${gravatar(c.rhodecode_user.email, 20)}
286 <span class="user">
286 <span class="user">
287 %if c.rhodecode_user.username != h.DEFAULT_USER:
287 %if c.rhodecode_user.username != h.DEFAULT_USER:
288 <span class="menu_link_user">${c.rhodecode_user.username}</span><div class="show_more"></div>
288 <span class="menu_link_user">${c.rhodecode_user.username}</span><div class="show_more"></div>
289 %else:
289 %else:
290 <span>${_('Sign in')}</span>
290 <span>${_('Sign in')}</span>
291 %endif
291 %endif
292 </span>
292 </span>
293 </a>
293 </a>
294
294
295 <div class="user-menu submenu">
295 <div class="user-menu submenu">
296 <div id="quick_login">
296 <div id="quick_login">
297 %if c.rhodecode_user.username == h.DEFAULT_USER:
297 %if c.rhodecode_user.username == h.DEFAULT_USER:
298 <h4>${_('Sign in to your account')}</h4>
298 <h4>${_('Sign in to your account')}</h4>
299 ${h.form(h.route_path('login', _query={'came_from': h.url.current()}), needs_csrf_token=False)}
299 ${h.form(h.route_path('login', _query={'came_from': h.url.current()}), needs_csrf_token=False)}
300 <div class="form form-vertical">
300 <div class="form form-vertical">
301 <div class="fields">
301 <div class="fields">
302 <div class="field">
302 <div class="field">
303 <div class="label">
303 <div class="label">
304 <label for="username">${_('Username')}:</label>
304 <label for="username">${_('Username')}:</label>
305 </div>
305 </div>
306 <div class="input">
306 <div class="input">
307 ${h.text('username',class_='focus',tabindex=1)}
307 ${h.text('username',class_='focus',tabindex=1)}
308 </div>
308 </div>
309
309
310 </div>
310 </div>
311 <div class="field">
311 <div class="field">
312 <div class="label">
312 <div class="label">
313 <label for="password">${_('Password')}:</label>
313 <label for="password">${_('Password')}:</label>
314 %if h.HasPermissionAny('hg.password_reset.enabled')():
314 %if h.HasPermissionAny('hg.password_reset.enabled')():
315 <span class="forgot_password">${h.link_to(_('(Forgot password?)'),h.route_path('reset_password'), class_='pwd_reset')}</span>
315 <span class="forgot_password">${h.link_to(_('(Forgot password?)'),h.route_path('reset_password'), class_='pwd_reset')}</span>
316 %endif
316 %endif
317 </div>
317 </div>
318 <div class="input">
318 <div class="input">
319 ${h.password('password',class_='focus',tabindex=2)}
319 ${h.password('password',class_='focus',tabindex=2)}
320 </div>
320 </div>
321 </div>
321 </div>
322 <div class="buttons">
322 <div class="buttons">
323 <div class="register">
323 <div class="register">
324 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
324 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
325 ${h.link_to(_("Don't have an account?"),h.route_path('register'))} <br/>
325 ${h.link_to(_("Don't have an account?"),h.route_path('register'))} <br/>
326 %endif
326 %endif
327 ${h.link_to(_("Using external auth? Sign In here."),h.route_path('login'))}
327 ${h.link_to(_("Using external auth? Sign In here."),h.route_path('login'))}
328 </div>
328 </div>
329 <div class="submit">
329 <div class="submit">
330 ${h.submit('sign_in',_('Sign In'),class_="btn btn-small",tabindex=3)}
330 ${h.submit('sign_in',_('Sign In'),class_="btn btn-small",tabindex=3)}
331 </div>
331 </div>
332 </div>
332 </div>
333 </div>
333 </div>
334 </div>
334 </div>
335 ${h.end_form()}
335 ${h.end_form()}
336 %else:
336 %else:
337 <div class="">
337 <div class="">
338 <div class="big_gravatar">${gravatar(c.rhodecode_user.email, 48)}</div>
338 <div class="big_gravatar">${gravatar(c.rhodecode_user.email, 48)}</div>
339 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
339 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
340 <div class="email">${c.rhodecode_user.email}</div>
340 <div class="email">${c.rhodecode_user.email}</div>
341 </div>
341 </div>
342 <div class="">
342 <div class="">
343 <ol class="links">
343 <ol class="links">
344 <li>${h.link_to(_(u'My account'),h.route_path('my_account_profile'))}</li>
344 <li>${h.link_to(_(u'My account'),h.route_path('my_account_profile'))}</li>
345 % if c.rhodecode_user.personal_repo_group:
345 % if c.rhodecode_user.personal_repo_group:
346 <li>${h.link_to(_(u'My personal group'), h.url('repo_group_home', group_name=c.rhodecode_user.personal_repo_group.group_name))}</li>
346 <li>${h.link_to(_(u'My personal group'), h.url('repo_group_home', group_name=c.rhodecode_user.personal_repo_group.group_name))}</li>
347 % endif
347 % endif
348 <li class="logout">
348 <li class="logout">
349 ${h.secure_form(h.route_path('logout'))}
349 ${h.secure_form(h.route_path('logout'))}
350 ${h.submit('log_out', _(u'Sign Out'),class_="btn btn-primary")}
350 ${h.submit('log_out', _(u'Sign Out'),class_="btn btn-primary")}
351 ${h.end_form()}
351 ${h.end_form()}
352 </li>
352 </li>
353 </ol>
353 </ol>
354 </div>
354 </div>
355 %endif
355 %endif
356 </div>
356 </div>
357 </div>
357 </div>
358 %if c.rhodecode_user.username != h.DEFAULT_USER:
358 %if c.rhodecode_user.username != h.DEFAULT_USER:
359 <div class="pill_container">
359 <div class="pill_container">
360 % if c.unread_notifications == 0:
360 % if c.unread_notifications == 0:
361 <a class="menu_link_notifications empty" href="${h.url('notifications')}">${c.unread_notifications}</a>
361 <a class="menu_link_notifications empty" href="${h.url('notifications')}">${c.unread_notifications}</a>
362 % else:
362 % else:
363 <a class="menu_link_notifications" href="${h.url('notifications')}">${c.unread_notifications}</a>
363 <a class="menu_link_notifications" href="${h.url('notifications')}">${c.unread_notifications}</a>
364 % endif
364 % endif
365 </div>
365 </div>
366 % endif
366 % endif
367 </li>
367 </li>
368 </%def>
368 </%def>
369
369
370 <%def name="menu_items(active=None)">
370 <%def name="menu_items(active=None)">
371 <%
371 <%
372 def is_active(selected):
372 def is_active(selected):
373 if selected == active:
373 if selected == active:
374 return "active"
374 return "active"
375 return ""
375 return ""
376 %>
376 %>
377 <ul id="quick" class="main_nav navigation horizontal-list">
377 <ul id="quick" class="main_nav navigation horizontal-list">
378 <!-- repo switcher -->
378 <!-- repo switcher -->
379 <li class="${is_active('repositories')} repo_switcher_li has_select2">
379 <li class="${is_active('repositories')} repo_switcher_li has_select2">
380 <input id="repo_switcher" name="repo_switcher" type="hidden">
380 <input id="repo_switcher" name="repo_switcher" type="hidden">
381 </li>
381 </li>
382
382
383 ## ROOT MENU
383 ## ROOT MENU
384 %if c.rhodecode_user.username != h.DEFAULT_USER:
384 %if c.rhodecode_user.username != h.DEFAULT_USER:
385 <li class="${is_active('journal')}">
385 <li class="${is_active('journal')}">
386 <a class="menulink" title="${_('Show activity journal')}" href="${h.url('journal')}">
386 <a class="menulink" title="${_('Show activity journal')}" href="${h.url('journal')}">
387 <div class="menulabel">${_('Journal')}</div>
387 <div class="menulabel">${_('Journal')}</div>
388 </a>
388 </a>
389 </li>
389 </li>
390 %else:
390 %else:
391 <li class="${is_active('journal')}">
391 <li class="${is_active('journal')}">
392 <a class="menulink" title="${_('Show Public activity journal')}" href="${h.url('public_journal')}">
392 <a class="menulink" title="${_('Show Public activity journal')}" href="${h.url('public_journal')}">
393 <div class="menulabel">${_('Public journal')}</div>
393 <div class="menulabel">${_('Public journal')}</div>
394 </a>
394 </a>
395 </li>
395 </li>
396 %endif
396 %endif
397 <li class="${is_active('gists')}">
397 <li class="${is_active('gists')}">
398 <a class="menulink childs" title="${_('Show Gists')}" href="${h.url('gists')}">
398 <a class="menulink childs" title="${_('Show Gists')}" href="${h.url('gists')}">
399 <div class="menulabel">${_('Gists')}</div>
399 <div class="menulabel">${_('Gists')}</div>
400 </a>
400 </a>
401 </li>
401 </li>
402 <li class="${is_active('search')}">
402 <li class="${is_active('search')}">
403 <a class="menulink" title="${_('Search in repositories you have access to')}" href="${h.route_path('search')}">
403 <a class="menulink" title="${_('Search in repositories you have access to')}" href="${h.route_path('search')}">
404 <div class="menulabel">${_('Search')}</div>
404 <div class="menulabel">${_('Search')}</div>
405 </a>
405 </a>
406 </li>
406 </li>
407 % if h.HasPermissionAll('hg.admin')('access admin main page'):
407 % if h.HasPermissionAll('hg.admin')('access admin main page'):
408 <li class="${is_active('admin')}">
408 <li class="${is_active('admin')}">
409 <a class="menulink childs" title="${_('Admin settings')}" href="#" onclick="return false;">
409 <a class="menulink childs" title="${_('Admin settings')}" href="#" onclick="return false;">
410 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
410 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
411 </a>
411 </a>
412 ${admin_menu()}
412 ${admin_menu()}
413 </li>
413 </li>
414 % elif c.rhodecode_user.repositories_admin or c.rhodecode_user.repository_groups_admin or c.rhodecode_user.user_groups_admin:
414 % elif c.rhodecode_user.repositories_admin or c.rhodecode_user.repository_groups_admin or c.rhodecode_user.user_groups_admin:
415 <li class="${is_active('admin')}">
415 <li class="${is_active('admin')}">
416 <a class="menulink childs" title="${_('Delegated Admin settings')}">
416 <a class="menulink childs" title="${_('Delegated Admin settings')}">
417 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
417 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
418 </a>
418 </a>
419 ${admin_menu_simple(c.rhodecode_user.repositories_admin,
419 ${admin_menu_simple(c.rhodecode_user.repositories_admin,
420 c.rhodecode_user.repository_groups_admin,
420 c.rhodecode_user.repository_groups_admin,
421 c.rhodecode_user.user_groups_admin or h.HasPermissionAny('hg.usergroup.create.true')())}
421 c.rhodecode_user.user_groups_admin or h.HasPermissionAny('hg.usergroup.create.true')())}
422 </li>
422 </li>
423 % endif
423 % endif
424 % if c.debug_style:
424 % if c.debug_style:
425 <li class="${is_active('debug_style')}">
425 <li class="${is_active('debug_style')}">
426 <a class="menulink" title="${_('Style')}" href="${h.url('debug_style_home')}">
426 <a class="menulink" title="${_('Style')}" href="${h.url('debug_style_home')}">
427 <div class="menulabel">${_('Style')}</div>
427 <div class="menulabel">${_('Style')}</div>
428 </a>
428 </a>
429 </li>
429 </li>
430 % endif
430 % endif
431 ## render extra user menu
431 ## render extra user menu
432 ${usermenu(active=(active=='my_account'))}
432 ${usermenu(active=(active=='my_account'))}
433 </ul>
433 </ul>
434
434
435 <script type="text/javascript">
435 <script type="text/javascript">
436 var visual_show_public_icon = "${c.visual.show_public_icon}" == "True";
436 var visual_show_public_icon = "${c.visual.show_public_icon}" == "True";
437
437
438 /*format the look of items in the list*/
438 /*format the look of items in the list*/
439 var format = function(state, escapeMarkup){
439 var format = function(state, escapeMarkup){
440 if (!state.id){
440 if (!state.id){
441 return state.text; // optgroup
441 return state.text; // optgroup
442 }
442 }
443 var obj_dict = state.obj;
443 var obj_dict = state.obj;
444 var tmpl = '';
444 var tmpl = '';
445
445
446 if(obj_dict && state.type == 'repo'){
446 if(obj_dict && state.type == 'repo'){
447 if(obj_dict['repo_type'] === 'hg'){
447 if(obj_dict['repo_type'] === 'hg'){
448 tmpl += '<i class="icon-hg"></i> ';
448 tmpl += '<i class="icon-hg"></i> ';
449 }
449 }
450 else if(obj_dict['repo_type'] === 'git'){
450 else if(obj_dict['repo_type'] === 'git'){
451 tmpl += '<i class="icon-git"></i> ';
451 tmpl += '<i class="icon-git"></i> ';
452 }
452 }
453 else if(obj_dict['repo_type'] === 'svn'){
453 else if(obj_dict['repo_type'] === 'svn'){
454 tmpl += '<i class="icon-svn"></i> ';
454 tmpl += '<i class="icon-svn"></i> ';
455 }
455 }
456 if(obj_dict['private']){
456 if(obj_dict['private']){
457 tmpl += '<i class="icon-lock" ></i> ';
457 tmpl += '<i class="icon-lock" ></i> ';
458 }
458 }
459 else if(visual_show_public_icon){
459 else if(visual_show_public_icon){
460 tmpl += '<i class="icon-unlock-alt"></i> ';
460 tmpl += '<i class="icon-unlock-alt"></i> ';
461 }
461 }
462 }
462 }
463 if(obj_dict && state.type == 'commit') {
463 if(obj_dict && state.type == 'commit') {
464 tmpl += '<i class="icon-tag"></i>';
464 tmpl += '<i class="icon-tag"></i>';
465 }
465 }
466 if(obj_dict && state.type == 'group'){
466 if(obj_dict && state.type == 'group'){
467 tmpl += '<i class="icon-folder-close"></i> ';
467 tmpl += '<i class="icon-folder-close"></i> ';
468 }
468 }
469 tmpl += escapeMarkup(state.text);
469 tmpl += escapeMarkup(state.text);
470 return tmpl;
470 return tmpl;
471 };
471 };
472
472
473 var formatResult = function(result, container, query, escapeMarkup) {
473 var formatResult = function(result, container, query, escapeMarkup) {
474 return format(result, escapeMarkup);
474 return format(result, escapeMarkup);
475 };
475 };
476
476
477 var formatSelection = function(data, container, escapeMarkup) {
477 var formatSelection = function(data, container, escapeMarkup) {
478 return format(data, escapeMarkup);
478 return format(data, escapeMarkup);
479 };
479 };
480
480
481 $("#repo_switcher").select2({
481 $("#repo_switcher").select2({
482 cachedDataSource: {},
482 cachedDataSource: {},
483 minimumInputLength: 2,
483 minimumInputLength: 2,
484 placeholder: '<div class="menulabel">${_('Go to')} <div class="show_more"></div></div>',
484 placeholder: '<div class="menulabel">${_('Go to')} <div class="show_more"></div></div>',
485 dropdownAutoWidth: true,
485 dropdownAutoWidth: true,
486 formatResult: formatResult,
486 formatResult: formatResult,
487 formatSelection: formatSelection,
487 formatSelection: formatSelection,
488 containerCssClass: "repo-switcher",
488 containerCssClass: "repo-switcher",
489 dropdownCssClass: "repo-switcher-dropdown",
489 dropdownCssClass: "repo-switcher-dropdown",
490 escapeMarkup: function(m){
490 escapeMarkup: function(m){
491 // don't escape our custom placeholder
491 // don't escape our custom placeholder
492 if(m.substr(0,23) == '<div class="menulabel">'){
492 if(m.substr(0,23) == '<div class="menulabel">'){
493 return m;
493 return m;
494 }
494 }
495
495
496 return Select2.util.escapeMarkup(m);
496 return Select2.util.escapeMarkup(m);
497 },
497 },
498 query: $.debounce(250, function(query){
498 query: $.debounce(250, function(query){
499 self = this;
499 self = this;
500 var cacheKey = query.term;
500 var cacheKey = query.term;
501 var cachedData = self.cachedDataSource[cacheKey];
501 var cachedData = self.cachedDataSource[cacheKey];
502
502
503 if (cachedData) {
503 if (cachedData) {
504 query.callback({results: cachedData.results});
504 query.callback({results: cachedData.results});
505 } else {
505 } else {
506 $.ajax({
506 $.ajax({
507 url: pyroutes.url('goto_switcher_data'),
507 url: pyroutes.url('goto_switcher_data'),
508 data: {'query': query.term},
508 data: {'query': query.term},
509 dataType: 'json',
509 dataType: 'json',
510 type: 'GET',
510 type: 'GET',
511 success: function(data) {
511 success: function(data) {
512 self.cachedDataSource[cacheKey] = data;
512 self.cachedDataSource[cacheKey] = data;
513 query.callback({results: data.results});
513 query.callback({results: data.results});
514 },
514 },
515 error: function(data, textStatus, errorThrown) {
515 error: function(data, textStatus, errorThrown) {
516 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
516 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
517 }
517 }
518 })
518 })
519 }
519 }
520 })
520 })
521 });
521 });
522
522
523 $("#repo_switcher").on('select2-selecting', function(e){
523 $("#repo_switcher").on('select2-selecting', function(e){
524 e.preventDefault();
524 e.preventDefault();
525 window.location = e.choice.url;
525 window.location = e.choice.url;
526 });
526 });
527
527
528 </script>
528 </script>
529 <script src="${h.asset('js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
529 <script src="${h.asset('js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
530 </%def>
530 </%def>
531
531
532 <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
532 <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
533 <div class="modal-dialog">
533 <div class="modal-dialog">
534 <div class="modal-content">
534 <div class="modal-content">
535 <div class="modal-header">
535 <div class="modal-header">
536 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
536 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
537 <h4 class="modal-title" id="myModalLabel">${_('Keyboard shortcuts')}</h4>
537 <h4 class="modal-title" id="myModalLabel">${_('Keyboard shortcuts')}</h4>
538 </div>
538 </div>
539 <div class="modal-body">
539 <div class="modal-body">
540 <div class="block-left">
540 <div class="block-left">
541 <table class="keyboard-mappings">
541 <table class="keyboard-mappings">
542 <tbody>
542 <tbody>
543 <tr>
543 <tr>
544 <th></th>
544 <th></th>
545 <th>${_('Site-wide shortcuts')}</th>
545 <th>${_('Site-wide shortcuts')}</th>
546 </tr>
546 </tr>
547 <%
547 <%
548 elems = [
548 elems = [
549 ('/', 'Open quick search box'),
549 ('/', 'Open quick search box'),
550 ('g h', 'Goto home page'),
550 ('g h', 'Goto home page'),
551 ('g g', 'Goto my private gists page'),
551 ('g g', 'Goto my private gists page'),
552 ('g G', 'Goto my public gists page'),
552 ('g G', 'Goto my public gists page'),
553 ('n r', 'New repository page'),
553 ('n r', 'New repository page'),
554 ('n g', 'New gist page'),
554 ('n g', 'New gist page'),
555 ]
555 ]
556 %>
556 %>
557 %for key, desc in elems:
557 %for key, desc in elems:
558 <tr>
558 <tr>
559 <td class="keys">
559 <td class="keys">
560 <span class="key tag">${key}</span>
560 <span class="key tag">${key}</span>
561 </td>
561 </td>
562 <td>${desc}</td>
562 <td>${desc}</td>
563 </tr>
563 </tr>
564 %endfor
564 %endfor
565 </tbody>
565 </tbody>
566 </table>
566 </table>
567 </div>
567 </div>
568 <div class="block-left">
568 <div class="block-left">
569 <table class="keyboard-mappings">
569 <table class="keyboard-mappings">
570 <tbody>
570 <tbody>
571 <tr>
571 <tr>
572 <th></th>
572 <th></th>
573 <th>${_('Repositories')}</th>
573 <th>${_('Repositories')}</th>
574 </tr>
574 </tr>
575 <%
575 <%
576 elems = [
576 elems = [
577 ('g s', 'Goto summary page'),
577 ('g s', 'Goto summary page'),
578 ('g c', 'Goto changelog page'),
578 ('g c', 'Goto changelog page'),
579 ('g f', 'Goto files page'),
579 ('g f', 'Goto files page'),
580 ('g F', 'Goto files page with file search activated'),
580 ('g F', 'Goto files page with file search activated'),
581 ('g p', 'Goto pull requests page'),
581 ('g p', 'Goto pull requests page'),
582 ('g o', 'Goto repository settings'),
582 ('g o', 'Goto repository settings'),
583 ('g O', 'Goto repository permissions settings'),
583 ('g O', 'Goto repository permissions settings'),
584 ]
584 ]
585 %>
585 %>
586 %for key, desc in elems:
586 %for key, desc in elems:
587 <tr>
587 <tr>
588 <td class="keys">
588 <td class="keys">
589 <span class="key tag">${key}</span>
589 <span class="key tag">${key}</span>
590 </td>
590 </td>
591 <td>${desc}</td>
591 <td>${desc}</td>
592 </tr>
592 </tr>
593 %endfor
593 %endfor
594 </tbody>
594 </tbody>
595 </table>
595 </table>
596 </div>
596 </div>
597 </div>
597 </div>
598 <div class="modal-footer">
598 <div class="modal-footer">
599 </div>
599 </div>
600 </div><!-- /.modal-content -->
600 </div><!-- /.modal-content -->
601 </div><!-- /.modal-dialog -->
601 </div><!-- /.modal-dialog -->
602 </div><!-- /.modal -->
602 </div><!-- /.modal -->
@@ -1,132 +1,146 b''
1 <%inherit file="/base/base.mako"/>
1 <%inherit file="/base/base.mako"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${_('%s Pull Requests') % c.repo_name}
4 ${_('%s Pull Requests') % c.repo_name}
5 %if c.rhodecode_name:
5 %if c.rhodecode_name:
6 &middot; ${h.branding(c.rhodecode_name)}
6 &middot; ${h.branding(c.rhodecode_name)}
7 %endif
7 %endif
8 </%def>
8 </%def>
9
9
10 <%def name="breadcrumbs_links()">
10 <%def name="breadcrumbs_links()">
11
11
12 </%def>
12 </%def>
13
13
14 <%def name="menu_bar_nav()">
14 <%def name="menu_bar_nav()">
15 ${self.menu_items(active='repositories')}
15 ${self.menu_items(active='repositories')}
16 </%def>
16 </%def>
17
17
18
18
19 <%def name="menu_bar_subnav()">
19 <%def name="menu_bar_subnav()">
20 ${self.repo_menu(active='showpullrequest')}
20 ${self.repo_menu(active='showpullrequest')}
21 </%def>
21 </%def>
22
22
23
23
24 <%def name="main()">
24 <%def name="main()">
25 <div class="box">
25 <div class="box">
26 <div class="title">
26 <div class="title">
27 ${self.repo_page_title(c.rhodecode_db_repo)}
27 ${self.repo_page_title(c.rhodecode_db_repo)}
28
28
29 <ul class="links">
29 <ul class="links">
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.url('pullrequest_home',repo_name=c.repo_name)}">
34 ${_('Open new Pull Request')}
34 ${_('Open new Pull Request')}
35 </a>
35 </a>
36 </span>
36 </span>
37 %endif
37 %endif
38 </li>
38 </li>
39 </ul>
39 </ul>
40
40
41 ${self.breadcrumbs()}
41 ${self.breadcrumbs()}
42 </div>
42 </div>
43
43
44 <div class="sidebar-col-wrapper">
44 <div class="sidebar-col-wrapper">
45 ##main
45 ##main
46 <div class="sidebar">
46 <div class="sidebar">
47 <ul class="nav nav-pills nav-stacked">
47 <ul class="nav nav-pills nav-stacked">
48 <li class="${'active' if c.active=='open' else ''}"><a href="${h.url('pullrequest_show_all',repo_name=c.repo_name,source=0)}">${_('Opened')}</a></li>
48 <li class="${'active' if c.active=='open' else ''}"><a href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0})}">${_('Opened')}</a></li>
49 <li class="${'active' if c.active=='my' else ''}"><a href="${h.url('pullrequest_show_all',repo_name=c.repo_name,source=0,my=1)}">${_('Opened by me')}</a></li>
49 <li class="${'active' if c.active=='my' else ''}"><a href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0,'my':1})}">${_('Opened by me')}</a></li>
50 <li class="${'active' if c.active=='awaiting' else ''}"><a href="${h.url('pullrequest_show_all',repo_name=c.repo_name,source=0,awaiting_review=1)}">${_('Awaiting review')}</a></li>
50 <li class="${'active' if c.active=='awaiting' else ''}"><a href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0,'awaiting_review':1})}">${_('Awaiting review')}</a></li>
51 <li class="${'active' if c.active=='awaiting_my' else ''}"><a href="${h.url('pullrequest_show_all',repo_name=c.repo_name,source=0,awaiting_my_review=1)}">${_('Awaiting my review')}</a></li>
51 <li class="${'active' if c.active=='awaiting_my' else ''}"><a href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0,'awaiting_my_review':1})}">${_('Awaiting my review')}</a></li>
52 <li class="${'active' if c.active=='closed' else ''}"><a href="${h.url('pullrequest_show_all',repo_name=c.repo_name,source=0,closed=1)}">${_('Closed')}</a></li>
52 <li class="${'active' if c.active=='closed' else ''}"><a href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0,'closed':1})}">${_('Closed')}</a></li>
53 <li class="${'active' if c.active=='source' else ''}"><a href="${h.url('pullrequest_show_all',repo_name=c.repo_name,source=1)}">${_('From this repo')}</a></li>
53 <li class="${'active' if c.active=='source' else ''}"><a href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':1})}">${_('From this repo')}</a></li>
54 </ul>
54 </ul>
55 </div>
55 </div>
56
56
57 <div class="main-content-full-width">
57 <div class="main-content-full-width">
58 <div class="panel panel-default">
58 <div class="panel panel-default">
59 <div class="panel-heading">
59 <div class="panel-heading">
60 <h3 class="panel-title">
60 <h3 class="panel-title">
61 %if c.source:
61 %if c.source:
62 ${_('Pull Requests from %(repo_name)s repository') % {'repo_name': c.repo_name}}
62 ${_('Pull Requests from %(repo_name)s repository') % {'repo_name': c.repo_name}}
63 %elif c.closed:
63 %elif c.closed:
64 ${_('Closed Pull Requests to repository %(repo_name)s') % {'repo_name': c.repo_name}}
64 ${_('Closed Pull Requests to repository %(repo_name)s') % {'repo_name': c.repo_name}}
65 %elif c.my:
65 %elif c.my:
66 ${_('Pull Requests to %(repo_name)s repository opened by me') % {'repo_name': c.repo_name}}
66 ${_('Pull Requests to %(repo_name)s repository opened by me') % {'repo_name': c.repo_name}}
67 %elif c.awaiting_review:
67 %elif c.awaiting_review:
68 ${_('Pull Requests to %(repo_name)s repository awaiting review') % {'repo_name': c.repo_name}}
68 ${_('Pull Requests to %(repo_name)s repository awaiting review') % {'repo_name': c.repo_name}}
69 %elif c.awaiting_my_review:
69 %elif c.awaiting_my_review:
70 ${_('Pull Requests to %(repo_name)s repository awaiting my review') % {'repo_name': c.repo_name}}
70 ${_('Pull Requests to %(repo_name)s repository awaiting my review') % {'repo_name': c.repo_name}}
71 %else:
71 %else:
72 ${_('Pull Requests to %(repo_name)s repository') % {'repo_name': c.repo_name}}
72 ${_('Pull Requests to %(repo_name)s repository') % {'repo_name': c.repo_name}}
73 %endif
73 %endif
74 </h3>
74 </h3>
75 </div>
75 </div>
76 <div class="panel-body">
76 <div class="panel-body panel-body-min-height">
77 <table id="pull_request_list_table" class="display"></table>
77 <table id="pull_request_list_table" class="display"></table>
78 </div>
78 </div>
79 </div>
79 </div>
80 </div>
80 </div>
81 </div>
81 </div>
82 </div>
82 </div>
83
83
84 <script type="text/javascript">
84 <script type="text/javascript">
85 $(document).ready(function() {
85 $(document).ready(function() {
86
87 var $pullRequestListTable = $('#pull_request_list_table');
88
86 // object list
89 // object list
87 $('#pull_request_list_table').DataTable({
90 $pullRequestListTable.DataTable({
88 data: ${c.data|n},
91 processing: true,
89 processing: false,
90 serverSide: true,
92 serverSide: true,
91 deferLoading: ${c.records_total},
93 ajax: {
92 ajax: "",
94 "url": "${h.route_path('pullrequest_show_all_data', repo_name=c.repo_name)}",
93 dom: 'tp',
95 "data": function (d) {
96 d.source = "${c.source}";
97 d.closed = "${c.closed}";
98 d.my = "${c.my}";
99 d.awaiting_review = "${c.awaiting_review}";
100 d.awaiting_my_review = "${c.awaiting_my_review}";
101 }
102 },
103 dom: 'rtp',
94 pageLength: ${c.visual.dashboard_items},
104 pageLength: ${c.visual.dashboard_items},
95 order: [[ 1, "desc" ]],
105 order: [[ 1, "desc" ]],
96 columns: [
106 columns: [
97 { data: {"_": "status",
107 { data: {"_": "status",
98 "sort": "status"}, title: "", className: "td-status", orderable: false},
108 "sort": "status"}, title: "", className: "td-status", orderable: false},
99 { data: {"_": "name",
109 { data: {"_": "name",
100 "sort": "name_raw"}, title: "${_('Name')}", className: "td-componentname", "type": "num" },
110 "sort": "name_raw"}, title: "${_('Name')}", className: "td-componentname", "type": "num" },
101 { data: {"_": "author",
111 { data: {"_": "author",
102 "sort": "author_raw"}, title: "${_('Author')}", className: "td-user", orderable: false },
112 "sort": "author_raw"}, title: "${_('Author')}", className: "td-user", orderable: false },
103 { data: {"_": "title",
113 { data: {"_": "title",
104 "sort": "title"}, title: "${_('Title')}", className: "td-description" },
114 "sort": "title"}, title: "${_('Title')}", className: "td-description" },
105 { data: {"_": "comments",
115 { data: {"_": "comments",
106 "sort": "comments_raw"}, title: "", className: "td-comments", orderable: false},
116 "sort": "comments_raw"}, title: "", className: "td-comments", orderable: false},
107 { data: {"_": "updated_on",
117 { data: {"_": "updated_on",
108 "sort": "updated_on_raw"}, title: "${_('Last Update')}", className: "td-time" }
118 "sort": "updated_on_raw"}, title: "${_('Last Update')}", className: "td-time" }
109 ],
119 ],
110 language: {
120 language: {
111 paginate: DEFAULT_GRID_PAGINATION,
121 paginate: DEFAULT_GRID_PAGINATION,
122 sProcessing: _gettext('loading...'),
112 emptyTable: _gettext("No pull requests available yet.")
123 emptyTable: _gettext("No pull requests available yet.")
113 },
124 },
114 "drawCallback": function( settings, json ) {
125 "drawCallback": function( settings, json ) {
115 timeagoActivate();
126 timeagoActivate();
116 },
127 },
117 "createdRow": function ( row, data, index ) {
128 "createdRow": function ( row, data, index ) {
118 if (data['closed']) {
129 if (data['closed']) {
119 $(row).addClass('closed');
130 $(row).addClass('closed');
120 }
131 }
121 }
132 }
122 });
133 });
123 });
134
124 $('#pull_request_list_table').on('xhr.dt', function(e, settings, json, xhr){
135 $pullRequestListTable.on('xhr.dt', function(e, settings, json, xhr){
125 $('#pull_request_list_table').css('opacity', 1);
136 $pullRequestListTable.css('opacity', 1);
137 });
138
139 $pullRequestListTable.on('preXhr.dt', function(e, settings, data){
140 $pullRequestListTable.css('opacity', 0.3);
141 });
142
126 });
143 });
127
144
128 $('#pull_request_list_table').on('preXhr.dt', function(e, settings, data){
129 $('#pull_request_list_table').css('opacity', 0.3);
130 });
131 </script>
145 </script>
132 </%def>
146 </%def>
General Comments 0
You need to be logged in to leave comments. Login now