##// END OF EJS Templates
audit-logger: use raw JSON with empty data to control unicode decode warnings....
audit-logger: use raw JSON with empty data to control unicode decode warnings. Passing in empty dict triggers warnings on sqlalchemy level that we're trying to pass in NON unicode into Unicode column.

File last commit:

r2138:cfe7604e default
r2184:c4cc6e2a default
Show More
views.py
459 lines | 15.8 KiB | text/x-python | PythonLexer
dan
integrations: add integration support...
r411 # -*- coding: utf-8 -*-
license: updated copyright year to 2017
r1271 # Copyright (C) 2012-2017 RhodeCode GmbH
dan
integrations: add integration support...
r411 #
# 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/
dan
forms: add deform for integration settings forms
r518 import deform
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 import logging
import peppercorn
import webhelpers.paginate
dan
integrations: add integration support...
r411
integrations: expose EE integrations as dummy objects to show...
r2138 from pyramid.httpexceptions import HTTPFound, HTTPForbidden, HTTPNotFound
dan
integrations: add integration support...
r411
integrations: rewrote usage of pylons components inside integrations....
r1990 from rhodecode.apps._base import BaseAppView
from rhodecode.integrations import integration_type_registry
admin: moved admin pyramid into apps.
r1503 from rhodecode.apps.admin.navigation import navigation_list
integrations: rewrote usage of pylons components inside integrations....
r1990 from rhodecode.lib.auth import (
LoginRequired, CSRFRequired, HasPermissionAnyDecorator,
HasRepoPermissionAnyDecorator, HasRepoGroupPermissionAnyDecorator)
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
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__)
integrations: rewrote usage of pylons components inside integrations....
r1990 class IntegrationSettingsViewBase(BaseAppView):
"""
Base Integration settings view used by both repo / global settings
"""
dan
integrations: add integration support...
r411
def __init__(self, context, request):
integrations: rewrote usage of pylons components inside integrations....
r1990 super(IntegrationSettingsViewBase, self).__init__(context, request)
self._load_view_context()
dan
integrations: add integration support...
r411
integrations: rewrote usage of pylons components inside integrations....
r1990 def _load_view_context(self):
dan
integrations: add integration support...
r411 """
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)
admin: moved admin pyramid into apps.
r1503 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']
integrations: expose EE integrations as dummy objects to show...
r2138 if integration_type not in integration_type_registry:
raise HTTPNotFound()
dan
integrations: add integration support...
r411 self.IntegrationType = integration_type_registry[integration_type]
integrations: expose EE integrations as dummy objects to show...
r2138 if self.IntegrationType.is_dummy:
raise HTTPNotFound()
dan
integrations: add integration support...
r411
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
integrations: rewrote usage of pylons components inside integrations....
r1990 def _get_local_tmpl_context(self, include_app_defaults=False):
_ = self.request.translate
c = super(IntegrationSettingsViewBase, self)._get_local_tmpl_context(
include_app_defaults=include_app_defaults)
dan
integrations: add integration support...
r411
c.active = 'integrations'
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):
integrations: rewrote usage of pylons components inside integrations....
r1990 _ = self.request.translate
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 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):
integrations: rewrote usage of pylons components inside integrations....
r1990 _ = self.request.translate
Session().delete(integration)
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 Session().commit()
self.request.session.flash(
_('Integration {integration_name} deleted successfully.').format(
integrations: rewrote usage of pylons components inside integrations....
r1990 integration_name=integration.name),
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 queue='success')
if self.repo:
integrations: rewrote usage of pylons components inside integrations....
r1990 redirect_to = self.request.route_path(
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 'repo_integrations_home', repo_name=self.repo.repo_name)
elif self.repo_group:
integrations: rewrote usage of pylons components inside integrations....
r1990 redirect_to = self.request.route_path(
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 'repo_group_integrations_home',
repo_group_name=self.repo_group.group_name)
else:
integrations: rewrote usage of pylons components inside integrations....
r1990 redirect_to = self.request.route_path('global_integrations_home')
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 raise HTTPFound(redirect_to)
integrations: rewrote usage of pylons components inside integrations....
r1990 def _integration_list(self):
""" List integrations """
c = self.load_default_context()
if self.repo:
scope = self.repo
elif self.repo_group:
scope = self.repo_group
else:
scope = 'all'
integrations = []
for IntType, 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((IntType, 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)
c.rev_sort_dir = sort_dir != 'desc' and 'desc' or 'asc'
c.current_IntegrationType = self.IntegrationType
c.integrations_list = integrations
c.available_integrations = integration_type_registry
return self._get_template_context(c)
def _settings_get(self, defaults=None, form=None):
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 """
View that displays the integration settings as a form.
"""
integrations: rewrote usage of pylons components inside integrations....
r1990 c = self.load_default_context()
dan
integrations: refactor/cleanup + features, fixes #4181...
r731
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
integrations: rewrote usage of pylons components inside integrations....
r1990 c.form = form
c.current_IntegrationType = self.IntegrationType
c.integration = self.integration
dan
integrations: add integration support...
r411
integrations: rewrote usage of pylons components inside integrations....
r1990 return self._get_template_context(c)
dan
integrations: add integration support...
r411
integrations: rewrote usage of pylons components inside integrations....
r1990 def _settings_post(self):
dan
integrations: add integration support...
r411 """
dan
integrations: refactor/cleanup + features, fixes #4181...
r731 View that validates and stores the integration settings.
dan
integrations: add integration support...
r411 """
integrations: rewrote usage of pylons components inside integrations....
r1990 _ = self.request.translate
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')
integrations: rewrote usage of pylons components inside integrations....
r1990 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)
integrations: rewrote usage of pylons components inside integrations....
r1990 def _new_integration(self):
c = self.load_default_context()
c.available_integrations = integration_type_registry
return self._get_template_context(c)
dan
integrations: refactor/cleanup + features, fixes #4181...
r731
integrations: rewrote usage of pylons components inside integrations....
r1990 def load_default_context(self):
raise NotImplementedError()
dan
integrations: add integration support...
r411
integrations: fix re-submit of form on repo integrations.
r1384
dan
integrations: add integration support...
r411 class GlobalIntegrationsView(IntegrationSettingsViewBase):
integrations: rewrote usage of pylons components inside integrations....
r1990 def load_default_context(self):
c = self._get_local_tmpl_context()
c.repo = self.repo
c.repo_group = self.repo_group
c.navlist = navigation_list(self.request)
self._register_global_c(c)
return c
@LoginRequired()
@HasPermissionAnyDecorator('hg.admin')
def integration_list(self):
return self._integration_list()
@LoginRequired()
@HasPermissionAnyDecorator('hg.admin')
def settings_get(self):
return self._settings_get()
@LoginRequired()
@HasPermissionAnyDecorator('hg.admin')
@CSRFRequired()
def settings_post(self):
return self._settings_post()
@LoginRequired()
@HasPermissionAnyDecorator('hg.admin')
def new_integration(self):
return self._new_integration()
dan
integrations: add integration support...
r411
class RepoIntegrationsView(IntegrationSettingsViewBase):
integrations: rewrote usage of pylons components inside integrations....
r1990 def load_default_context(self):
c = self._get_local_tmpl_context()
c.repo = self.repo
c.repo_group = self.repo_group
apps: removed deprecated usage of c.repo_info
r2081 self.db_repo = self.repo
integrations: rewrote usage of pylons components inside integrations....
r1990 c.rhodecode_db_repo = self.repo
c.repo_name = self.db_repo.repo_name
c.repository_pull_requests = ScmModel().get_pull_requests(self.repo)
self._register_global_c(c)
return c
@LoginRequired()
@HasRepoPermissionAnyDecorator('repository.admin')
def integration_list(self):
return self._integration_list()
@LoginRequired()
@HasRepoPermissionAnyDecorator('repository.admin')
def settings_get(self):
return self._settings_get()
@LoginRequired()
@HasRepoPermissionAnyDecorator('repository.admin')
@CSRFRequired()
def settings_post(self):
return self._settings_post()
@LoginRequired()
@HasRepoPermissionAnyDecorator('repository.admin')
def new_integration(self):
return self._new_integration()
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):
integrations: rewrote usage of pylons components inside integrations....
r1990 def load_default_context(self):
c = self._get_local_tmpl_context()
c.repo = self.repo
c.repo_group = self.repo_group
c.navlist = navigation_list(self.request)
self._register_global_c(c)
return c
@LoginRequired()
@HasRepoGroupPermissionAnyDecorator('group.admin')
def integration_list(self):
return self._integration_list()
dan
integrations: refactor/cleanup + features, fixes #4181...
r731
integrations: rewrote usage of pylons components inside integrations....
r1990 @LoginRequired()
@HasRepoGroupPermissionAnyDecorator('group.admin')
def settings_get(self):
return self._settings_get()
@LoginRequired()
@HasRepoGroupPermissionAnyDecorator('group.admin')
@CSRFRequired()
def settings_post(self):
return self._settings_post()
@LoginRequired()
@HasRepoGroupPermissionAnyDecorator('group.admin')
def new_integration(self):
return self._new_integration()