##// END OF EJS Templates
integrations: add repo group integrations, fixes #4175
dan -
r667:b9ef2c10 default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,133 +1,200 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2016 RhodeCode GmbH
3 # Copyright (C) 2012-2016 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 logging
21 import logging
22
22
23 from rhodecode.model.db import Repository, Integration
23 from rhodecode.model.db import Repository, Integration, RepoGroup
24 from rhodecode.config.routing import (
24 from rhodecode.config.routing import (
25 ADMIN_PREFIX, add_route_requirements, URL_NAME_REQUIREMENTS)
25 ADMIN_PREFIX, add_route_requirements, URL_NAME_REQUIREMENTS)
26 from rhodecode.integrations import integration_type_registry
26 from rhodecode.integrations import integration_type_registry
27
27
28 log = logging.getLogger(__name__)
28 log = logging.getLogger(__name__)
29
29
30
30
31 def includeme(config):
31 def includeme(config):
32
33 # global integrations
32 config.add_route('global_integrations_home',
34 config.add_route('global_integrations_home',
33 ADMIN_PREFIX + '/integrations')
35 ADMIN_PREFIX + '/integrations')
34 config.add_route('global_integrations_list',
36 config.add_route('global_integrations_list',
35 ADMIN_PREFIX + '/integrations/{integration}')
37 ADMIN_PREFIX + '/integrations/{integration}')
36 for route_name in ['global_integrations_home', 'global_integrations_list']:
38 for route_name in ['global_integrations_home', 'global_integrations_list']:
37 config.add_view('rhodecode.integrations.views.GlobalIntegrationsView',
39 config.add_view('rhodecode.integrations.views.GlobalIntegrationsView',
38 attr='index',
40 attr='index',
39 renderer='rhodecode:templates/admin/integrations/list.html',
41 renderer='rhodecode:templates/admin/integrations/list.html',
40 request_method='GET',
42 request_method='GET',
41 route_name=route_name)
43 route_name=route_name)
42
44
43 config.add_route('global_integrations_create',
45 config.add_route('global_integrations_create',
44 ADMIN_PREFIX + '/integrations/{integration}/new',
46 ADMIN_PREFIX + '/integrations/{integration}/new',
45 custom_predicates=(valid_integration,))
47 custom_predicates=(valid_integration,))
46 config.add_route('global_integrations_edit',
48 config.add_route('global_integrations_edit',
47 ADMIN_PREFIX + '/integrations/{integration}/{integration_id}',
49 ADMIN_PREFIX + '/integrations/{integration}/{integration_id}',
48 custom_predicates=(valid_integration,))
50 custom_predicates=(valid_integration,))
49 for route_name in ['global_integrations_create', 'global_integrations_edit']:
51 for route_name in ['global_integrations_create', 'global_integrations_edit']:
50 config.add_view('rhodecode.integrations.views.GlobalIntegrationsView',
52 config.add_view('rhodecode.integrations.views.GlobalIntegrationsView',
51 attr='settings_get',
53 attr='settings_get',
52 renderer='rhodecode:templates/admin/integrations/edit.html',
54 renderer='rhodecode:templates/admin/integrations/edit.html',
53 request_method='GET',
55 request_method='GET',
54 route_name=route_name)
56 route_name=route_name)
55 config.add_view('rhodecode.integrations.views.GlobalIntegrationsView',
57 config.add_view('rhodecode.integrations.views.GlobalIntegrationsView',
56 attr='settings_post',
58 attr='settings_post',
57 renderer='rhodecode:templates/admin/integrations/edit.html',
59 renderer='rhodecode:templates/admin/integrations/edit.html',
58 request_method='POST',
60 request_method='POST',
59 route_name=route_name)
61 route_name=route_name)
60
62
63
64 # repo integrations
61 config.add_route('repo_integrations_home',
65 config.add_route('repo_integrations_home',
62 add_route_requirements(
66 add_route_requirements(
63 '{repo_name}/settings/integrations',
67 '{repo_name}/settings/integrations',
64 URL_NAME_REQUIREMENTS
68 URL_NAME_REQUIREMENTS
65 ),
69 ),
66 custom_predicates=(valid_repo,))
70 custom_predicates=(valid_repo,))
67 config.add_route('repo_integrations_list',
71 config.add_route('repo_integrations_list',
68 add_route_requirements(
72 add_route_requirements(
69 '{repo_name}/settings/integrations/{integration}',
73 '{repo_name}/settings/integrations/{integration}',
70 URL_NAME_REQUIREMENTS
74 URL_NAME_REQUIREMENTS
71 ),
75 ),
72 custom_predicates=(valid_repo, valid_integration))
76 custom_predicates=(valid_repo, valid_integration))
73 for route_name in ['repo_integrations_home', 'repo_integrations_list']:
77 for route_name in ['repo_integrations_home', 'repo_integrations_list']:
74 config.add_view('rhodecode.integrations.views.RepoIntegrationsView',
78 config.add_view('rhodecode.integrations.views.RepoIntegrationsView',
75 attr='index',
79 attr='index',
76 request_method='GET',
80 request_method='GET',
77 route_name=route_name)
81 route_name=route_name)
78
82
79 config.add_route('repo_integrations_create',
83 config.add_route('repo_integrations_create',
80 add_route_requirements(
84 add_route_requirements(
81 '{repo_name}/settings/integrations/{integration}/new',
85 '{repo_name}/settings/integrations/{integration}/new',
82 URL_NAME_REQUIREMENTS
86 URL_NAME_REQUIREMENTS
83 ),
87 ),
84 custom_predicates=(valid_repo, valid_integration))
88 custom_predicates=(valid_repo, valid_integration))
85 config.add_route('repo_integrations_edit',
89 config.add_route('repo_integrations_edit',
86 add_route_requirements(
90 add_route_requirements(
87 '{repo_name}/settings/integrations/{integration}/{integration_id}',
91 '{repo_name}/settings/integrations/{integration}/{integration_id}',
88 URL_NAME_REQUIREMENTS
92 URL_NAME_REQUIREMENTS
89 ),
93 ),
90 custom_predicates=(valid_repo, valid_integration))
94 custom_predicates=(valid_repo, valid_integration))
91 for route_name in ['repo_integrations_edit', 'repo_integrations_create']:
95 for route_name in ['repo_integrations_edit', 'repo_integrations_create']:
92 config.add_view('rhodecode.integrations.views.RepoIntegrationsView',
96 config.add_view('rhodecode.integrations.views.RepoIntegrationsView',
93 attr='settings_get',
97 attr='settings_get',
94 renderer='rhodecode:templates/admin/integrations/edit.html',
98 renderer='rhodecode:templates/admin/integrations/edit.html',
95 request_method='GET',
99 request_method='GET',
96 route_name=route_name)
100 route_name=route_name)
97 config.add_view('rhodecode.integrations.views.RepoIntegrationsView',
101 config.add_view('rhodecode.integrations.views.RepoIntegrationsView',
98 attr='settings_post',
102 attr='settings_post',
99 renderer='rhodecode:templates/admin/integrations/edit.html',
103 renderer='rhodecode:templates/admin/integrations/edit.html',
100 request_method='POST',
104 request_method='POST',
101 route_name=route_name)
105 route_name=route_name)
102
106
103
107
108 # repo group integrations
109 config.add_route('repo_group_integrations_home',
110 add_route_requirements(
111 '{repo_group_name}/settings/integrations',
112 URL_NAME_REQUIREMENTS
113 ),
114 custom_predicates=(valid_repo_group,))
115 config.add_route('repo_group_integrations_list',
116 add_route_requirements(
117 '{repo_group_name}/settings/integrations/{integration}',
118 URL_NAME_REQUIREMENTS
119 ),
120 custom_predicates=(valid_repo_group, valid_integration))
121 for route_name in ['repo_group_integrations_home', 'repo_group_integrations_list']:
122 config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView',
123 attr='index',
124 request_method='GET',
125 route_name=route_name)
126
127 config.add_route('repo_group_integrations_create',
128 add_route_requirements(
129 '{repo_group_name}/settings/integrations/{integration}/new',
130 URL_NAME_REQUIREMENTS
131 ),
132 custom_predicates=(valid_repo_group, valid_integration))
133 config.add_route('repo_group_integrations_edit',
134 add_route_requirements(
135 '{repo_group_name}/settings/integrations/{integration}/{integration_id}',
136 URL_NAME_REQUIREMENTS
137 ),
138 custom_predicates=(valid_repo_group, valid_integration))
139 for route_name in ['repo_group_integrations_edit', 'repo_group_integrations_create']:
140 config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView',
141 attr='settings_get',
142 renderer='rhodecode:templates/admin/integrations/edit.html',
143 request_method='GET',
144 route_name=route_name)
145 config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView',
146 attr='settings_post',
147 renderer='rhodecode:templates/admin/integrations/edit.html',
148 request_method='POST',
149 route_name=route_name)
150
151
104 def valid_repo(info, request):
152 def valid_repo(info, request):
105 repo = Repository.get_by_repo_name(info['match']['repo_name'])
153 repo = Repository.get_by_repo_name(info['match']['repo_name'])
106 if repo:
154 if repo:
107 return True
155 return True
108
156
109
157
158 def valid_repo_group(info, request):
159 repo_group = RepoGroup.get_by_group_name(info['match']['repo_group_name'])
160 if repo_group:
161 return True
162 return False
163
164
110 def valid_integration(info, request):
165 def valid_integration(info, request):
111 integration_type = info['match']['integration']
166 integration_type = info['match']['integration']
112 integration_id = info['match'].get('integration_id')
167 integration_id = info['match'].get('integration_id')
113 repo_name = info['match'].get('repo_name')
168 repo_name = info['match'].get('repo_name')
169 repo_group_name = info['match'].get('repo_group_name')
114
170
115 if integration_type not in integration_type_registry:
171 if integration_type not in integration_type_registry:
116 return False
172 return False
117
173
118 repo = None
174 repo, repo_group = None, None
119 if repo_name:
175 if repo_name:
120 repo = Repository.get_by_repo_name(info['match']['repo_name'])
176 repo = Repository.get_by_repo_name(repo_name)
121 if not repo:
177 if not repo:
122 return False
178 return False
123
179
180 if repo_group_name:
181 repo_group = RepoGroup.get_by_group_name(repo_group_name)
182 if not repo_group:
183 return False
184
185 if repo_name and repo_group:
186 raise Exception('Either repo or repo_group can be set, not both')
187
188
124 if integration_id:
189 if integration_id:
125 integration = Integration.get(integration_id)
190 integration = Integration.get(integration_id)
126 if not integration:
191 if not integration:
127 return False
192 return False
128 if integration.integration_type != integration_type:
193 if integration.integration_type != integration_type:
129 return False
194 return False
130 if repo and repo.repo_id != integration.repo_id:
195 if repo and repo.repo_id != integration.repo_id:
131 return False
196 return False
197 if repo_group and repo_group.repo_group_id != integration.repo_group_id:
198 return False
132
199
133 return True
200 return True
@@ -1,272 +1,299 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2016 RhodeCode GmbH
3 # Copyright (C) 2012-2016 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 colander
21 import colander
22 import logging
22 import logging
23 import pylons
23 import pylons
24 import deform
24 import deform
25
25
26 from pyramid.httpexceptions import HTTPFound, HTTPForbidden
26 from pyramid.httpexceptions import HTTPFound, HTTPForbidden
27 from pyramid.renderers import render
27 from pyramid.renderers import render
28 from pyramid.response import Response
28 from pyramid.response import Response
29
29
30 from rhodecode.lib import auth
30 from rhodecode.lib import auth
31 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
31 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
32 from rhodecode.model.db import Repository, Session, Integration
32 from rhodecode.model.db import Repository, RepoGroup, Session, Integration
33 from rhodecode.model.scm import ScmModel
33 from rhodecode.model.scm import ScmModel
34 from rhodecode.model.integration import IntegrationModel
34 from rhodecode.model.integration import IntegrationModel
35 from rhodecode.admin.navigation import navigation_list
35 from rhodecode.admin.navigation import navigation_list
36 from rhodecode.translation import _
36 from rhodecode.translation import _
37 from rhodecode.integrations import integration_type_registry
37 from rhodecode.integrations import integration_type_registry
38
38
39 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
40
40
41
41
42 class IntegrationSettingsViewBase(object):
42 class IntegrationSettingsViewBase(object):
43 """ Base Integration settings view used by both repo / global settings """
43 """ Base Integration settings view used by both repo / global settings """
44
44
45 def __init__(self, context, request):
45 def __init__(self, context, request):
46 self.context = context
46 self.context = context
47 self.request = request
47 self.request = request
48 self._load_general_context()
48 self._load_general_context()
49
49
50 if not self.perm_check(request.user):
50 if not self.perm_check(request.user):
51 raise HTTPForbidden()
51 raise HTTPForbidden()
52
52
53 def _load_general_context(self):
53 def _load_general_context(self):
54 """
54 """
55 This avoids boilerplate for repo/global+list/edit+views/templates
55 This avoids boilerplate for repo/global+list/edit+views/templates
56 by doing all possible contexts at the same time however it should
56 by doing all possible contexts at the same time however it should
57 be split up into separate functions once more "contexts" exist
57 be split up into separate functions once more "contexts" exist
58 """
58 """
59
59
60 self.IntegrationType = None
60 self.IntegrationType = None
61 self.repo = None
61 self.repo = None
62 self.repo_group = None
62 self.integration = None
63 self.integration = None
63 self.integrations = {}
64 self.integrations = {}
64
65
65 request = self.request
66 request = self.request
66
67
67 if 'repo_name' in request.matchdict: # we're in a repo context
68 if 'repo_name' in request.matchdict: # we're in a repo context
68 repo_name = request.matchdict['repo_name']
69 repo_name = request.matchdict['repo_name']
69 self.repo = Repository.get_by_repo_name(repo_name)
70 self.repo = Repository.get_by_repo_name(repo_name)
70
71
72 if 'repo_group_name' in request.matchdict: # we're in repo_group context
73 repo_group_name = request.matchdict['repo_group_name']
74 self.repo_group = RepoGroup.get_by_group_name(repo_group_name)
75
71 if 'integration' in request.matchdict: # we're in integration context
76 if 'integration' in request.matchdict: # we're in integration context
72 integration_type = request.matchdict['integration']
77 integration_type = request.matchdict['integration']
73 self.IntegrationType = integration_type_registry[integration_type]
78 self.IntegrationType = integration_type_registry[integration_type]
74
79
75 if 'integration_id' in request.matchdict: # single integration context
80 if 'integration_id' in request.matchdict: # single integration context
76 integration_id = request.matchdict['integration_id']
81 integration_id = request.matchdict['integration_id']
77 self.integration = Integration.get(integration_id)
82 self.integration = Integration.get(integration_id)
78 else: # list integrations context
83 else: # list integrations context
79 for integration in IntegrationModel().get_integrations(self.repo):
84 integrations = IntegrationModel().get_integrations(
85 repo=self.repo, repo_group=self.repo_group)
86
87 for integration in integrations:
80 self.integrations.setdefault(integration.integration_type, []
88 self.integrations.setdefault(integration.integration_type, []
81 ).append(integration)
89 ).append(integration)
82
90
83 self.settings = self.integration and self.integration.settings or {}
91 self.settings = self.integration and self.integration.settings or {}
84
92
85 def _template_c_context(self):
93 def _template_c_context(self):
86 # TODO: dan: this is a stopgap in order to inherit from current pylons
94 # TODO: dan: this is a stopgap in order to inherit from current pylons
87 # based admin/repo settings templates - this should be removed entirely
95 # based admin/repo settings templates - this should be removed entirely
88 # after port to pyramid
96 # after port to pyramid
89
97
90 c = pylons.tmpl_context
98 c = pylons.tmpl_context
91 c.active = 'integrations'
99 c.active = 'integrations'
92 c.rhodecode_user = self.request.user
100 c.rhodecode_user = self.request.user
93 c.repo = self.repo
101 c.repo = self.repo
102 c.repo_group = self.repo_group
94 c.repo_name = self.repo and self.repo.repo_name or None
103 c.repo_name = self.repo and self.repo.repo_name or None
104 c.repo_group_name = self.repo_group and self.repo_group.group_name or None
95 if self.repo:
105 if self.repo:
96 c.repo_info = self.repo
106 c.repo_info = self.repo
97 c.rhodecode_db_repo = self.repo
107 c.rhodecode_db_repo = self.repo
98 c.repository_pull_requests = ScmModel().get_pull_requests(self.repo)
108 c.repository_pull_requests = ScmModel().get_pull_requests(self.repo)
99 else:
109 else:
100 c.navlist = navigation_list(self.request)
110 c.navlist = navigation_list(self.request)
101
111
102 return c
112 return c
103
113
104 def _form_schema(self):
114 def _form_schema(self):
105 if self.integration:
115 if self.integration:
106 settings = self.integration.settings
116 settings = self.integration.settings
107 else:
117 else:
108 settings = {}
118 settings = {}
109 return self.IntegrationType(settings=settings).settings_schema()
119 return self.IntegrationType(settings=settings).settings_schema()
110
120
111 def settings_get(self, defaults=None, errors=None, form=None):
121 def settings_get(self, defaults=None, errors=None, form=None):
112 """
122 """
113 View that displays the plugin settings as a form.
123 View that displays the plugin settings as a form.
114 """
124 """
115 defaults = defaults or {}
125 defaults = defaults or {}
116 errors = errors or {}
126 errors = errors or {}
117
127
118 if self.integration:
128 if self.integration:
119 defaults = self.integration.settings or {}
129 defaults = self.integration.settings or {}
120 defaults['name'] = self.integration.name
130 defaults['name'] = self.integration.name
121 defaults['enabled'] = self.integration.enabled
131 defaults['enabled'] = self.integration.enabled
122 else:
132 else:
123 if self.repo:
133 if self.repo:
124 scope = self.repo.repo_name
134 scope = _('{repo_name} repository').format(
135 repo_name=self.repo.repo_name)
136 elif self.repo_group:
137 scope = _('{repo_group_name} repo group').format(
138 repo_group_name=self.repo_group.group_name)
125 else:
139 else:
126 scope = _('Global')
140 scope = _('Global')
127
141
128 defaults['name'] = '{} {} integration'.format(scope,
142 defaults['name'] = '{} {} integration'.format(scope,
129 self.IntegrationType.display_name)
143 self.IntegrationType.display_name)
130 defaults['enabled'] = True
144 defaults['enabled'] = True
131
145
132 schema = self._form_schema().bind(request=self.request)
146 schema = self._form_schema().bind(request=self.request)
133
147
134 if self.integration:
148 if self.integration:
135 buttons = ('submit', 'delete')
149 buttons = ('submit', 'delete')
136 else:
150 else:
137 buttons = ('submit',)
151 buttons = ('submit',)
138
152
139 form = form or deform.Form(schema, appstruct=defaults, buttons=buttons)
153 form = form or deform.Form(schema, appstruct=defaults, buttons=buttons)
140
154
141 for node in schema:
155 for node in schema:
142 setting = self.settings.get(node.name)
156 setting = self.settings.get(node.name)
143 if setting is not None:
157 if setting is not None:
144 defaults.setdefault(node.name, setting)
158 defaults.setdefault(node.name, setting)
145 else:
159 else:
146 if node.default:
160 if node.default:
147 defaults.setdefault(node.name, node.default)
161 defaults.setdefault(node.name, node.default)
148
162
149 template_context = {
163 template_context = {
150 'form': form,
164 'form': form,
151 'defaults': defaults,
165 'defaults': defaults,
152 'errors': errors,
166 'errors': errors,
153 'schema': schema,
167 'schema': schema,
154 'current_IntegrationType': self.IntegrationType,
168 'current_IntegrationType': self.IntegrationType,
155 'integration': self.integration,
169 'integration': self.integration,
156 'settings': self.settings,
170 'settings': self.settings,
157 'resource': self.context,
171 'resource': self.context,
158 'c': self._template_c_context(),
172 'c': self._template_c_context(),
159 }
173 }
160
174
161 return template_context
175 return template_context
162
176
163 @auth.CSRFRequired()
177 @auth.CSRFRequired()
164 def settings_post(self):
178 def settings_post(self):
165 """
179 """
166 View that validates and stores the plugin settings.
180 View that validates and stores the plugin settings.
167 """
181 """
168 if self.request.params.get('delete'):
182 if self.request.params.get('delete'):
169 Session().delete(self.integration)
183 Session().delete(self.integration)
170 Session().commit()
184 Session().commit()
171 self.request.session.flash(
185 self.request.session.flash(
172 _('Integration {integration_name} deleted successfully.').format(
186 _('Integration {integration_name} deleted successfully.').format(
173 integration_name=self.integration.name),
187 integration_name=self.integration.name),
174 queue='success')
188 queue='success')
175 if self.repo:
189 if self.repo:
176 redirect_to = self.request.route_url(
190 redirect_to = self.request.route_url(
177 'repo_integrations_home', repo_name=self.repo.repo_name)
191 'repo_integrations_home', repo_name=self.repo.repo_name)
178 else:
192 else:
179 redirect_to = self.request.route_url('global_integrations_home')
193 redirect_to = self.request.route_url('global_integrations_home')
180 raise HTTPFound(redirect_to)
194 raise HTTPFound(redirect_to)
181
195
182 schema = self._form_schema().bind(request=self.request)
196 schema = self._form_schema().bind(request=self.request)
183
197
184 form = deform.Form(schema, buttons=('submit', 'delete'))
198 form = deform.Form(schema, buttons=('submit', 'delete'))
185
199
186 params = {}
200 params = {}
187 for node in schema.children:
201 for node in schema.children:
188 if type(node.typ) in (colander.Set, colander.List):
202 if type(node.typ) in (colander.Set, colander.List):
189 val = self.request.params.getall(node.name)
203 val = self.request.params.getall(node.name)
190 else:
204 else:
191 val = self.request.params.get(node.name)
205 val = self.request.params.get(node.name)
192 if val:
206 if val:
193 params[node.name] = val
207 params[node.name] = val
194
208
195 controls = self.request.POST.items()
209 controls = self.request.POST.items()
196 try:
210 try:
197 valid_data = form.validate(controls)
211 valid_data = form.validate(controls)
198 except deform.ValidationFailure as e:
212 except deform.ValidationFailure as e:
199 self.request.session.flash(
213 self.request.session.flash(
200 _('Errors exist when saving integration settings. '
214 _('Errors exist when saving integration settings. '
201 'Please check the form inputs.'),
215 'Please check the form inputs.'),
202 queue='error')
216 queue='error')
203 return self.settings_get(errors={}, defaults=params, form=e)
217 return self.settings_get(errors={}, defaults=params, form=e)
204
218
205 if not self.integration:
219 if not self.integration:
206 self.integration = Integration()
220 self.integration = Integration()
207 self.integration.integration_type = self.IntegrationType.key
221 self.integration.integration_type = self.IntegrationType.key
208 if self.repo:
222 if self.repo:
209 self.integration.repo = self.repo
223 self.integration.repo = self.repo
224 elif self.repo_group:
225 self.integration.repo_group = self.repo_group
210 Session().add(self.integration)
226 Session().add(self.integration)
211
227
212 self.integration.enabled = valid_data.pop('enabled', False)
228 self.integration.enabled = valid_data.pop('enabled', False)
213 self.integration.name = valid_data.pop('name')
229 self.integration.name = valid_data.pop('name')
214 self.integration.settings = valid_data
230 self.integration.settings = valid_data
215
231
216 Session().commit()
232 Session().commit()
217
233
218 # Display success message and redirect.
234 # Display success message and redirect.
219 self.request.session.flash(
235 self.request.session.flash(
220 _('Integration {integration_name} updated successfully.').format(
236 _('Integration {integration_name} updated successfully.').format(
221 integration_name=self.IntegrationType.display_name),
237 integration_name=self.IntegrationType.display_name),
222 queue='success')
238 queue='success')
223
239
224 if self.repo:
240 if self.repo:
225 redirect_to = self.request.route_url(
241 redirect_to = self.request.route_url(
226 'repo_integrations_edit', repo_name=self.repo.repo_name,
242 'repo_integrations_edit', repo_name=self.repo.repo_name,
227 integration=self.integration.integration_type,
243 integration=self.integration.integration_type,
228 integration_id=self.integration.integration_id)
244 integration_id=self.integration.integration_id)
245 elif self.repo:
246 redirect_to = self.request.route_url(
247 'repo_group_integrations_edit',
248 repo_group_name=self.repo_group.group_name,
249 integration=self.integration.integration_type,
250 integration_id=self.integration.integration_id)
229 else:
251 else:
230 redirect_to = self.request.route_url(
252 redirect_to = self.request.route_url(
231 'global_integrations_edit',
253 'global_integrations_edit',
232 integration=self.integration.integration_type,
254 integration=self.integration.integration_type,
233 integration_id=self.integration.integration_id)
255 integration_id=self.integration.integration_id)
234
256
235 return HTTPFound(redirect_to)
257 return HTTPFound(redirect_to)
236
258
237 def index(self):
259 def index(self):
238 current_integrations = self.integrations
260 current_integrations = self.integrations
239 if self.IntegrationType:
261 if self.IntegrationType:
240 current_integrations = {
262 current_integrations = {
241 self.IntegrationType.key: self.integrations.get(
263 self.IntegrationType.key: self.integrations.get(
242 self.IntegrationType.key, [])
264 self.IntegrationType.key, [])
243 }
265 }
244
266
245 template_context = {
267 template_context = {
246 'current_IntegrationType': self.IntegrationType,
268 'current_IntegrationType': self.IntegrationType,
247 'current_integrations': current_integrations,
269 'current_integrations': current_integrations,
248 'available_integrations': integration_type_registry,
270 'available_integrations': integration_type_registry,
249 'c': self._template_c_context()
271 'c': self._template_c_context()
250 }
272 }
251
273
252 if self.repo:
274 if self.repo:
253 html = render('rhodecode:templates/admin/integrations/list.html',
275 html = render('rhodecode:templates/admin/integrations/list.html',
254 template_context,
276 template_context,
255 request=self.request)
277 request=self.request)
256 else:
278 else:
257 html = render('rhodecode:templates/admin/integrations/list.html',
279 html = render('rhodecode:templates/admin/integrations/list.html',
258 template_context,
280 template_context,
259 request=self.request)
281 request=self.request)
260
282
261 return Response(html)
283 return Response(html)
262
284
263
285
264 class GlobalIntegrationsView(IntegrationSettingsViewBase):
286 class GlobalIntegrationsView(IntegrationSettingsViewBase):
265 def perm_check(self, user):
287 def perm_check(self, user):
266 return auth.HasPermissionAll('hg.admin').check_permissions(user=user)
288 return auth.HasPermissionAll('hg.admin').check_permissions(user=user)
267
289
268
290
269 class RepoIntegrationsView(IntegrationSettingsViewBase):
291 class RepoIntegrationsView(IntegrationSettingsViewBase):
270 def perm_check(self, user):
292 def perm_check(self, user):
271 return auth.HasRepoPermissionAll('repository.admin'
293 return auth.HasRepoPermissionAll('repository.admin'
272 )(repo_name=self.repo.repo_name, user=user)
294 )(repo_name=self.repo.repo_name, user=user)
295
296 class RepoGroupIntegrationsView(IntegrationSettingsViewBase):
297 def perm_check(self, user):
298 return auth.HasRepoGroupPermissionAll('group.admin'
299 )(group_name=self.repo_group.group_name, user=user)
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,132 +1,140 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2016 RhodeCode GmbH
3 # Copyright (C) 2011-2016 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 """
22 """
23 Model for integrations
23 Model for integrations
24 """
24 """
25
25
26
26
27 import logging
27 import logging
28 import traceback
28 import traceback
29
29
30 from pylons import tmpl_context as c
30 from pylons import tmpl_context as c
31 from pylons.i18n.translation import _, ungettext
31 from pylons.i18n.translation import _, ungettext
32 from sqlalchemy import or_
32 from sqlalchemy import or_
33 from sqlalchemy.sql.expression import false, true
33 from sqlalchemy.sql.expression import false, true
34 from mako import exceptions
34 from mako import exceptions
35
35
36 import rhodecode
36 import rhodecode
37 from rhodecode import events
37 from rhodecode import events
38 from rhodecode.lib import helpers as h
38 from rhodecode.lib import helpers as h
39 from rhodecode.lib.caching_query import FromCache
39 from rhodecode.lib.caching_query import FromCache
40 from rhodecode.lib.utils import PartialRenderer
40 from rhodecode.lib.utils import PartialRenderer
41 from rhodecode.model import BaseModel
41 from rhodecode.model import BaseModel
42 from rhodecode.model.db import Integration, User
42 from rhodecode.model.db import Integration, User
43 from rhodecode.model.meta import Session
43 from rhodecode.model.meta import Session
44 from rhodecode.integrations import integration_type_registry
44 from rhodecode.integrations import integration_type_registry
45 from rhodecode.integrations.types.base import IntegrationTypeBase
45 from rhodecode.integrations.types.base import IntegrationTypeBase
46
46
47 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
48
48
49
49
50 class IntegrationModel(BaseModel):
50 class IntegrationModel(BaseModel):
51
51
52 cls = Integration
52 cls = Integration
53
53
54 def __get_integration(self, integration):
54 def __get_integration(self, integration):
55 if isinstance(integration, Integration):
55 if isinstance(integration, Integration):
56 return integration
56 return integration
57 elif isinstance(integration, (int, long)):
57 elif isinstance(integration, (int, long)):
58 return self.sa.query(Integration).get(integration)
58 return self.sa.query(Integration).get(integration)
59 else:
59 else:
60 if integration:
60 if integration:
61 raise Exception('integration must be int, long or Instance'
61 raise Exception('integration must be int, long or Instance'
62 ' of Integration got %s' % type(integration))
62 ' of Integration got %s' % type(integration))
63
63
64 def create(self, IntegrationType, enabled, name, settings, repo=None):
64 def create(self, IntegrationType, enabled, name, settings, repo=None):
65 """ Create an IntegrationType integration """
65 """ Create an IntegrationType integration """
66 integration = Integration()
66 integration = Integration()
67 integration.integration_type = IntegrationType.key
67 integration.integration_type = IntegrationType.key
68 integration.settings = {}
68 integration.settings = {}
69 integration.repo = repo
69 integration.repo = repo
70 integration.enabled = enabled
70 integration.enabled = enabled
71 integration.name = name
71 integration.name = name
72
72
73 self.sa.add(integration)
73 self.sa.add(integration)
74 self.sa.commit()
74 self.sa.commit()
75 return integration
75 return integration
76
76
77 def delete(self, integration):
77 def delete(self, integration):
78 try:
78 try:
79 integration = self.__get_integration(integration)
79 integration = self.__get_integration(integration)
80 if integration:
80 if integration:
81 self.sa.delete(integration)
81 self.sa.delete(integration)
82 return True
82 return True
83 except Exception:
83 except Exception:
84 log.error(traceback.format_exc())
84 log.error(traceback.format_exc())
85 raise
85 raise
86 return False
86 return False
87
87
88 def get_integration_handler(self, integration):
88 def get_integration_handler(self, integration):
89 TypeClass = integration_type_registry.get(integration.integration_type)
89 TypeClass = integration_type_registry.get(integration.integration_type)
90 if not TypeClass:
90 if not TypeClass:
91 log.error('No class could be found for integration type: {}'.format(
91 log.error('No class could be found for integration type: {}'.format(
92 integration.integration_type))
92 integration.integration_type))
93 return None
93 return None
94
94
95 return TypeClass(integration.settings)
95 return TypeClass(integration.settings)
96
96
97 def send_event(self, integration, event):
97 def send_event(self, integration, event):
98 """ Send an event to an integration """
98 """ Send an event to an integration """
99 handler = self.get_integration_handler(integration)
99 handler = self.get_integration_handler(integration)
100 if handler:
100 if handler:
101 handler.send_event(event)
101 handler.send_event(event)
102
102
103 def get_integrations(self, repo=None):
103 def get_integrations(self, repo=None, repo_group=None):
104 if repo:
104 if repo:
105 return self.sa.query(Integration).filter(
105 return self.sa.query(Integration).filter(
106 Integration.repo_id==repo.repo_id).all()
106 Integration.repo_id==repo.repo_id).all()
107 elif repo_group:
108 return self.sa.query(Integration).filter(
109 Integration.repo_group_id==repo_group.group_id).all()
107
110
108 # global integrations
111 # global integrations
109 return self.sa.query(Integration).filter(
112 return self.sa.query(Integration).filter(
110 Integration.repo_id==None).all()
113 Integration.repo_id==None).all()
111
114
112 def get_for_event(self, event, cache=False):
115 def get_for_event(self, event, cache=False):
113 """
116 """
114 Get integrations that match an event
117 Get integrations that match an event
115 """
118 """
116 query = self.sa.query(Integration).filter(Integration.enabled==True)
119 query = self.sa.query(Integration).filter(Integration.enabled==True)
117
120
118 if isinstance(event, events.RepoEvent): # global + repo integrations
121 if isinstance(event, events.RepoEvent): # global + repo integrations
122 # + repo_group integrations
123 parent_groups = event.repo.groups_with_parents
119 query = query.filter(
124 query = query.filter(
120 or_(Integration.repo_id==None,
125 or_(Integration.repo_id==None,
121 Integration.repo_id==event.repo.repo_id))
126 Integration.repo_id==event.repo.repo_id,
127 Integration.repo_group_id.in_(
128 [group.group_id for group in parent_groups]
129 )))
122 if cache:
130 if cache:
123 query = query.options(FromCache(
131 query = query.options(FromCache(
124 "sql_cache_short",
132 "sql_cache_short",
125 "get_enabled_repo_integrations_%i" % event.repo.repo_id))
133 "get_enabled_repo_integrations_%i" % event.repo.repo_id))
126 else: # only global integrations
134 else: # only global integrations
127 query = query.filter(Integration.repo_id==None)
135 query = query.filter(Integration.repo_id==None)
128 if cache:
136 if cache:
129 query = query.options(FromCache(
137 query = query.options(FromCache(
130 "sql_cache_short", "get_enabled_global_integrations"))
138 "sql_cache_short", "get_enabled_global_integrations"))
131
139
132 return query.all()
140 return query.all()
@@ -1,40 +1,42 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%!
2 <%!
3 def inherit(context):
3 def inherit(context):
4 if context['c'].repo:
4 if context['c'].repo:
5 return "/admin/repos/repo_edit.html"
5 return "/admin/repos/repo_edit.html"
6 elif context['c'].repo_group:
7 return "/admin/repo_groups/repo_group_edit.html"
6 else:
8 else:
7 return "/admin/settings/settings.html"
9 return "/admin/settings/settings.html"
8 %>
10 %>
9 <%inherit file="${inherit(context)}" />
11 <%inherit file="${inherit(context)}" />
10
12
11 <%def name="title()">
13 <%def name="title()">
12 ${_('Integrations Settings')}
14 ${_('Integrations Settings')}
13 %if c.rhodecode_name:
15 %if c.rhodecode_name:
14 &middot; ${h.branding(c.rhodecode_name)}
16 &middot; ${h.branding(c.rhodecode_name)}
15 %endif
17 %endif
16 </%def>
18 </%def>
17
19
18 <%def name="breadcrumbs_links()">
20 <%def name="breadcrumbs_links()">
19 ${h.link_to(_('Admin'),h.url('admin_home'))}
21 ${h.link_to(_('Admin'),h.url('admin_home'))}
20 &raquo;
22 &raquo;
21 ${_('Integrations')}
23 ${_('Integrations')}
22 </%def>
24 </%def>
23
25
24 <%def name="menu_bar_nav()">
26 <%def name="menu_bar_nav()">
25 %if c.repo:
27 %if c.repo:
26 ${self.menu_items(active='repositories')}
28 ${self.menu_items(active='repositories')}
27 %else:
29 %else:
28 ${self.menu_items(active='admin')}
30 ${self.menu_items(active='admin')}
29 %endif
31 %endif
30 </%def>
32 </%def>
31
33
32 <%def name="menu_bar_subnav()">
34 <%def name="menu_bar_subnav()">
33 %if c.repo:
35 %if c.repo:
34 ${self.repo_menu(active='options')}
36 ${self.repo_menu(active='options')}
35 %endif
37 %endif
36 </%def>
38 </%def>
37
39
38 <%def name="main_content()">
40 <%def name="main_content()">
39 ${next.body()}
41 ${next.body()}
40 </%def>
42 </%def>
@@ -1,147 +1,156 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="base.html"/>
2 <%inherit file="base.html"/>
3
3
4 <%def name="breadcrumbs_links()">
4 <%def name="breadcrumbs_links()">
5 %if c.repo:
5 %if c.repo:
6 ${h.link_to('Settings',h.url('edit_repo', repo_name=c.repo.repo_name))}
6 ${h.link_to('Settings',h.url('edit_repo', repo_name=c.repo.repo_name))}
7 %else:
7 %else:
8 ${h.link_to(_('Admin'),h.url('admin_home'))}
8 ${h.link_to(_('Admin'),h.url('admin_home'))}
9 &raquo;
9 &raquo;
10 ${h.link_to(_('Settings'),h.url('admin_settings'))}
10 ${h.link_to(_('Settings'),h.url('admin_settings'))}
11 %endif
11 %endif
12 %if current_IntegrationType:
12 %if current_IntegrationType:
13 &raquo;
13 &raquo;
14 %if c.repo:
14 %if c.repo:
15 ${h.link_to(_('Integrations'),
15 ${h.link_to(_('Integrations'),
16 request.route_url(route_name='repo_integrations_home',
16 request.route_url(route_name='repo_integrations_home',
17 repo_name=c.repo.repo_name))}
17 repo_name=c.repo.repo_name))}
18 %else:
18 %else:
19 ${h.link_to(_('Integrations'),
19 ${h.link_to(_('Integrations'),
20 request.route_url(route_name='global_integrations_home'))}
20 request.route_url(route_name='global_integrations_home'))}
21 %endif
21 %endif
22 &raquo;
22 &raquo;
23 ${current_IntegrationType.display_name}
23 ${current_IntegrationType.display_name}
24 %else:
24 %else:
25 &raquo;
25 &raquo;
26 ${_('Integrations')}
26 ${_('Integrations')}
27 %endif
27 %endif
28 </%def>
28 </%def>
29 <div class="panel panel-default">
29 <div class="panel panel-default">
30 <div class="panel-heading">
30 <div class="panel-heading">
31 <h3 class="panel-title">${_('Create New Integration')}</h3>
31 <h3 class="panel-title">${_('Create New Integration')}</h3>
32 </div>
32 </div>
33 <div class="panel-body">
33 <div class="panel-body">
34 %if not available_integrations:
34 %if not available_integrations:
35 ${_('No integrations available.')}
35 ${_('No integrations available.')}
36 %else:
36 %else:
37 %for integration in available_integrations:
37 %for integration in available_integrations:
38 <%
38 <%
39 if c.repo:
39 if c.repo:
40 create_url = request.route_url('repo_integrations_create',
40 create_url = request.route_path('repo_integrations_create',
41 repo_name=c.repo.repo_name,
41 repo_name=c.repo.repo_name,
42 integration=integration)
42 integration=integration)
43 elif c.repo_group:
44 create_url = request.route_path('repo_group_integrations_create',
45 repo_group_name=c.repo_group.group_name,
46 integration=integration)
43 else:
47 else:
44 create_url = request.route_url('global_integrations_create',
48 create_url = request.route_path('global_integrations_create',
45 integration=integration)
49 integration=integration)
46 %>
50 %>
47 <a href="${create_url}" class="btn">
51 <a href="${create_url}" class="btn">
48 ${integration}
52 ${integration}
49 </a>
53 </a>
50 %endfor
54 %endfor
51 %endif
55 %endif
52 </div>
56 </div>
53 </div>
57 </div>
54 <div class="panel panel-default">
58 <div class="panel panel-default">
55 <div class="panel-heading">
59 <div class="panel-heading">
56 <h3 class="panel-title">${_('Current Integrations')}</h3>
60 <h3 class="panel-title">${_('Current Integrations')}</h3>
57 </div>
61 </div>
58 <div class="panel-body">
62 <div class="panel-body">
59 <table class="rctable issuetracker">
63 <table class="rctable issuetracker">
60 <thead>
64 <thead>
61 <tr>
65 <tr>
62 <th>${_('Enabled')}</th>
66 <th>${_('Enabled')}</th>
63 <th>${_('Description')}</th>
67 <th>${_('Description')}</th>
64 <th>${_('Type')}</th>
68 <th>${_('Type')}</th>
65 <th>${_('Actions')}</th>
69 <th>${_('Actions')}</th>
66 <th></th>
70 <th></th>
67 </tr>
71 </tr>
68 </thead>
72 </thead>
69 <tbody>
73 <tbody>
70
74
71 %for integration_type, integrations in sorted(current_integrations.items()):
75 %for integration_type, integrations in sorted(current_integrations.items()):
72 %for integration in sorted(integrations, key=lambda x: x.name):
76 %for integration in sorted(integrations, key=lambda x: x.name):
73 <tr id="integration_${integration.integration_id}">
77 <tr id="integration_${integration.integration_id}">
74 <td class="td-enabled">
78 <td class="td-enabled">
75 %if integration.enabled:
79 %if integration.enabled:
76 <div class="flag_status approved pull-left"></div>
80 <div class="flag_status approved pull-left"></div>
77 %else:
81 %else:
78 <div class="flag_status rejected pull-left"></div>
82 <div class="flag_status rejected pull-left"></div>
79 %endif
83 %endif
80 </td>
84 </td>
81 <td class="td-description">
85 <td class="td-description">
82 ${integration.name}
86 ${integration.name}
83 </td>
87 </td>
84 <td class="td-regex">
88 <td class="td-regex">
85 ${integration.integration_type}
89 ${integration.integration_type}
86 </td>
90 </td>
87 <td class="td-action">
91 <td class="td-action">
88 %if integration_type not in available_integrations:
92 %if integration_type not in available_integrations:
89 ${_('unknown integration')}
93 ${_('unknown integration')}
90 %else:
94 %else:
91 <%
95 <%
92 if c.repo:
96 if c.repo:
93 edit_url = request.route_url('repo_integrations_edit',
97 edit_url = request.route_path('repo_integrations_edit',
94 repo_name=c.repo.repo_name,
98 repo_name=c.repo.repo_name,
95 integration=integration.integration_type,
99 integration=integration.integration_type,
96 integration_id=integration.integration_id)
100 integration_id=integration.integration_id)
101 elif c.repo_group:
102 edit_url = request.route_path('repo_group_integrations_edit',
103 repo_group_name=c.repo_group.group_name,
104 integration=integration.integration_type,
105 integration_id=integration.integration_id)
97 else:
106 else:
98 edit_url = request.route_url('global_integrations_edit',
107 edit_url = request.route_path('global_integrations_edit',
99 integration=integration.integration_type,
108 integration=integration.integration_type,
100 integration_id=integration.integration_id)
109 integration_id=integration.integration_id)
101 %>
110 %>
102 <div class="grid_edit">
111 <div class="grid_edit">
103 <a href="${edit_url}">${_('Edit')}</a>
112 <a href="${edit_url}">${_('Edit')}</a>
104 </div>
113 </div>
105 <div class="grid_delete">
114 <div class="grid_delete">
106 <a href="${edit_url}"
115 <a href="${edit_url}"
107 class="btn btn-link btn-danger delete_integration_entry"
116 class="btn btn-link btn-danger delete_integration_entry"
108 data-desc="${integration.name}"
117 data-desc="${integration.name}"
109 data-uid="${integration.integration_id}">
118 data-uid="${integration.integration_id}">
110 ${_('Delete')}
119 ${_('Delete')}
111 </a>
120 </a>
112 </div>
121 </div>
113 %endif
122 %endif
114 </td>
123 </td>
115 </tr>
124 </tr>
116 %endfor
125 %endfor
117 %endfor
126 %endfor
118 <tr id="last-row"></tr>
127 <tr id="last-row"></tr>
119 </tbody>
128 </tbody>
120 </table>
129 </table>
121 </div>
130 </div>
122 </div>
131 </div>
123 <script type="text/javascript">
132 <script type="text/javascript">
124 var delete_integration = function(entry) {
133 var delete_integration = function(entry) {
125 if (confirm("Confirm to remove this integration: "+$(entry).data('desc'))) {
134 if (confirm("Confirm to remove this integration: "+$(entry).data('desc'))) {
126 var request = $.ajax({
135 var request = $.ajax({
127 type: "POST",
136 type: "POST",
128 url: $(entry).attr('href'),
137 url: $(entry).attr('href'),
129 data: {
138 data: {
130 'delete': 'delete',
139 'delete': 'delete',
131 'csrf_token': CSRF_TOKEN
140 'csrf_token': CSRF_TOKEN
132 },
141 },
133 success: function(){
142 success: function(){
134 location.reload();
143 location.reload();
135 },
144 },
136 error: function(data, textStatus, errorThrown){
145 error: function(data, textStatus, errorThrown){
137 alert("Error while deleting entry.\nError code {0} ({1}). URL: {2}".format(data.status,data.statusText,$(entry)[0].url));
146 alert("Error while deleting entry.\nError code {0} ({1}). URL: {2}".format(data.status,data.statusText,$(entry)[0].url));
138 }
147 }
139 });
148 });
140 };
149 };
141 }
150 }
142
151
143 $('.delete_integration_entry').on('click', function(e){
152 $('.delete_integration_entry').on('click', function(e){
144 e.preventDefault();
153 e.preventDefault();
145 delete_integration(this);
154 delete_integration(this);
146 });
155 });
147 </script> No newline at end of file
156 </script>
@@ -1,56 +1,61 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('%s repository group settings') % c.repo_group.name}
5 ${_('%s repository group settings') % c.repo_group.name}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${h.link_to(_('Admin'),h.url('admin_home'))}
12 ${h.link_to(_('Admin'),h.url('admin_home'))}
13 &raquo;
13 &raquo;
14 ${h.link_to(_('Repository Groups'),h.url('repo_groups'))}
14 ${h.link_to(_('Repository Groups'),h.url('repo_groups'))}
15 %if c.repo_group.parent_group:
15 %if c.repo_group.parent_group:
16 &raquo; ${h.link_to(c.repo_group.parent_group.name,h.url('repo_group_home',group_name=c.repo_group.parent_group.group_name))}
16 &raquo; ${h.link_to(c.repo_group.parent_group.name,h.url('repo_group_home',group_name=c.repo_group.parent_group.group_name))}
17 %endif
17 %endif
18 &raquo; ${c.repo_group.name}
18 &raquo; ${c.repo_group.name}
19 </%def>
19 </%def>
20
20
21 <%def name="breadcrumbs_side_links()">
21 <%def name="breadcrumbs_side_links()">
22 <ul class="links">
22 <ul class="links">
23 <li>
23 <li>
24 <a href="${h.url('new_repo_group', parent_group=c.repo_group.group_id)}" class="btn btn-small btn-success">${_(u'Add Child Group')}</a>
24 <a href="${h.url('new_repo_group', parent_group=c.repo_group.group_id)}" class="btn btn-small btn-success">${_(u'Add Child Group')}</a>
25 </li>
25 </li>
26 </ul>
26 </ul>
27 </%def>
27 </%def>
28
28
29 <%def name="menu_bar_nav()">
29 <%def name="menu_bar_nav()">
30 ${self.menu_items(active='admin')}
30 ${self.menu_items(active='admin')}
31 </%def>
31 </%def>
32
32
33 <%def name="main_content()">
34 <%include file="/admin/repo_groups/repo_group_edit_${c.active}.html"/>
35 </%def>
36
33 <%def name="main()">
37 <%def name="main()">
34 <div class="box">
38 <div class="box">
35 <div class="title">
39 <div class="title">
36 ${self.breadcrumbs()}
40 ${self.breadcrumbs()}
37 ${self.breadcrumbs_side_links()}
41 ${self.breadcrumbs_side_links()}
38 </div>
42 </div>
39
43
40 <div class="sidebar-col-wrapper">
44 <div class="sidebar-col-wrapper">
41 ##main
45 ##main
42 <div class="sidebar">
46 <div class="sidebar">
43 <ul class="nav nav-pills nav-stacked">
47 <ul class="nav nav-pills nav-stacked">
44 <li class="${'active' if c.active=='settings' else ''}"><a href="${h.url('edit_repo_group', group_name=c.repo_group.group_name)}">${_('Settings')}</a></li>
48 <li class="${'active' if c.active=='settings' else ''}"><a href="${h.url('edit_repo_group', group_name=c.repo_group.group_name)}">${_('Settings')}</a></li>
45 <li class="${'active' if c.active=='perms' else ''}"><a href="${h.url('edit_repo_group_perms', group_name=c.repo_group.group_name)}">${_('Permissions')}</a></li>
49 <li class="${'active' if c.active=='perms' else ''}"><a href="${h.url('edit_repo_group_perms', group_name=c.repo_group.group_name)}">${_('Permissions')}</a></li>
46 <li class="${'active' if c.active=='advanced' else ''}"><a href="${h.url('edit_repo_group_advanced', group_name=c.repo_group.group_name)}">${_('Advanced')}</a></li>
50 <li class="${'active' if c.active=='advanced' else ''}"><a href="${h.url('edit_repo_group_advanced', group_name=c.repo_group.group_name)}">${_('Advanced')}</a></li>
51 <li class="${'active' if c.active=='integrations' else ''}"><a href="${h.route_path('repo_group_integrations_home', repo_group_name=c.repo_group.group_name)}">${_('Integrations')}</a></li>
47 </ul>
52 </ul>
48 </div>
53 </div>
49
54
50 <div class="main-content-full-width">
55 <div class="main-content-full-width">
51 <%include file="/admin/repo_groups/repo_group_edit_${c.active}.html"/>
56 ${self.main_content()}
52 </div>
57 </div>
53
58
54 </div>
59 </div>
55 </div>
60 </div>
56 </%def>
61 </%def>
General Comments 0
You need to be logged in to leave comments. Login now