##// END OF EJS Templates
vcs: Use key lookup to explode if key is not available....
vcs: Use key lookup to explode if key is not available. The key has to be in the dictionary, otherwise something changed in the code and expoding here is better than hiding the error.

File last commit:

r793:fc8d2069 default
r890:ed7b68ec default
Show More
views.py
392 lines | 14.1 KiB | text/x-python | PythonLexer
dan
integrations: add integration support...
r411 # -*- coding: utf-8 -*-
# Copyright (C) 2012-2016 RhodeCode GmbH
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License, version 3
# (only), as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# This program is dual-licensed. If you wish to learn more about the
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/
import pylons
dan
forms: add deform for integration settings forms
r518 import deform
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 import logging
import colander
import peppercorn
import webhelpers.paginate
dan
integrations: add integration support...
r411
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 from pyramid.httpexceptions import HTTPFound, HTTPForbidden, HTTPBadRequest
dan
integrations: add integration support...
r411 from pyramid.renderers import render
from pyramid.response import Response
from rhodecode.lib import auth
from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 from rhodecode.lib.utils2 import safe_int
from rhodecode.lib.helpers import Page
dan
integrations: add repo group integrations, fixes #4175
r667 from rhodecode.model.db import Repository, RepoGroup, Session, Integration
dan
integrations: add integration support...
r411 from rhodecode.model.scm import ScmModel
from rhodecode.model.integration import IntegrationModel
from rhodecode.admin.navigation import navigation_list
from rhodecode.translation import _
from rhodecode.integrations import integration_type_registry
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 from rhodecode.model.validation_schema.schemas.integration_schema import (
dan
integrations: add recursive repo group scope to allow integrations...
r793 make_integration_schema, IntegrationScopeType)
dan
integrations: add integration support...
r411
log = logging.getLogger(__name__)
class IntegrationSettingsViewBase(object):
""" Base Integration settings view used by both repo / global settings """
def __init__(self, context, request):
self.context = context
self.request = request
self._load_general_context()
if not self.perm_check(request.user):
raise HTTPForbidden()
def _load_general_context(self):
"""
This avoids boilerplate for repo/global+list/edit+views/templates
by doing all possible contexts at the same time however it should
be split up into separate functions once more "contexts" exist
"""
self.IntegrationType = None
self.repo = None
dan
integrations: add repo group integrations, fixes #4175
r667 self.repo_group = None
dan
integrations: add integration support...
r411 self.integration = None
self.integrations = {}
request = self.request
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 if 'repo_name' in request.matchdict: # in repo settings context
dan
integrations: add integration support...
r411 repo_name = request.matchdict['repo_name']
self.repo = Repository.get_by_repo_name(repo_name)
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 if 'repo_group_name' in request.matchdict: # in group settings context
dan
integrations: add repo group integrations, fixes #4175
r667 repo_group_name = request.matchdict['repo_group_name']
self.repo_group = RepoGroup.get_by_group_name(repo_group_name)
dan
integrations: refactor/cleanup + features, fixes #4181...
r731
if 'integration' in request.matchdict: # integration type context
dan
integrations: add integration support...
r411 integration_type = request.matchdict['integration']
self.IntegrationType = integration_type_registry[integration_type]
if 'integration_id' in request.matchdict: # single integration context
integration_id = request.matchdict['integration_id']
self.integration = Integration.get(integration_id)
dan
integrations: add repo group integrations, fixes #4175
r667
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 # extra perms check just in case
if not self._has_perms_for_integration(self.integration):
raise HTTPForbidden()
dan
integrations: add integration support...
r411
self.settings = self.integration and self.integration.settings or {}
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 self.admin_view = not (self.repo or self.repo_group)
def _has_perms_for_integration(self, integration):
perms = self.request.user.permissions
if 'hg.admin' in perms['global']:
return True
if integration.repo:
return perms['repositories'].get(
integration.repo.repo_name) == 'repository.admin'
if integration.repo_group:
return perms['repositories_groups'].get(
integration.repo_group.group_name) == 'group.admin'
return False
dan
integrations: add integration support...
r411
def _template_c_context(self):
# TODO: dan: this is a stopgap in order to inherit from current pylons
# based admin/repo settings templates - this should be removed entirely
# after port to pyramid
c = pylons.tmpl_context
c.active = 'integrations'
c.rhodecode_user = self.request.user
c.repo = self.repo
dan
integrations: add repo group integrations, fixes #4175
r667 c.repo_group = self.repo_group
dan
integrations: add integration support...
r411 c.repo_name = self.repo and self.repo.repo_name or None
dan
integrations: add repo group integrations, fixes #4175
r667 c.repo_group_name = self.repo_group and self.repo_group.group_name or None
dan
integrations: refactor/cleanup + features, fixes #4181...
r731
dan
integrations: add integration support...
r411 if self.repo:
c.repo_info = self.repo
c.rhodecode_db_repo = self.repo
c.repository_pull_requests = ScmModel().get_pull_requests(self.repo)
else:
c.navlist = navigation_list(self.request)
return c
def _form_schema(self):
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 schema = make_integration_schema(IntegrationType=self.IntegrationType,
settings=self.settings)
dan
integrations: add integration support...
r411
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 # returns a clone, important if mutating the schema later
return schema.bind(
permissions=self.request.user.permissions,
no_scope=not self.admin_view)
def _form_defaults(self):
defaults = {}
dan
integrations: add integration support...
r411
dan
forms: add deform for integration settings forms
r518 if self.integration:
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 defaults['settings'] = self.integration.settings or {}
defaults['options'] = {
'name': self.integration.name,
'enabled': self.integration.enabled,
dan
integrations: add recursive repo group scope to allow integrations...
r793 'scope': {
'repo': self.integration.repo,
'repo_group': self.integration.repo_group,
'child_repos_only': self.integration.child_repos_only,
},
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 }
dan
forms: add deform for integration settings forms
r518 else:
if self.repo:
dan
integrations: add repo group integrations, fixes #4175
r667 scope = _('{repo_name} repository').format(
repo_name=self.repo.repo_name)
elif self.repo_group:
scope = _('{repo_group_name} repo group').format(
repo_group_name=self.repo_group.group_name)
dan
integrations: add integration support...
r411 else:
dan
forms: add deform for integration settings forms
r518 scope = _('Global')
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 defaults['options'] = {
'enabled': True,
'name': _('{name} integration').format(
name=self.IntegrationType.display_name),
}
dan
integrations: add recursive repo group scope to allow integrations...
r793 defaults['options']['scope'] = {
'repo': self.repo,
'repo_group': self.repo_group,
}
dan
integrations: refactor/cleanup + features, fixes #4181...
r731
return defaults
dan
integrations: add integration support...
r411
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 def _delete_integration(self, integration):
Session().delete(self.integration)
Session().commit()
self.request.session.flash(
_('Integration {integration_name} deleted successfully.').format(
integration_name=self.integration.name),
queue='success')
if self.repo:
redirect_to = self.request.route_url(
'repo_integrations_home', repo_name=self.repo.repo_name)
elif self.repo_group:
redirect_to = self.request.route_url(
'repo_group_integrations_home',
repo_group_name=self.repo_group.group_name)
else:
redirect_to = self.request.route_url('global_integrations_home')
raise HTTPFound(redirect_to)
def settings_get(self, defaults=None, form=None):
"""
View that displays the integration settings as a form.
"""
defaults = defaults or self._form_defaults()
schema = self._form_schema()
dan
forms: add deform for integration settings forms
r518
if self.integration:
buttons = ('submit', 'delete')
else:
buttons = ('submit',)
form = form or deform.Form(schema, appstruct=defaults, buttons=buttons)
dan
integrations: add integration support...
r411
template_context = {
dan
forms: add deform for integration settings forms
r518 'form': form,
dan
integrations: add integration support...
r411 'current_IntegrationType': self.IntegrationType,
'integration': self.integration,
'c': self._template_c_context(),
}
return template_context
@auth.CSRFRequired()
def settings_post(self):
"""
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 View that validates and stores the integration settings.
dan
integrations: add integration support...
r411 """
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 controls = self.request.POST.items()
pstruct = peppercorn.parse(controls)
if self.integration and pstruct.get('delete'):
return self._delete_integration(self.integration)
schema = self._form_schema()
skip_settings_validation = False
if self.integration and 'enabled' not in pstruct.get('options', {}):
skip_settings_validation = True
schema['settings'].validator = None
for field in schema['settings'].children:
field.validator = None
field.missing = ''
dan
integrations: add integration support...
r411
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 if self.integration:
buttons = ('submit', 'delete')
else:
buttons = ('submit',)
dan
forms: add deform for integration settings forms
r518
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 form = deform.Form(schema, buttons=buttons)
dan
integrations: add integration support...
r411
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 if not self.admin_view:
# scope is read only field in these cases, and has to be added
options = pstruct.setdefault('options', {})
if 'scope' not in options:
dan
integrations: add recursive repo group scope to allow integrations...
r793 options['scope'] = IntegrationScopeType().serialize(None, {
'repo': self.repo,
'repo_group': self.repo_group,
})
dan
integrations: add integration support...
r411
try:
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 valid_data = form.validate_pstruct(pstruct)
dan
forms: add deform for integration settings forms
r518 except deform.ValidationFailure as e:
dan
integrations: add integration support...
r411 self.request.session.flash(
dan
forms: add deform for integration settings forms
r518 _('Errors exist when saving integration settings. '
dan
integrations: add integration support...
r411 'Please check the form inputs.'),
queue='error')
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 return self.settings_get(form=e)
dan
integrations: add integration support...
r411
if not self.integration:
integrations-db: don't use default contructor to be consisten with other modules...
r448 self.integration = Integration()
self.integration.integration_type = self.IntegrationType.key
Session().add(self.integration)
dan
integrations: add integration support...
r411
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 scope = valid_data['options']['scope']
dan
integrations: add integration support...
r411
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 IntegrationModel().update_integration(self.integration,
name=valid_data['options']['name'],
enabled=valid_data['options']['enabled'],
settings=valid_data['settings'],
dan
integrations: add recursive repo group scope to allow integrations...
r793 repo=scope['repo'],
repo_group=scope['repo_group'],
child_repos_only=scope['child_repos_only'],
)
dan
integrations: refactor/cleanup + features, fixes #4181...
r731
self.integration.settings = valid_data['settings']
integrations-db: don't use default contructor to be consisten with other modules...
r448 Session().commit()
dan
integrations: add integration support...
r411 # Display success message and redirect.
self.request.session.flash(
_('Integration {integration_name} updated successfully.').format(
integrations: fixed flash message queue.
r424 integration_name=self.IntegrationType.display_name),
queue='success')
dan
integrations: refactor/cleanup + features, fixes #4181...
r731
# if integration scope changes, we must redirect to the right place
# keeping in mind if the original view was for /repo/ or /_admin/
admin_view = not (self.repo or self.repo_group)
dan
integrations: add recursive repo group scope to allow integrations...
r793 if self.integration.repo and not admin_view:
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 redirect_to = self.request.route_path(
'repo_integrations_edit',
dan
integrations: add recursive repo group scope to allow integrations...
r793 repo_name=self.integration.repo.repo_name,
dan
integrations: add integration support...
r411 integration=self.integration.integration_type,
integration_id=self.integration.integration_id)
dan
integrations: add recursive repo group scope to allow integrations...
r793 elif self.integration.repo_group and not admin_view:
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 redirect_to = self.request.route_path(
dan
integrations: add repo group integrations, fixes #4175
r667 'repo_group_integrations_edit',
dan
integrations: add recursive repo group scope to allow integrations...
r793 repo_group_name=self.integration.repo_group.group_name,
dan
integrations: add repo group integrations, fixes #4175
r667 integration=self.integration.integration_type,
integration_id=self.integration.integration_id)
dan
integrations: add integration support...
r411 else:
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 redirect_to = self.request.route_path(
dan
integrations: add integration support...
r411 'global_integrations_edit',
integration=self.integration.integration_type,
integration_id=self.integration.integration_id)
return HTTPFound(redirect_to)
def index(self):
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 """ List integrations """
if self.repo:
scope = self.repo
elif self.repo_group:
scope = self.repo_group
else:
scope = 'all'
integrations = []
for integration in IntegrationModel().get_integrations(
scope=scope, IntegrationType=self.IntegrationType):
# extra permissions check *just in case*
if not self._has_perms_for_integration(integration):
continue
integrations.append(integration)
sort_arg = self.request.GET.get('sort', 'name:asc')
if ':' in sort_arg:
sort_field, sort_dir = sort_arg.split(':')
else:
sort_field = sort_arg, 'asc'
assert sort_field in ('name', 'integration_type', 'enabled', 'scope')
integrations.sort(
key=lambda x: getattr(x[1], sort_field), reverse=(sort_dir=='desc'))
page_url = webhelpers.paginate.PageURL(
self.request.path, self.request.GET)
page = safe_int(self.request.GET.get('page', 1), 1)
integrations = Page(integrations, page=page, items_per_page=10,
url=page_url)
dan
integrations: add integration support...
r411
template_context = {
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 'sort_field': sort_field,
'rev_sort_dir': sort_dir != 'desc' and 'desc' or 'asc',
dan
integrations: add integration support...
r411 'current_IntegrationType': self.IntegrationType,
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 'integrations_list': integrations,
dan
integrations: add integration support...
r411 'available_integrations': integration_type_registry,
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 'c': self._template_c_context(),
'request': self.request,
dan
integrations: add integration support...
r411 }
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 return template_context
dan
integrations: add integration support...
r411
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 def new_integration(self):
template_context = {
'available_integrations': integration_type_registry,
'c': self._template_c_context(),
}
return template_context
dan
integrations: add integration support...
r411
class GlobalIntegrationsView(IntegrationSettingsViewBase):
def perm_check(self, user):
return auth.HasPermissionAll('hg.admin').check_permissions(user=user)
class RepoIntegrationsView(IntegrationSettingsViewBase):
def perm_check(self, user):
return auth.HasRepoPermissionAll('repository.admin'
)(repo_name=self.repo.repo_name, user=user)
dan
integrations: add repo group integrations, fixes #4175
r667
dan
integrations: refactor/cleanup + features, fixes #4181...
r731
dan
integrations: add repo group integrations, fixes #4175
r667 class RepoGroupIntegrationsView(IntegrationSettingsViewBase):
def perm_check(self, user):
return auth.HasRepoGroupPermissionAll('group.admin'
)(group_name=self.repo_group.group_name, user=user)
dan
integrations: refactor/cleanup + features, fixes #4181...
r731