##// END OF EJS Templates
integrations: fix bug with integrations view page not loading...
dan -
r935:83975818 default
parent child Browse files
Show More
@@ -1,392 +1,393 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2012-2016 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import pylons
22 22 import deform
23 23 import logging
24 24 import colander
25 25 import peppercorn
26 26 import webhelpers.paginate
27 27
28 28 from pyramid.httpexceptions import HTTPFound, HTTPForbidden, HTTPBadRequest
29 29 from pyramid.renderers import render
30 30 from pyramid.response import Response
31 31
32 32 from rhodecode.lib import auth
33 33 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
34 34 from rhodecode.lib.utils2 import safe_int
35 35 from rhodecode.lib.helpers import Page
36 36 from rhodecode.model.db import Repository, RepoGroup, Session, Integration
37 37 from rhodecode.model.scm import ScmModel
38 38 from rhodecode.model.integration import IntegrationModel
39 39 from rhodecode.admin.navigation import navigation_list
40 40 from rhodecode.translation import _
41 41 from rhodecode.integrations import integration_type_registry
42 42 from rhodecode.model.validation_schema.schemas.integration_schema import (
43 43 make_integration_schema, IntegrationScopeType)
44 44
45 45 log = logging.getLogger(__name__)
46 46
47 47
48 48 class IntegrationSettingsViewBase(object):
49 49 """ Base Integration settings view used by both repo / global settings """
50 50
51 51 def __init__(self, context, request):
52 52 self.context = context
53 53 self.request = request
54 54 self._load_general_context()
55 55
56 56 if not self.perm_check(request.user):
57 57 raise HTTPForbidden()
58 58
59 59 def _load_general_context(self):
60 60 """
61 61 This avoids boilerplate for repo/global+list/edit+views/templates
62 62 by doing all possible contexts at the same time however it should
63 63 be split up into separate functions once more "contexts" exist
64 64 """
65 65
66 66 self.IntegrationType = None
67 67 self.repo = None
68 68 self.repo_group = None
69 69 self.integration = None
70 70 self.integrations = {}
71 71
72 72 request = self.request
73 73
74 74 if 'repo_name' in request.matchdict: # in repo settings context
75 75 repo_name = request.matchdict['repo_name']
76 76 self.repo = Repository.get_by_repo_name(repo_name)
77 77
78 78 if 'repo_group_name' in request.matchdict: # in group settings context
79 79 repo_group_name = request.matchdict['repo_group_name']
80 80 self.repo_group = RepoGroup.get_by_group_name(repo_group_name)
81 81
82 82
83 83 if 'integration' in request.matchdict: # integration type context
84 84 integration_type = request.matchdict['integration']
85 85 self.IntegrationType = integration_type_registry[integration_type]
86 86
87 87 if 'integration_id' in request.matchdict: # single integration context
88 88 integration_id = request.matchdict['integration_id']
89 89 self.integration = Integration.get(integration_id)
90 90
91 91 # extra perms check just in case
92 92 if not self._has_perms_for_integration(self.integration):
93 93 raise HTTPForbidden()
94 94
95 95 self.settings = self.integration and self.integration.settings or {}
96 96 self.admin_view = not (self.repo or self.repo_group)
97 97
98 98 def _has_perms_for_integration(self, integration):
99 99 perms = self.request.user.permissions
100 100
101 101 if 'hg.admin' in perms['global']:
102 102 return True
103 103
104 104 if integration.repo:
105 105 return perms['repositories'].get(
106 106 integration.repo.repo_name) == 'repository.admin'
107 107
108 108 if integration.repo_group:
109 109 return perms['repositories_groups'].get(
110 110 integration.repo_group.group_name) == 'group.admin'
111 111
112 112 return False
113 113
114 114 def _template_c_context(self):
115 115 # TODO: dan: this is a stopgap in order to inherit from current pylons
116 116 # based admin/repo settings templates - this should be removed entirely
117 117 # after port to pyramid
118 118
119 119 c = pylons.tmpl_context
120 120 c.active = 'integrations'
121 121 c.rhodecode_user = self.request.user
122 122 c.repo = self.repo
123 123 c.repo_group = self.repo_group
124 124 c.repo_name = self.repo and self.repo.repo_name or None
125 125 c.repo_group_name = self.repo_group and self.repo_group.group_name or None
126 126
127 127 if self.repo:
128 128 c.repo_info = self.repo
129 129 c.rhodecode_db_repo = self.repo
130 130 c.repository_pull_requests = ScmModel().get_pull_requests(self.repo)
131 131 else:
132 132 c.navlist = navigation_list(self.request)
133 133
134 134 return c
135 135
136 136 def _form_schema(self):
137 137 schema = make_integration_schema(IntegrationType=self.IntegrationType,
138 138 settings=self.settings)
139 139
140 140 # returns a clone, important if mutating the schema later
141 141 return schema.bind(
142 142 permissions=self.request.user.permissions,
143 143 no_scope=not self.admin_view)
144 144
145 145
146 146 def _form_defaults(self):
147 147 defaults = {}
148 148
149 149 if self.integration:
150 150 defaults['settings'] = self.integration.settings or {}
151 151 defaults['options'] = {
152 152 'name': self.integration.name,
153 153 'enabled': self.integration.enabled,
154 154 'scope': {
155 155 'repo': self.integration.repo,
156 156 'repo_group': self.integration.repo_group,
157 157 'child_repos_only': self.integration.child_repos_only,
158 158 },
159 159 }
160 160 else:
161 161 if self.repo:
162 162 scope = _('{repo_name} repository').format(
163 163 repo_name=self.repo.repo_name)
164 164 elif self.repo_group:
165 165 scope = _('{repo_group_name} repo group').format(
166 166 repo_group_name=self.repo_group.group_name)
167 167 else:
168 168 scope = _('Global')
169 169
170 170 defaults['options'] = {
171 171 'enabled': True,
172 172 'name': _('{name} integration').format(
173 173 name=self.IntegrationType.display_name),
174 174 }
175 175 defaults['options']['scope'] = {
176 176 'repo': self.repo,
177 177 'repo_group': self.repo_group,
178 178 }
179 179
180 180 return defaults
181 181
182 182 def _delete_integration(self, integration):
183 183 Session().delete(self.integration)
184 184 Session().commit()
185 185 self.request.session.flash(
186 186 _('Integration {integration_name} deleted successfully.').format(
187 187 integration_name=self.integration.name),
188 188 queue='success')
189 189
190 190 if self.repo:
191 191 redirect_to = self.request.route_url(
192 192 'repo_integrations_home', repo_name=self.repo.repo_name)
193 193 elif self.repo_group:
194 194 redirect_to = self.request.route_url(
195 195 'repo_group_integrations_home',
196 196 repo_group_name=self.repo_group.group_name)
197 197 else:
198 198 redirect_to = self.request.route_url('global_integrations_home')
199 199 raise HTTPFound(redirect_to)
200 200
201 201 def settings_get(self, defaults=None, form=None):
202 202 """
203 203 View that displays the integration settings as a form.
204 204 """
205 205
206 206 defaults = defaults or self._form_defaults()
207 207 schema = self._form_schema()
208 208
209 209 if self.integration:
210 210 buttons = ('submit', 'delete')
211 211 else:
212 212 buttons = ('submit',)
213 213
214 214 form = form or deform.Form(schema, appstruct=defaults, buttons=buttons)
215 215
216 216 template_context = {
217 217 'form': form,
218 218 'current_IntegrationType': self.IntegrationType,
219 219 'integration': self.integration,
220 220 'c': self._template_c_context(),
221 221 }
222 222
223 223 return template_context
224 224
225 225 @auth.CSRFRequired()
226 226 def settings_post(self):
227 227 """
228 228 View that validates and stores the integration settings.
229 229 """
230 230 controls = self.request.POST.items()
231 231 pstruct = peppercorn.parse(controls)
232 232
233 233 if self.integration and pstruct.get('delete'):
234 234 return self._delete_integration(self.integration)
235 235
236 236 schema = self._form_schema()
237 237
238 238 skip_settings_validation = False
239 239 if self.integration and 'enabled' not in pstruct.get('options', {}):
240 240 skip_settings_validation = True
241 241 schema['settings'].validator = None
242 242 for field in schema['settings'].children:
243 243 field.validator = None
244 244 field.missing = ''
245 245
246 246 if self.integration:
247 247 buttons = ('submit', 'delete')
248 248 else:
249 249 buttons = ('submit',)
250 250
251 251 form = deform.Form(schema, buttons=buttons)
252 252
253 253 if not self.admin_view:
254 254 # scope is read only field in these cases, and has to be added
255 255 options = pstruct.setdefault('options', {})
256 256 if 'scope' not in options:
257 257 options['scope'] = IntegrationScopeType().serialize(None, {
258 258 'repo': self.repo,
259 259 'repo_group': self.repo_group,
260 260 })
261 261
262 262 try:
263 263 valid_data = form.validate_pstruct(pstruct)
264 264 except deform.ValidationFailure as e:
265 265 self.request.session.flash(
266 266 _('Errors exist when saving integration settings. '
267 267 'Please check the form inputs.'),
268 268 queue='error')
269 269 return self.settings_get(form=e)
270 270
271 271 if not self.integration:
272 272 self.integration = Integration()
273 273 self.integration.integration_type = self.IntegrationType.key
274 274 Session().add(self.integration)
275 275
276 276 scope = valid_data['options']['scope']
277 277
278 278 IntegrationModel().update_integration(self.integration,
279 279 name=valid_data['options']['name'],
280 280 enabled=valid_data['options']['enabled'],
281 281 settings=valid_data['settings'],
282 282 repo=scope['repo'],
283 283 repo_group=scope['repo_group'],
284 284 child_repos_only=scope['child_repos_only'],
285 285 )
286 286
287 287
288 288 self.integration.settings = valid_data['settings']
289 289 Session().commit()
290 290 # Display success message and redirect.
291 291 self.request.session.flash(
292 292 _('Integration {integration_name} updated successfully.').format(
293 293 integration_name=self.IntegrationType.display_name),
294 294 queue='success')
295 295
296 296
297 297 # if integration scope changes, we must redirect to the right place
298 298 # keeping in mind if the original view was for /repo/ or /_admin/
299 299 admin_view = not (self.repo or self.repo_group)
300 300
301 301 if self.integration.repo and not admin_view:
302 302 redirect_to = self.request.route_path(
303 303 'repo_integrations_edit',
304 304 repo_name=self.integration.repo.repo_name,
305 305 integration=self.integration.integration_type,
306 306 integration_id=self.integration.integration_id)
307 307 elif self.integration.repo_group and not admin_view:
308 308 redirect_to = self.request.route_path(
309 309 'repo_group_integrations_edit',
310 310 repo_group_name=self.integration.repo_group.group_name,
311 311 integration=self.integration.integration_type,
312 312 integration_id=self.integration.integration_id)
313 313 else:
314 314 redirect_to = self.request.route_path(
315 315 'global_integrations_edit',
316 316 integration=self.integration.integration_type,
317 317 integration_id=self.integration.integration_id)
318 318
319 319 return HTTPFound(redirect_to)
320 320
321 321 def index(self):
322 322 """ List integrations """
323 323 if self.repo:
324 324 scope = self.repo
325 325 elif self.repo_group:
326 326 scope = self.repo_group
327 327 else:
328 328 scope = 'all'
329 329
330 330 integrations = []
331 331
332 for integration in IntegrationModel().get_integrations(
332 for IntType, integration in IntegrationModel().get_integrations(
333 333 scope=scope, IntegrationType=self.IntegrationType):
334 334
335 335 # extra permissions check *just in case*
336 336 if not self._has_perms_for_integration(integration):
337 337 continue
338 integrations.append(integration)
338
339 integrations.append((IntType, integration))
339 340
340 341 sort_arg = self.request.GET.get('sort', 'name:asc')
341 342 if ':' in sort_arg:
342 343 sort_field, sort_dir = sort_arg.split(':')
343 344 else:
344 345 sort_field = sort_arg, 'asc'
345 346
346 347 assert sort_field in ('name', 'integration_type', 'enabled', 'scope')
347 348
348 349 integrations.sort(
349 350 key=lambda x: getattr(x[1], sort_field), reverse=(sort_dir=='desc'))
350 351
351 352
352 353 page_url = webhelpers.paginate.PageURL(
353 354 self.request.path, self.request.GET)
354 355 page = safe_int(self.request.GET.get('page', 1), 1)
355 356
356 357 integrations = Page(integrations, page=page, items_per_page=10,
357 358 url=page_url)
358 359
359 360 template_context = {
360 361 'sort_field': sort_field,
361 362 'rev_sort_dir': sort_dir != 'desc' and 'desc' or 'asc',
362 363 'current_IntegrationType': self.IntegrationType,
363 364 'integrations_list': integrations,
364 365 'available_integrations': integration_type_registry,
365 366 'c': self._template_c_context(),
366 367 'request': self.request,
367 368 }
368 369 return template_context
369 370
370 371 def new_integration(self):
371 372 template_context = {
372 373 'available_integrations': integration_type_registry,
373 374 'c': self._template_c_context(),
374 375 }
375 376 return template_context
376 377
377 378 class GlobalIntegrationsView(IntegrationSettingsViewBase):
378 379 def perm_check(self, user):
379 380 return auth.HasPermissionAll('hg.admin').check_permissions(user=user)
380 381
381 382
382 383 class RepoIntegrationsView(IntegrationSettingsViewBase):
383 384 def perm_check(self, user):
384 385 return auth.HasRepoPermissionAll('repository.admin'
385 386 )(repo_name=self.repo.repo_name, user=user)
386 387
387 388
388 389 class RepoGroupIntegrationsView(IntegrationSettingsViewBase):
389 390 def perm_check(self, user):
390 391 return auth.HasRepoGroupPermissionAll('group.admin'
391 392 )(group_name=self.repo_group.group_name, user=user)
392 393
General Comments 0
You need to be logged in to leave comments. Login now