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