##// END OF EJS Templates
events: use a distinction between RhodeCodeEvent which is a base class and it used by all events, and...
marcink -
r2921:042146f6 default
parent child Browse files
Show More
@@ -1,522 +1,518 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2018 RhodeCode GmbH
3 # Copyright (C) 2010-2018 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 os
21 import os
22 import sys
22 import sys
23 import logging
23 import logging
24 import traceback
24 import traceback
25 import collections
25 import collections
26 import tempfile
26 import tempfile
27
27
28 from paste.gzipper import make_gzip_middleware
28 from paste.gzipper import make_gzip_middleware
29 from pyramid.wsgi import wsgiapp
29 from pyramid.wsgi import wsgiapp
30 from pyramid.authorization import ACLAuthorizationPolicy
30 from pyramid.authorization import ACLAuthorizationPolicy
31 from pyramid.config import Configurator
31 from pyramid.config import Configurator
32 from pyramid.settings import asbool, aslist
32 from pyramid.settings import asbool, aslist
33 from pyramid.httpexceptions import (
33 from pyramid.httpexceptions import (
34 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound, HTTPNotFound)
34 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound, HTTPNotFound)
35 from pyramid.events import ApplicationCreated
35 from pyramid.events import ApplicationCreated
36 from pyramid.renderers import render_to_response
36 from pyramid.renderers import render_to_response
37
37
38 from rhodecode.model import meta
38 from rhodecode.model import meta
39 from rhodecode.config import patches
39 from rhodecode.config import patches
40 from rhodecode.config import utils as config_utils
40 from rhodecode.config import utils as config_utils
41 from rhodecode.config.environment import load_pyramid_environment
41 from rhodecode.config.environment import load_pyramid_environment
42
42
43 from rhodecode.lib.middleware.vcs import VCSMiddleware
43 from rhodecode.lib.middleware.vcs import VCSMiddleware
44 from rhodecode.lib.request import Request
44 from rhodecode.lib.request import Request
45 from rhodecode.lib.vcs import VCSCommunicationError
45 from rhodecode.lib.vcs import VCSCommunicationError
46 from rhodecode.lib.exceptions import VCSServerUnavailable
46 from rhodecode.lib.exceptions import VCSServerUnavailable
47 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
47 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
48 from rhodecode.lib.middleware.https_fixup import HttpsFixup
48 from rhodecode.lib.middleware.https_fixup import HttpsFixup
49 from rhodecode.lib.celerylib.loader import configure_celery
49 from rhodecode.lib.celerylib.loader import configure_celery
50 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
50 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
51 from rhodecode.lib.utils2 import aslist as rhodecode_aslist, AttributeDict
51 from rhodecode.lib.utils2 import aslist as rhodecode_aslist, AttributeDict
52 from rhodecode.lib.exc_tracking import store_exception
52 from rhodecode.lib.exc_tracking import store_exception
53 from rhodecode.subscribers import (
53 from rhodecode.subscribers import (
54 scan_repositories_if_enabled, write_js_routes_if_enabled,
54 scan_repositories_if_enabled, write_js_routes_if_enabled,
55 write_metadata_if_needed, inject_app_settings)
55 write_metadata_if_needed, inject_app_settings)
56
56
57
57
58 log = logging.getLogger(__name__)
58 log = logging.getLogger(__name__)
59
59
60
60
61 def is_http_error(response):
61 def is_http_error(response):
62 # error which should have traceback
62 # error which should have traceback
63 return response.status_code > 499
63 return response.status_code > 499
64
64
65
65
66 def make_pyramid_app(global_config, **settings):
66 def make_pyramid_app(global_config, **settings):
67 """
67 """
68 Constructs the WSGI application based on Pyramid.
68 Constructs the WSGI application based on Pyramid.
69
69
70 Specials:
70 Specials:
71
71
72 * The application can also be integrated like a plugin via the call to
72 * The application can also be integrated like a plugin via the call to
73 `includeme`. This is accompanied with the other utility functions which
73 `includeme`. This is accompanied with the other utility functions which
74 are called. Changing this should be done with great care to not break
74 are called. Changing this should be done with great care to not break
75 cases when these fragments are assembled from another place.
75 cases when these fragments are assembled from another place.
76
76
77 """
77 """
78
78
79 # Allows to use format style "{ENV_NAME}" placeholders in the configuration. It
79 # Allows to use format style "{ENV_NAME}" placeholders in the configuration. It
80 # will be replaced by the value of the environment variable "NAME" in this case.
80 # will be replaced by the value of the environment variable "NAME" in this case.
81 environ = {
81 environ = {
82 'ENV_{}'.format(key): value for key, value in os.environ.items()}
82 'ENV_{}'.format(key): value for key, value in os.environ.items()}
83
83
84 global_config = _substitute_values(global_config, environ)
84 global_config = _substitute_values(global_config, environ)
85 settings = _substitute_values(settings, environ)
85 settings = _substitute_values(settings, environ)
86
86
87 sanitize_settings_and_apply_defaults(settings)
87 sanitize_settings_and_apply_defaults(settings)
88
88
89 config = Configurator(settings=settings)
89 config = Configurator(settings=settings)
90
90
91 # Apply compatibility patches
91 # Apply compatibility patches
92 patches.inspect_getargspec()
92 patches.inspect_getargspec()
93
93
94 load_pyramid_environment(global_config, settings)
94 load_pyramid_environment(global_config, settings)
95
95
96 # Static file view comes first
96 # Static file view comes first
97 includeme_first(config)
97 includeme_first(config)
98
98
99 includeme(config)
99 includeme(config)
100
100
101 pyramid_app = config.make_wsgi_app()
101 pyramid_app = config.make_wsgi_app()
102 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
102 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
103 pyramid_app.config = config
103 pyramid_app.config = config
104
104
105 config.configure_celery(global_config['__file__'])
105 config.configure_celery(global_config['__file__'])
106 # creating the app uses a connection - return it after we are done
106 # creating the app uses a connection - return it after we are done
107 meta.Session.remove()
107 meta.Session.remove()
108
108
109 log.info('Pyramid app %s created and configured.', pyramid_app)
109 log.info('Pyramid app %s created and configured.', pyramid_app)
110 return pyramid_app
110 return pyramid_app
111
111
112
112
113 def not_found_view(request):
113 def not_found_view(request):
114 """
114 """
115 This creates the view which should be registered as not-found-view to
115 This creates the view which should be registered as not-found-view to
116 pyramid.
116 pyramid.
117 """
117 """
118
118
119 if not getattr(request, 'vcs_call', None):
119 if not getattr(request, 'vcs_call', None):
120 # handle like regular case with our error_handler
120 # handle like regular case with our error_handler
121 return error_handler(HTTPNotFound(), request)
121 return error_handler(HTTPNotFound(), request)
122
122
123 # handle not found view as a vcs call
123 # handle not found view as a vcs call
124 settings = request.registry.settings
124 settings = request.registry.settings
125 ae_client = getattr(request, 'ae_client', None)
125 ae_client = getattr(request, 'ae_client', None)
126 vcs_app = VCSMiddleware(
126 vcs_app = VCSMiddleware(
127 HTTPNotFound(), request.registry, settings,
127 HTTPNotFound(), request.registry, settings,
128 appenlight_client=ae_client)
128 appenlight_client=ae_client)
129
129
130 return wsgiapp(vcs_app)(None, request)
130 return wsgiapp(vcs_app)(None, request)
131
131
132
132
133 def error_handler(exception, request):
133 def error_handler(exception, request):
134 import rhodecode
134 import rhodecode
135 from rhodecode.lib import helpers
135 from rhodecode.lib import helpers
136
136
137 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
137 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
138
138
139 base_response = HTTPInternalServerError()
139 base_response = HTTPInternalServerError()
140 # prefer original exception for the response since it may have headers set
140 # prefer original exception for the response since it may have headers set
141 if isinstance(exception, HTTPException):
141 if isinstance(exception, HTTPException):
142 base_response = exception
142 base_response = exception
143 elif isinstance(exception, VCSCommunicationError):
143 elif isinstance(exception, VCSCommunicationError):
144 base_response = VCSServerUnavailable()
144 base_response = VCSServerUnavailable()
145
145
146 if is_http_error(base_response):
146 if is_http_error(base_response):
147 log.exception(
147 log.exception(
148 'error occurred handling this request for path: %s', request.path)
148 'error occurred handling this request for path: %s', request.path)
149
149
150 error_explanation = base_response.explanation or str(base_response)
150 error_explanation = base_response.explanation or str(base_response)
151 if base_response.status_code == 404:
151 if base_response.status_code == 404:
152 error_explanation += " Or you don't have permission to access it."
152 error_explanation += " Or you don't have permission to access it."
153 c = AttributeDict()
153 c = AttributeDict()
154 c.error_message = base_response.status
154 c.error_message = base_response.status
155 c.error_explanation = error_explanation
155 c.error_explanation = error_explanation
156 c.visual = AttributeDict()
156 c.visual = AttributeDict()
157
157
158 c.visual.rhodecode_support_url = (
158 c.visual.rhodecode_support_url = (
159 request.registry.settings.get('rhodecode_support_url') or
159 request.registry.settings.get('rhodecode_support_url') or
160 request.route_url('rhodecode_support')
160 request.route_url('rhodecode_support')
161 )
161 )
162 c.redirect_time = 0
162 c.redirect_time = 0
163 c.rhodecode_name = rhodecode_title
163 c.rhodecode_name = rhodecode_title
164 if not c.rhodecode_name:
164 if not c.rhodecode_name:
165 c.rhodecode_name = 'Rhodecode'
165 c.rhodecode_name = 'Rhodecode'
166
166
167 c.causes = []
167 c.causes = []
168 if is_http_error(base_response):
168 if is_http_error(base_response):
169 c.causes.append('Server is overloaded.')
169 c.causes.append('Server is overloaded.')
170 c.causes.append('Server database connection is lost.')
170 c.causes.append('Server database connection is lost.')
171 c.causes.append('Server expected unhandled error.')
171 c.causes.append('Server expected unhandled error.')
172
172
173 if hasattr(base_response, 'causes'):
173 if hasattr(base_response, 'causes'):
174 c.causes = base_response.causes
174 c.causes = base_response.causes
175
175
176 c.messages = helpers.flash.pop_messages(request=request)
176 c.messages = helpers.flash.pop_messages(request=request)
177
177
178 exc_info = sys.exc_info()
178 exc_info = sys.exc_info()
179 c.exception_id = id(exc_info)
179 c.exception_id = id(exc_info)
180 c.show_exception_id = isinstance(base_response, VCSServerUnavailable) \
180 c.show_exception_id = isinstance(base_response, VCSServerUnavailable) \
181 or base_response.status_code > 499
181 or base_response.status_code > 499
182 c.exception_id_url = request.route_url(
182 c.exception_id_url = request.route_url(
183 'admin_settings_exception_tracker_show', exception_id=c.exception_id)
183 'admin_settings_exception_tracker_show', exception_id=c.exception_id)
184
184
185 if c.show_exception_id:
185 if c.show_exception_id:
186 store_exception(c.exception_id, exc_info)
186 store_exception(c.exception_id, exc_info)
187
187
188 response = render_to_response(
188 response = render_to_response(
189 '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request,
189 '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request,
190 response=base_response)
190 response=base_response)
191
191
192 return response
192 return response
193
193
194
194
195 def includeme_first(config):
195 def includeme_first(config):
196 # redirect automatic browser favicon.ico requests to correct place
196 # redirect automatic browser favicon.ico requests to correct place
197 def favicon_redirect(context, request):
197 def favicon_redirect(context, request):
198 return HTTPFound(
198 return HTTPFound(
199 request.static_path('rhodecode:public/images/favicon.ico'))
199 request.static_path('rhodecode:public/images/favicon.ico'))
200
200
201 config.add_view(favicon_redirect, route_name='favicon')
201 config.add_view(favicon_redirect, route_name='favicon')
202 config.add_route('favicon', '/favicon.ico')
202 config.add_route('favicon', '/favicon.ico')
203
203
204 def robots_redirect(context, request):
204 def robots_redirect(context, request):
205 return HTTPFound(
205 return HTTPFound(
206 request.static_path('rhodecode:public/robots.txt'))
206 request.static_path('rhodecode:public/robots.txt'))
207
207
208 config.add_view(robots_redirect, route_name='robots')
208 config.add_view(robots_redirect, route_name='robots')
209 config.add_route('robots', '/robots.txt')
209 config.add_route('robots', '/robots.txt')
210
210
211 config.add_static_view(
211 config.add_static_view(
212 '_static/deform', 'deform:static')
212 '_static/deform', 'deform:static')
213 config.add_static_view(
213 config.add_static_view(
214 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
214 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
215
215
216
216
217 def includeme(config):
217 def includeme(config):
218 settings = config.registry.settings
218 settings = config.registry.settings
219 config.set_request_factory(Request)
219 config.set_request_factory(Request)
220
220
221 # plugin information
221 # plugin information
222 config.registry.rhodecode_plugins = collections.OrderedDict()
222 config.registry.rhodecode_plugins = collections.OrderedDict()
223
223
224 config.add_directive(
224 config.add_directive(
225 'register_rhodecode_plugin', register_rhodecode_plugin)
225 'register_rhodecode_plugin', register_rhodecode_plugin)
226
226
227 config.add_directive('configure_celery', configure_celery)
227 config.add_directive('configure_celery', configure_celery)
228
228
229 if asbool(settings.get('appenlight', 'false')):
229 if asbool(settings.get('appenlight', 'false')):
230 config.include('appenlight_client.ext.pyramid_tween')
230 config.include('appenlight_client.ext.pyramid_tween')
231
231
232 # Includes which are required. The application would fail without them.
232 # Includes which are required. The application would fail without them.
233 config.include('pyramid_mako')
233 config.include('pyramid_mako')
234 config.include('pyramid_beaker')
234 config.include('pyramid_beaker')
235 config.include('rhodecode.lib.caches')
235 config.include('rhodecode.lib.caches')
236 config.include('rhodecode.lib.rc_cache')
236 config.include('rhodecode.lib.rc_cache')
237
237
238 config.include('rhodecode.authentication')
238 config.include('rhodecode.authentication')
239 config.include('rhodecode.integrations')
239 config.include('rhodecode.integrations')
240
240
241 # apps
241 # apps
242 config.include('rhodecode.apps._base')
242 config.include('rhodecode.apps._base')
243 config.include('rhodecode.apps.ops')
243 config.include('rhodecode.apps.ops')
244
244
245 config.include('rhodecode.apps.admin')
245 config.include('rhodecode.apps.admin')
246 config.include('rhodecode.apps.channelstream')
246 config.include('rhodecode.apps.channelstream')
247 config.include('rhodecode.apps.login')
247 config.include('rhodecode.apps.login')
248 config.include('rhodecode.apps.home')
248 config.include('rhodecode.apps.home')
249 config.include('rhodecode.apps.journal')
249 config.include('rhodecode.apps.journal')
250 config.include('rhodecode.apps.repository')
250 config.include('rhodecode.apps.repository')
251 config.include('rhodecode.apps.repo_group')
251 config.include('rhodecode.apps.repo_group')
252 config.include('rhodecode.apps.user_group')
252 config.include('rhodecode.apps.user_group')
253 config.include('rhodecode.apps.search')
253 config.include('rhodecode.apps.search')
254 config.include('rhodecode.apps.user_profile')
254 config.include('rhodecode.apps.user_profile')
255 config.include('rhodecode.apps.user_group_profile')
255 config.include('rhodecode.apps.user_group_profile')
256 config.include('rhodecode.apps.my_account')
256 config.include('rhodecode.apps.my_account')
257 config.include('rhodecode.apps.svn_support')
257 config.include('rhodecode.apps.svn_support')
258 config.include('rhodecode.apps.ssh_support')
258 config.include('rhodecode.apps.ssh_support')
259 config.include('rhodecode.apps.gist')
259 config.include('rhodecode.apps.gist')
260
260
261 config.include('rhodecode.apps.debug_style')
261 config.include('rhodecode.apps.debug_style')
262 config.include('rhodecode.tweens')
262 config.include('rhodecode.tweens')
263 config.include('rhodecode.api')
263 config.include('rhodecode.api')
264
264
265 config.add_route(
265 config.add_route(
266 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
266 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
267
267
268 config.add_translation_dirs('rhodecode:i18n/')
268 config.add_translation_dirs('rhodecode:i18n/')
269 settings['default_locale_name'] = settings.get('lang', 'en')
269 settings['default_locale_name'] = settings.get('lang', 'en')
270
270
271 # Add subscribers.
271 # Add subscribers.
272 config.add_subscriber(inject_app_settings, ApplicationCreated)
272 config.add_subscriber(inject_app_settings, ApplicationCreated)
273 config.add_subscriber(scan_repositories_if_enabled, ApplicationCreated)
273 config.add_subscriber(scan_repositories_if_enabled, ApplicationCreated)
274 config.add_subscriber(write_metadata_if_needed, ApplicationCreated)
274 config.add_subscriber(write_metadata_if_needed, ApplicationCreated)
275 config.add_subscriber(write_js_routes_if_enabled, ApplicationCreated)
275 config.add_subscriber(write_js_routes_if_enabled, ApplicationCreated)
276
276
277 # events
278 # TODO(marcink): this should be done when pyramid migration is finished
279 # config.add_subscriber(
280 # 'rhodecode.integrations.integrations_event_handler',
281 # 'rhodecode.events.RhodecodeEvent')
282
277
283 # request custom methods
278 # request custom methods
284 config.add_request_method(
279 config.add_request_method(
285 'rhodecode.lib.partial_renderer.get_partial_renderer',
280 'rhodecode.lib.partial_renderer.get_partial_renderer',
286 'get_partial_renderer')
281 'get_partial_renderer')
287
282
288 # Set the authorization policy.
283 # Set the authorization policy.
289 authz_policy = ACLAuthorizationPolicy()
284 authz_policy = ACLAuthorizationPolicy()
290 config.set_authorization_policy(authz_policy)
285 config.set_authorization_policy(authz_policy)
291
286
292 # Set the default renderer for HTML templates to mako.
287 # Set the default renderer for HTML templates to mako.
293 config.add_mako_renderer('.html')
288 config.add_mako_renderer('.html')
294
289
295 config.add_renderer(
290 config.add_renderer(
296 name='json_ext',
291 name='json_ext',
297 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
292 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
298
293
299 # include RhodeCode plugins
294 # include RhodeCode plugins
300 includes = aslist(settings.get('rhodecode.includes', []))
295 includes = aslist(settings.get('rhodecode.includes', []))
301 for inc in includes:
296 for inc in includes:
302 config.include(inc)
297 config.include(inc)
303
298
304 # custom not found view, if our pyramid app doesn't know how to handle
299 # custom not found view, if our pyramid app doesn't know how to handle
305 # the request pass it to potential VCS handling ap
300 # the request pass it to potential VCS handling ap
306 config.add_notfound_view(not_found_view)
301 config.add_notfound_view(not_found_view)
307 if not settings.get('debugtoolbar.enabled', False):
302 if not settings.get('debugtoolbar.enabled', False):
308 # disabled debugtoolbar handle all exceptions via the error_handlers
303 # disabled debugtoolbar handle all exceptions via the error_handlers
309 config.add_view(error_handler, context=Exception)
304 config.add_view(error_handler, context=Exception)
310
305
311 # all errors including 403/404/50X
306 # all errors including 403/404/50X
312 config.add_view(error_handler, context=HTTPError)
307 config.add_view(error_handler, context=HTTPError)
313
308
314
309
315 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
310 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
316 """
311 """
317 Apply outer WSGI middlewares around the application.
312 Apply outer WSGI middlewares around the application.
318 """
313 """
319 settings = config.registry.settings
314 registry = config.registry
315 settings = registry.settings
320
316
321 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
317 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
322 pyramid_app = HttpsFixup(pyramid_app, settings)
318 pyramid_app = HttpsFixup(pyramid_app, settings)
323
319
324 pyramid_app, _ae_client = wrap_in_appenlight_if_enabled(
320 pyramid_app, _ae_client = wrap_in_appenlight_if_enabled(
325 pyramid_app, settings)
321 pyramid_app, settings)
326 config.registry.ae_client = _ae_client
322 registry.ae_client = _ae_client
327
323
328 if settings['gzip_responses']:
324 if settings['gzip_responses']:
329 pyramid_app = make_gzip_middleware(
325 pyramid_app = make_gzip_middleware(
330 pyramid_app, settings, compress_level=1)
326 pyramid_app, settings, compress_level=1)
331
327
332 # this should be the outer most middleware in the wsgi stack since
328 # this should be the outer most middleware in the wsgi stack since
333 # middleware like Routes make database calls
329 # middleware like Routes make database calls
334 def pyramid_app_with_cleanup(environ, start_response):
330 def pyramid_app_with_cleanup(environ, start_response):
335 try:
331 try:
336 return pyramid_app(environ, start_response)
332 return pyramid_app(environ, start_response)
337 finally:
333 finally:
338 # Dispose current database session and rollback uncommitted
334 # Dispose current database session and rollback uncommitted
339 # transactions.
335 # transactions.
340 meta.Session.remove()
336 meta.Session.remove()
341
337
342 # In a single threaded mode server, on non sqlite db we should have
338 # In a single threaded mode server, on non sqlite db we should have
343 # '0 Current Checked out connections' at the end of a request,
339 # '0 Current Checked out connections' at the end of a request,
344 # if not, then something, somewhere is leaving a connection open
340 # if not, then something, somewhere is leaving a connection open
345 pool = meta.Base.metadata.bind.engine.pool
341 pool = meta.Base.metadata.bind.engine.pool
346 log.debug('sa pool status: %s', pool.status())
342 log.debug('sa pool status: %s', pool.status())
347
343
348 return pyramid_app_with_cleanup
344 return pyramid_app_with_cleanup
349
345
350
346
351 def sanitize_settings_and_apply_defaults(settings):
347 def sanitize_settings_and_apply_defaults(settings):
352 """
348 """
353 Applies settings defaults and does all type conversion.
349 Applies settings defaults and does all type conversion.
354
350
355 We would move all settings parsing and preparation into this place, so that
351 We would move all settings parsing and preparation into this place, so that
356 we have only one place left which deals with this part. The remaining parts
352 we have only one place left which deals with this part. The remaining parts
357 of the application would start to rely fully on well prepared settings.
353 of the application would start to rely fully on well prepared settings.
358
354
359 This piece would later be split up per topic to avoid a big fat monster
355 This piece would later be split up per topic to avoid a big fat monster
360 function.
356 function.
361 """
357 """
362
358
363 settings.setdefault('rhodecode.edition', 'Community Edition')
359 settings.setdefault('rhodecode.edition', 'Community Edition')
364
360
365 if 'mako.default_filters' not in settings:
361 if 'mako.default_filters' not in settings:
366 # set custom default filters if we don't have it defined
362 # set custom default filters if we don't have it defined
367 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
363 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
368 settings['mako.default_filters'] = 'h_filter'
364 settings['mako.default_filters'] = 'h_filter'
369
365
370 if 'mako.directories' not in settings:
366 if 'mako.directories' not in settings:
371 mako_directories = settings.setdefault('mako.directories', [
367 mako_directories = settings.setdefault('mako.directories', [
372 # Base templates of the original application
368 # Base templates of the original application
373 'rhodecode:templates',
369 'rhodecode:templates',
374 ])
370 ])
375 log.debug(
371 log.debug(
376 "Using the following Mako template directories: %s",
372 "Using the following Mako template directories: %s",
377 mako_directories)
373 mako_directories)
378
374
379 # Default includes, possible to change as a user
375 # Default includes, possible to change as a user
380 pyramid_includes = settings.setdefault('pyramid.includes', [
376 pyramid_includes = settings.setdefault('pyramid.includes', [
381 'rhodecode.lib.middleware.request_wrapper',
377 'rhodecode.lib.middleware.request_wrapper',
382 ])
378 ])
383 log.debug(
379 log.debug(
384 "Using the following pyramid.includes: %s",
380 "Using the following pyramid.includes: %s",
385 pyramid_includes)
381 pyramid_includes)
386
382
387 # TODO: johbo: Re-think this, usually the call to config.include
383 # TODO: johbo: Re-think this, usually the call to config.include
388 # should allow to pass in a prefix.
384 # should allow to pass in a prefix.
389 settings.setdefault('rhodecode.api.url', '/_admin/api')
385 settings.setdefault('rhodecode.api.url', '/_admin/api')
390
386
391 # Sanitize generic settings.
387 # Sanitize generic settings.
392 _list_setting(settings, 'default_encoding', 'UTF-8')
388 _list_setting(settings, 'default_encoding', 'UTF-8')
393 _bool_setting(settings, 'is_test', 'false')
389 _bool_setting(settings, 'is_test', 'false')
394 _bool_setting(settings, 'gzip_responses', 'false')
390 _bool_setting(settings, 'gzip_responses', 'false')
395
391
396 # Call split out functions that sanitize settings for each topic.
392 # Call split out functions that sanitize settings for each topic.
397 _sanitize_appenlight_settings(settings)
393 _sanitize_appenlight_settings(settings)
398 _sanitize_vcs_settings(settings)
394 _sanitize_vcs_settings(settings)
399 _sanitize_cache_settings(settings)
395 _sanitize_cache_settings(settings)
400
396
401 # configure instance id
397 # configure instance id
402 config_utils.set_instance_id(settings)
398 config_utils.set_instance_id(settings)
403
399
404 return settings
400 return settings
405
401
406
402
407 def _sanitize_appenlight_settings(settings):
403 def _sanitize_appenlight_settings(settings):
408 _bool_setting(settings, 'appenlight', 'false')
404 _bool_setting(settings, 'appenlight', 'false')
409
405
410
406
411 def _sanitize_vcs_settings(settings):
407 def _sanitize_vcs_settings(settings):
412 """
408 """
413 Applies settings defaults and does type conversion for all VCS related
409 Applies settings defaults and does type conversion for all VCS related
414 settings.
410 settings.
415 """
411 """
416 _string_setting(settings, 'vcs.svn.compatible_version', '')
412 _string_setting(settings, 'vcs.svn.compatible_version', '')
417 _string_setting(settings, 'git_rev_filter', '--all')
413 _string_setting(settings, 'git_rev_filter', '--all')
418 _string_setting(settings, 'vcs.hooks.protocol', 'http')
414 _string_setting(settings, 'vcs.hooks.protocol', 'http')
419 _string_setting(settings, 'vcs.hooks.host', '127.0.0.1')
415 _string_setting(settings, 'vcs.hooks.host', '127.0.0.1')
420 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
416 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
421 _string_setting(settings, 'vcs.server', '')
417 _string_setting(settings, 'vcs.server', '')
422 _string_setting(settings, 'vcs.server.log_level', 'debug')
418 _string_setting(settings, 'vcs.server.log_level', 'debug')
423 _string_setting(settings, 'vcs.server.protocol', 'http')
419 _string_setting(settings, 'vcs.server.protocol', 'http')
424 _bool_setting(settings, 'startup.import_repos', 'false')
420 _bool_setting(settings, 'startup.import_repos', 'false')
425 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
421 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
426 _bool_setting(settings, 'vcs.server.enable', 'true')
422 _bool_setting(settings, 'vcs.server.enable', 'true')
427 _bool_setting(settings, 'vcs.start_server', 'false')
423 _bool_setting(settings, 'vcs.start_server', 'false')
428 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
424 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
429 _int_setting(settings, 'vcs.connection_timeout', 3600)
425 _int_setting(settings, 'vcs.connection_timeout', 3600)
430
426
431 # Support legacy values of vcs.scm_app_implementation. Legacy
427 # Support legacy values of vcs.scm_app_implementation. Legacy
432 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http'
428 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http'
433 # which is now mapped to 'http'.
429 # which is now mapped to 'http'.
434 scm_app_impl = settings['vcs.scm_app_implementation']
430 scm_app_impl = settings['vcs.scm_app_implementation']
435 if scm_app_impl == 'rhodecode.lib.middleware.utils.scm_app_http':
431 if scm_app_impl == 'rhodecode.lib.middleware.utils.scm_app_http':
436 settings['vcs.scm_app_implementation'] = 'http'
432 settings['vcs.scm_app_implementation'] = 'http'
437
433
438
434
439 def _sanitize_cache_settings(settings):
435 def _sanitize_cache_settings(settings):
440 _string_setting(settings, 'cache_dir',
436 _string_setting(settings, 'cache_dir',
441 os.path.join(tempfile.gettempdir(), 'rc_cache'))
437 os.path.join(tempfile.gettempdir(), 'rc_cache'))
442 # cache_perms
438 # cache_perms
443 _string_setting(
439 _string_setting(
444 settings,
440 settings,
445 'rc_cache.cache_perms.backend',
441 'rc_cache.cache_perms.backend',
446 'dogpile.cache.rc.file_namespace')
442 'dogpile.cache.rc.file_namespace')
447 _int_setting(
443 _int_setting(
448 settings,
444 settings,
449 'rc_cache.cache_perms.expiration_time',
445 'rc_cache.cache_perms.expiration_time',
450 60)
446 60)
451 _string_setting(
447 _string_setting(
452 settings,
448 settings,
453 'rc_cache.cache_perms.arguments.filename',
449 'rc_cache.cache_perms.arguments.filename',
454 os.path.join(tempfile.gettempdir(), 'rc_cache_1'))
450 os.path.join(tempfile.gettempdir(), 'rc_cache_1'))
455
451
456 # cache_repo
452 # cache_repo
457 _string_setting(
453 _string_setting(
458 settings,
454 settings,
459 'rc_cache.cache_repo.backend',
455 'rc_cache.cache_repo.backend',
460 'dogpile.cache.rc.file_namespace')
456 'dogpile.cache.rc.file_namespace')
461 _int_setting(
457 _int_setting(
462 settings,
458 settings,
463 'rc_cache.cache_repo.expiration_time',
459 'rc_cache.cache_repo.expiration_time',
464 60)
460 60)
465 _string_setting(
461 _string_setting(
466 settings,
462 settings,
467 'rc_cache.cache_repo.arguments.filename',
463 'rc_cache.cache_repo.arguments.filename',
468 os.path.join(tempfile.gettempdir(), 'rc_cache_2'))
464 os.path.join(tempfile.gettempdir(), 'rc_cache_2'))
469
465
470 # sql_cache_short
466 # sql_cache_short
471 _string_setting(
467 _string_setting(
472 settings,
468 settings,
473 'rc_cache.sql_cache_short.backend',
469 'rc_cache.sql_cache_short.backend',
474 'dogpile.cache.rc.memory_lru')
470 'dogpile.cache.rc.memory_lru')
475 _int_setting(
471 _int_setting(
476 settings,
472 settings,
477 'rc_cache.sql_cache_short.expiration_time',
473 'rc_cache.sql_cache_short.expiration_time',
478 30)
474 30)
479 _int_setting(
475 _int_setting(
480 settings,
476 settings,
481 'rc_cache.sql_cache_short.max_size',
477 'rc_cache.sql_cache_short.max_size',
482 10000)
478 10000)
483
479
484
480
485 def _int_setting(settings, name, default):
481 def _int_setting(settings, name, default):
486 settings[name] = int(settings.get(name, default))
482 settings[name] = int(settings.get(name, default))
487
483
488
484
489 def _bool_setting(settings, name, default):
485 def _bool_setting(settings, name, default):
490 input_val = settings.get(name, default)
486 input_val = settings.get(name, default)
491 if isinstance(input_val, unicode):
487 if isinstance(input_val, unicode):
492 input_val = input_val.encode('utf8')
488 input_val = input_val.encode('utf8')
493 settings[name] = asbool(input_val)
489 settings[name] = asbool(input_val)
494
490
495
491
496 def _list_setting(settings, name, default):
492 def _list_setting(settings, name, default):
497 raw_value = settings.get(name, default)
493 raw_value = settings.get(name, default)
498
494
499 old_separator = ','
495 old_separator = ','
500 if old_separator in raw_value:
496 if old_separator in raw_value:
501 # If we get a comma separated list, pass it to our own function.
497 # If we get a comma separated list, pass it to our own function.
502 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
498 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
503 else:
499 else:
504 # Otherwise we assume it uses pyramids space/newline separation.
500 # Otherwise we assume it uses pyramids space/newline separation.
505 settings[name] = aslist(raw_value)
501 settings[name] = aslist(raw_value)
506
502
507
503
508 def _string_setting(settings, name, default, lower=True):
504 def _string_setting(settings, name, default, lower=True):
509 value = settings.get(name, default)
505 value = settings.get(name, default)
510 if lower:
506 if lower:
511 value = value.lower()
507 value = value.lower()
512 settings[name] = value
508 settings[name] = value
513
509
514
510
515 def _substitute_values(mapping, substitutions):
511 def _substitute_values(mapping, substitutions):
516 result = {
512 result = {
517 # Note: Cannot use regular replacements, since they would clash
513 # Note: Cannot use regular replacements, since they would clash
518 # with the implementation of ConfigParser. Using "format" instead.
514 # with the implementation of ConfigParser. Using "format" instead.
519 key: value.format(**substitutions)
515 key: value.format(**substitutions)
520 for key, value in mapping.items()
516 for key, value in mapping.items()
521 }
517 }
522 return result
518 return result
@@ -1,84 +1,76 b''
1 # Copyright (C) 2016-2018 RhodeCode GmbH
1 # Copyright (C) 2016-2018 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
5 # (only), as published by the Free Software Foundation.
5 # (only), as published by the Free Software Foundation.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU Affero General Public License
12 # You should have received a copy of the GNU Affero General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 #
14 #
15 # This program is dual-licensed. If you wish to learn more about the
15 # This program is dual-licensed. If you wish to learn more about the
16 # RhodeCode Enterprise Edition, including its added features, Support services,
16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 import logging
19 import logging
20 from pyramid.threadlocal import get_current_registry
20 from pyramid.threadlocal import get_current_registry
21 from rhodecode.events.base import RhodecodeEvent
21 from rhodecode.events.base import RhodeCodeIntegrationEvent
22
22
23
23
24 log = logging.getLogger(__name__)
24 log = logging.getLogger(__name__)
25
25
26
26
27 def trigger(event, registry=None):
27 def trigger(event, registry=None):
28 """
28 """
29 Helper method to send an event. This wraps the pyramid logic to send an
29 Helper method to send an event. This wraps the pyramid logic to send an
30 event.
30 event.
31 """
31 """
32 # For the first step we are using pyramids thread locals here. If the
32 # For the first step we are using pyramids thread locals here. If the
33 # event mechanism works out as a good solution we should think about
33 # event mechanism works out as a good solution we should think about
34 # passing the registry as an argument to get rid of it.
34 # passing the registry as an argument to get rid of it.
35 registry = registry or get_current_registry()
35 registry = registry or get_current_registry()
36 registry.notify(event)
36 registry.notify(event)
37 log.debug('event %s triggered using registry %s', event.__class__, registry)
37 log.debug('event %s triggered using registry %s', event.__class__, registry)
38
38
39 # Until we can work around the problem that VCS operations do not have a
39 # Send the events to integrations directly
40 # pyramid context to work with, we send the events to integrations directly
41
42 # Later it will be possible to use regular pyramid subscribers ie:
43 # config.add_subscriber(
44 # 'rhodecode.integrations.integrations_event_handler',
45 # 'rhodecode.events.RhodecodeEvent')
46 # trigger(event, request.registry)
47
48 from rhodecode.integrations import integrations_event_handler
40 from rhodecode.integrations import integrations_event_handler
49 if isinstance(event, RhodecodeEvent):
41 if isinstance(event, RhodeCodeIntegrationEvent):
50 integrations_event_handler(event)
42 integrations_event_handler(event)
51
43
52
44
53 from rhodecode.events.user import ( # noqa
45 from rhodecode.events.user import ( # noqa
54 UserPreCreate,
46 UserPreCreate,
55 UserPostCreate,
47 UserPostCreate,
56 UserPreUpdate,
48 UserPreUpdate,
57 UserRegistered,
49 UserRegistered,
58 UserPermissionsChange,
50 UserPermissionsChange,
59 )
51 )
60
52
61 from rhodecode.events.repo import ( # noqa
53 from rhodecode.events.repo import ( # noqa
62 RepoEvent,
54 RepoEvent,
63 RepoPreCreateEvent, RepoCreateEvent,
55 RepoPreCreateEvent, RepoCreateEvent,
64 RepoPreDeleteEvent, RepoDeleteEvent,
56 RepoPreDeleteEvent, RepoDeleteEvent,
65 RepoPrePushEvent, RepoPushEvent,
57 RepoPrePushEvent, RepoPushEvent,
66 RepoPrePullEvent, RepoPullEvent,
58 RepoPrePullEvent, RepoPullEvent,
67 )
59 )
68
60
69 from rhodecode.events.repo_group import ( # noqa
61 from rhodecode.events.repo_group import ( # noqa
70 RepoGroupEvent,
62 RepoGroupEvent,
71 RepoGroupCreateEvent,
63 RepoGroupCreateEvent,
72 RepoGroupUpdateEvent,
64 RepoGroupUpdateEvent,
73 RepoGroupDeleteEvent,
65 RepoGroupDeleteEvent,
74 )
66 )
75
67
76 from rhodecode.events.pullrequest import ( # noqa
68 from rhodecode.events.pullrequest import ( # noqa
77 PullRequestEvent,
69 PullRequestEvent,
78 PullRequestCreateEvent,
70 PullRequestCreateEvent,
79 PullRequestUpdateEvent,
71 PullRequestUpdateEvent,
80 PullRequestCommentEvent,
72 PullRequestCommentEvent,
81 PullRequestReviewEvent,
73 PullRequestReviewEvent,
82 PullRequestMergeEvent,
74 PullRequestMergeEvent,
83 PullRequestCloseEvent,
75 PullRequestCloseEvent,
84 )
76 )
@@ -1,112 +1,118 b''
1 # Copyright (C) 2016-2018 RhodeCode GmbH
1 # Copyright (C) 2016-2018 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
5 # (only), as published by the Free Software Foundation.
5 # (only), as published by the Free Software Foundation.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU Affero General Public License
12 # You should have received a copy of the GNU Affero General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 #
14 #
15 # This program is dual-licensed. If you wish to learn more about the
15 # This program is dual-licensed. If you wish to learn more about the
16 # RhodeCode Enterprise Edition, including its added features, Support services,
16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 import logging
18 import logging
19 import datetime
19 import datetime
20
20
21 from zope.cachedescriptors.property import Lazy as LazyProperty
21 from zope.cachedescriptors.property import Lazy as LazyProperty
22 from pyramid.threadlocal import get_current_request
22 from pyramid.threadlocal import get_current_request
23
23
24 from rhodecode.lib.utils2 import AttributeDict
24 from rhodecode.lib.utils2 import AttributeDict
25
25
26
26
27 # this is a user object to be used for events caused by the system (eg. shell)
27 # this is a user object to be used for events caused by the system (eg. shell)
28 SYSTEM_USER = AttributeDict(dict(
28 SYSTEM_USER = AttributeDict(dict(
29 username='__SYSTEM__',
29 username='__SYSTEM__',
30 user_id='__SYSTEM_ID__'
30 user_id='__SYSTEM_ID__'
31 ))
31 ))
32
32
33 log = logging.getLogger(__name__)
33 log = logging.getLogger(__name__)
34
34
35
35
36 class RhodecodeEvent(object):
36 class RhodecodeEvent(object):
37 """
37 """
38 Base event class for all RhodeCode events
38 Base event class for all RhodeCode events
39 """
39 """
40 name = "RhodeCodeEvent"
40 name = "RhodeCodeEvent"
41 no_url_set = '<no server_url available>'
41 no_url_set = '<no server_url available>'
42
42
43 def __init__(self, request=None):
43 def __init__(self, request=None):
44 self._request = request
44 self._request = request
45 self.utc_timestamp = datetime.datetime.utcnow()
45 self.utc_timestamp = datetime.datetime.utcnow()
46
46
47 def get_request(self):
47 def get_request(self):
48 if self._request:
48 if self._request:
49 return self._request
49 return self._request
50 return get_current_request()
50 return get_current_request()
51
51
52 @LazyProperty
52 @LazyProperty
53 def request(self):
53 def request(self):
54 return self.get_request()
54 return self.get_request()
55
55
56 @property
56 @property
57 def auth_user(self):
57 def auth_user(self):
58 if not self.request:
58 if not self.request:
59 return
59 return
60
60
61 user = getattr(self.request, 'user', None)
61 user = getattr(self.request, 'user', None)
62 if user:
62 if user:
63 return user
63 return user
64
64
65 api_user = getattr(self.request, 'rpc_user', None)
65 api_user = getattr(self.request, 'rpc_user', None)
66 if api_user:
66 if api_user:
67 return api_user
67 return api_user
68
68
69 @property
69 @property
70 def actor(self):
70 def actor(self):
71 auth_user = self.auth_user
71 auth_user = self.auth_user
72 if auth_user:
72 if auth_user:
73 instance = auth_user.get_instance()
73 instance = auth_user.get_instance()
74 if not instance:
74 if not instance:
75 return AttributeDict(dict(
75 return AttributeDict(dict(
76 username=auth_user.username,
76 username=auth_user.username,
77 user_id=auth_user.user_id,
77 user_id=auth_user.user_id,
78 ))
78 ))
79 return instance
79 return instance
80
80
81 return SYSTEM_USER
81 return SYSTEM_USER
82
82
83 @property
83 @property
84 def actor_ip(self):
84 def actor_ip(self):
85 auth_user = self.auth_user
85 auth_user = self.auth_user
86 if auth_user:
86 if auth_user:
87 return auth_user.ip_addr
87 return auth_user.ip_addr
88 return '<no ip available>'
88 return '<no ip available>'
89
89
90 @property
90 @property
91 def server_url(self):
91 def server_url(self):
92 if self.request:
92 if self.request:
93 try:
93 try:
94 return self.request.route_url('home')
94 return self.request.route_url('home')
95 except Exception:
95 except Exception:
96 log.exception('Failed to fetch URL for server')
96 log.exception('Failed to fetch URL for server')
97 return self.no_url_set
97 return self.no_url_set
98
98
99 return self.no_url_set
99 return self.no_url_set
100
100
101 def as_dict(self):
101 def as_dict(self):
102 data = {
102 data = {
103 'name': self.name,
103 'name': self.name,
104 'utc_timestamp': self.utc_timestamp,
104 'utc_timestamp': self.utc_timestamp,
105 'actor_ip': self.actor_ip,
105 'actor_ip': self.actor_ip,
106 'actor': {
106 'actor': {
107 'username': self.actor.username,
107 'username': self.actor.username,
108 'user_id': self.actor.user_id
108 'user_id': self.actor.user_id
109 },
109 },
110 'server_url': self.server_url
110 'server_url': self.server_url
111 }
111 }
112 return data
112 return data
113
114
115 class RhodeCodeIntegrationEvent(RhodecodeEvent):
116 """
117 Special subclass for Integration events
118 """
@@ -1,356 +1,356 b''
1 # Copyright (C) 2016-2018 RhodeCode GmbH
1 # Copyright (C) 2016-2018 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
5 # (only), as published by the Free Software Foundation.
5 # (only), as published by the Free Software Foundation.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU Affero General Public License
12 # You should have received a copy of the GNU Affero General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 #
14 #
15 # This program is dual-licensed. If you wish to learn more about the
15 # This program is dual-licensed. If you wish to learn more about the
16 # RhodeCode Enterprise Edition, including its added features, Support services,
16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 import collections
19 import collections
20 import logging
20 import logging
21 import datetime
21 import datetime
22
22
23 from rhodecode.translation import lazy_ugettext
23 from rhodecode.translation import lazy_ugettext
24 from rhodecode.model.db import User, Repository, Session
24 from rhodecode.model.db import User, Repository, Session
25 from rhodecode.events.base import RhodecodeEvent
25 from rhodecode.events.base import RhodeCodeIntegrationEvent
26 from rhodecode.lib.vcs.exceptions import CommitDoesNotExistError
26 from rhodecode.lib.vcs.exceptions import CommitDoesNotExistError
27
27
28 log = logging.getLogger(__name__)
28 log = logging.getLogger(__name__)
29
29
30
30
31 def _commits_as_dict(event, commit_ids, repos):
31 def _commits_as_dict(event, commit_ids, repos):
32 """
32 """
33 Helper function to serialize commit_ids
33 Helper function to serialize commit_ids
34
34
35 :param event: class calling this method
35 :param event: class calling this method
36 :param commit_ids: commits to get
36 :param commit_ids: commits to get
37 :param repos: list of repos to check
37 :param repos: list of repos to check
38 """
38 """
39 from rhodecode.lib.utils2 import extract_mentioned_users
39 from rhodecode.lib.utils2 import extract_mentioned_users
40 from rhodecode.lib.helpers import (
40 from rhodecode.lib.helpers import (
41 urlify_commit_message, process_patterns, chop_at_smart)
41 urlify_commit_message, process_patterns, chop_at_smart)
42 from rhodecode.model.repo import RepoModel
42 from rhodecode.model.repo import RepoModel
43
43
44 if not repos:
44 if not repos:
45 raise Exception('no repo defined')
45 raise Exception('no repo defined')
46
46
47 if not isinstance(repos, (tuple, list)):
47 if not isinstance(repos, (tuple, list)):
48 repos = [repos]
48 repos = [repos]
49
49
50 if not commit_ids:
50 if not commit_ids:
51 return []
51 return []
52
52
53 needed_commits = list(commit_ids)
53 needed_commits = list(commit_ids)
54
54
55 commits = []
55 commits = []
56 reviewers = []
56 reviewers = []
57 for repo in repos:
57 for repo in repos:
58 if not needed_commits:
58 if not needed_commits:
59 return commits # return early if we have the commits we need
59 return commits # return early if we have the commits we need
60
60
61 vcs_repo = repo.scm_instance(cache=False)
61 vcs_repo = repo.scm_instance(cache=False)
62
62
63 try:
63 try:
64 # use copy of needed_commits since we modify it while iterating
64 # use copy of needed_commits since we modify it while iterating
65 for commit_id in list(needed_commits):
65 for commit_id in list(needed_commits):
66 if commit_id.startswith('tag=>'):
66 if commit_id.startswith('tag=>'):
67 raw_id = commit_id[5:]
67 raw_id = commit_id[5:]
68 cs_data = {
68 cs_data = {
69 'raw_id': commit_id, 'short_id': commit_id,
69 'raw_id': commit_id, 'short_id': commit_id,
70 'branch': None,
70 'branch': None,
71 'git_ref_change': 'tag_add',
71 'git_ref_change': 'tag_add',
72 'message': 'Added new tag {}'.format(raw_id),
72 'message': 'Added new tag {}'.format(raw_id),
73 'author': event.actor.full_contact,
73 'author': event.actor.full_contact,
74 'date': datetime.datetime.now(),
74 'date': datetime.datetime.now(),
75 'refs': {
75 'refs': {
76 'branches': [],
76 'branches': [],
77 'bookmarks': [],
77 'bookmarks': [],
78 'tags': []
78 'tags': []
79 }
79 }
80 }
80 }
81 commits.append(cs_data)
81 commits.append(cs_data)
82
82
83 elif commit_id.startswith('delete_branch=>'):
83 elif commit_id.startswith('delete_branch=>'):
84 raw_id = commit_id[15:]
84 raw_id = commit_id[15:]
85 cs_data = {
85 cs_data = {
86 'raw_id': commit_id, 'short_id': commit_id,
86 'raw_id': commit_id, 'short_id': commit_id,
87 'branch': None,
87 'branch': None,
88 'git_ref_change': 'branch_delete',
88 'git_ref_change': 'branch_delete',
89 'message': 'Deleted branch {}'.format(raw_id),
89 'message': 'Deleted branch {}'.format(raw_id),
90 'author': event.actor.full_contact,
90 'author': event.actor.full_contact,
91 'date': datetime.datetime.now(),
91 'date': datetime.datetime.now(),
92 'refs': {
92 'refs': {
93 'branches': [],
93 'branches': [],
94 'bookmarks': [],
94 'bookmarks': [],
95 'tags': []
95 'tags': []
96 }
96 }
97 }
97 }
98 commits.append(cs_data)
98 commits.append(cs_data)
99
99
100 else:
100 else:
101 try:
101 try:
102 cs = vcs_repo.get_changeset(commit_id)
102 cs = vcs_repo.get_changeset(commit_id)
103 except CommitDoesNotExistError:
103 except CommitDoesNotExistError:
104 continue # maybe its in next repo
104 continue # maybe its in next repo
105
105
106 cs_data = cs.__json__()
106 cs_data = cs.__json__()
107 cs_data['refs'] = cs._get_refs()
107 cs_data['refs'] = cs._get_refs()
108
108
109 cs_data['mentions'] = extract_mentioned_users(cs_data['message'])
109 cs_data['mentions'] = extract_mentioned_users(cs_data['message'])
110 cs_data['reviewers'] = reviewers
110 cs_data['reviewers'] = reviewers
111 cs_data['url'] = RepoModel().get_commit_url(
111 cs_data['url'] = RepoModel().get_commit_url(
112 repo, cs_data['raw_id'], request=event.request)
112 repo, cs_data['raw_id'], request=event.request)
113 cs_data['permalink_url'] = RepoModel().get_commit_url(
113 cs_data['permalink_url'] = RepoModel().get_commit_url(
114 repo, cs_data['raw_id'], request=event.request,
114 repo, cs_data['raw_id'], request=event.request,
115 permalink=True)
115 permalink=True)
116 urlified_message, issues_data = process_patterns(
116 urlified_message, issues_data = process_patterns(
117 cs_data['message'], repo.repo_name)
117 cs_data['message'], repo.repo_name)
118 cs_data['issues'] = issues_data
118 cs_data['issues'] = issues_data
119 cs_data['message_html'] = urlify_commit_message(
119 cs_data['message_html'] = urlify_commit_message(
120 cs_data['message'], repo.repo_name)
120 cs_data['message'], repo.repo_name)
121 cs_data['message_html_title'] = chop_at_smart(
121 cs_data['message_html_title'] = chop_at_smart(
122 cs_data['message'], '\n', suffix_if_chopped='...')
122 cs_data['message'], '\n', suffix_if_chopped='...')
123 commits.append(cs_data)
123 commits.append(cs_data)
124
124
125 needed_commits.remove(commit_id)
125 needed_commits.remove(commit_id)
126
126
127 except Exception:
127 except Exception:
128 log.exception('Failed to extract commits data')
128 log.exception('Failed to extract commits data')
129 # we don't send any commits when crash happens, only full list
129 # we don't send any commits when crash happens, only full list
130 # matters we short circuit then.
130 # matters we short circuit then.
131 return []
131 return []
132
132
133 missing_commits = set(commit_ids) - set(c['raw_id'] for c in commits)
133 missing_commits = set(commit_ids) - set(c['raw_id'] for c in commits)
134 if missing_commits:
134 if missing_commits:
135 log.error('Inconsistent repository state. '
135 log.error('Inconsistent repository state. '
136 'Missing commits: %s' % ', '.join(missing_commits))
136 'Missing commits: %s' % ', '.join(missing_commits))
137
137
138 return commits
138 return commits
139
139
140
140
141 def _issues_as_dict(commits):
141 def _issues_as_dict(commits):
142 """ Helper function to serialize issues from commits """
142 """ Helper function to serialize issues from commits """
143 issues = {}
143 issues = {}
144 for commit in commits:
144 for commit in commits:
145 for issue in commit['issues']:
145 for issue in commit['issues']:
146 issues[issue['id']] = issue
146 issues[issue['id']] = issue
147 return issues
147 return issues
148
148
149
149
150 class RepoEvent(RhodecodeEvent):
150 class RepoEvent(RhodeCodeIntegrationEvent):
151 """
151 """
152 Base class for events acting on a repository.
152 Base class for events acting on a repository.
153
153
154 :param repo: a :class:`Repository` instance
154 :param repo: a :class:`Repository` instance
155 """
155 """
156
156
157 def __init__(self, repo):
157 def __init__(self, repo):
158 super(RepoEvent, self).__init__()
158 super(RepoEvent, self).__init__()
159 self.repo = repo
159 self.repo = repo
160
160
161 def as_dict(self):
161 def as_dict(self):
162 from rhodecode.model.repo import RepoModel
162 from rhodecode.model.repo import RepoModel
163 data = super(RepoEvent, self).as_dict()
163 data = super(RepoEvent, self).as_dict()
164
164
165 extra_fields = collections.OrderedDict()
165 extra_fields = collections.OrderedDict()
166 for field in self.repo.extra_fields:
166 for field in self.repo.extra_fields:
167 extra_fields[field.field_key] = field.field_value
167 extra_fields[field.field_key] = field.field_value
168
168
169 data.update({
169 data.update({
170 'repo': {
170 'repo': {
171 'repo_id': self.repo.repo_id,
171 'repo_id': self.repo.repo_id,
172 'repo_name': self.repo.repo_name,
172 'repo_name': self.repo.repo_name,
173 'repo_type': self.repo.repo_type,
173 'repo_type': self.repo.repo_type,
174 'url': RepoModel().get_url(
174 'url': RepoModel().get_url(
175 self.repo, request=self.request),
175 self.repo, request=self.request),
176 'permalink_url': RepoModel().get_url(
176 'permalink_url': RepoModel().get_url(
177 self.repo, request=self.request, permalink=True),
177 self.repo, request=self.request, permalink=True),
178 'extra_fields': extra_fields
178 'extra_fields': extra_fields
179 }
179 }
180 })
180 })
181 return data
181 return data
182
182
183
183
184 class RepoPreCreateEvent(RepoEvent):
184 class RepoPreCreateEvent(RepoEvent):
185 """
185 """
186 An instance of this class is emitted as an :term:`event` before a repo is
186 An instance of this class is emitted as an :term:`event` before a repo is
187 created.
187 created.
188 """
188 """
189 name = 'repo-pre-create'
189 name = 'repo-pre-create'
190 display_name = lazy_ugettext('repository pre create')
190 display_name = lazy_ugettext('repository pre create')
191
191
192
192
193 class RepoCreateEvent(RepoEvent):
193 class RepoCreateEvent(RepoEvent):
194 """
194 """
195 An instance of this class is emitted as an :term:`event` whenever a repo is
195 An instance of this class is emitted as an :term:`event` whenever a repo is
196 created.
196 created.
197 """
197 """
198 name = 'repo-create'
198 name = 'repo-create'
199 display_name = lazy_ugettext('repository created')
199 display_name = lazy_ugettext('repository created')
200
200
201
201
202 class RepoPreDeleteEvent(RepoEvent):
202 class RepoPreDeleteEvent(RepoEvent):
203 """
203 """
204 An instance of this class is emitted as an :term:`event` whenever a repo is
204 An instance of this class is emitted as an :term:`event` whenever a repo is
205 created.
205 created.
206 """
206 """
207 name = 'repo-pre-delete'
207 name = 'repo-pre-delete'
208 display_name = lazy_ugettext('repository pre delete')
208 display_name = lazy_ugettext('repository pre delete')
209
209
210
210
211 class RepoDeleteEvent(RepoEvent):
211 class RepoDeleteEvent(RepoEvent):
212 """
212 """
213 An instance of this class is emitted as an :term:`event` whenever a repo is
213 An instance of this class is emitted as an :term:`event` whenever a repo is
214 created.
214 created.
215 """
215 """
216 name = 'repo-delete'
216 name = 'repo-delete'
217 display_name = lazy_ugettext('repository deleted')
217 display_name = lazy_ugettext('repository deleted')
218
218
219
219
220 class RepoVCSEvent(RepoEvent):
220 class RepoVCSEvent(RepoEvent):
221 """
221 """
222 Base class for events triggered by the VCS
222 Base class for events triggered by the VCS
223 """
223 """
224 def __init__(self, repo_name, extras):
224 def __init__(self, repo_name, extras):
225 self.repo = Repository.get_by_repo_name(repo_name)
225 self.repo = Repository.get_by_repo_name(repo_name)
226 if not self.repo:
226 if not self.repo:
227 raise Exception('repo by this name %s does not exist' % repo_name)
227 raise Exception('repo by this name %s does not exist' % repo_name)
228 self.extras = extras
228 self.extras = extras
229 super(RepoVCSEvent, self).__init__(self.repo)
229 super(RepoVCSEvent, self).__init__(self.repo)
230
230
231 @property
231 @property
232 def actor(self):
232 def actor(self):
233 if self.extras.get('username'):
233 if self.extras.get('username'):
234 return User.get_by_username(self.extras['username'])
234 return User.get_by_username(self.extras['username'])
235
235
236 @property
236 @property
237 def actor_ip(self):
237 def actor_ip(self):
238 if self.extras.get('ip'):
238 if self.extras.get('ip'):
239 return self.extras['ip']
239 return self.extras['ip']
240
240
241 @property
241 @property
242 def server_url(self):
242 def server_url(self):
243 if self.extras.get('server_url'):
243 if self.extras.get('server_url'):
244 return self.extras['server_url']
244 return self.extras['server_url']
245
245
246 @property
246 @property
247 def request(self):
247 def request(self):
248 return self.extras.get('request') or self.get_request()
248 return self.extras.get('request') or self.get_request()
249
249
250
250
251 class RepoPrePullEvent(RepoVCSEvent):
251 class RepoPrePullEvent(RepoVCSEvent):
252 """
252 """
253 An instance of this class is emitted as an :term:`event` before commits
253 An instance of this class is emitted as an :term:`event` before commits
254 are pulled from a repo.
254 are pulled from a repo.
255 """
255 """
256 name = 'repo-pre-pull'
256 name = 'repo-pre-pull'
257 display_name = lazy_ugettext('repository pre pull')
257 display_name = lazy_ugettext('repository pre pull')
258
258
259
259
260 class RepoPullEvent(RepoVCSEvent):
260 class RepoPullEvent(RepoVCSEvent):
261 """
261 """
262 An instance of this class is emitted as an :term:`event` after commits
262 An instance of this class is emitted as an :term:`event` after commits
263 are pulled from a repo.
263 are pulled from a repo.
264 """
264 """
265 name = 'repo-pull'
265 name = 'repo-pull'
266 display_name = lazy_ugettext('repository pull')
266 display_name = lazy_ugettext('repository pull')
267
267
268
268
269 class RepoPrePushEvent(RepoVCSEvent):
269 class RepoPrePushEvent(RepoVCSEvent):
270 """
270 """
271 An instance of this class is emitted as an :term:`event` before commits
271 An instance of this class is emitted as an :term:`event` before commits
272 are pushed to a repo.
272 are pushed to a repo.
273 """
273 """
274 name = 'repo-pre-push'
274 name = 'repo-pre-push'
275 display_name = lazy_ugettext('repository pre push')
275 display_name = lazy_ugettext('repository pre push')
276
276
277
277
278 class RepoPushEvent(RepoVCSEvent):
278 class RepoPushEvent(RepoVCSEvent):
279 """
279 """
280 An instance of this class is emitted as an :term:`event` after commits
280 An instance of this class is emitted as an :term:`event` after commits
281 are pushed to a repo.
281 are pushed to a repo.
282
282
283 :param extras: (optional) dict of data from proxied VCS actions
283 :param extras: (optional) dict of data from proxied VCS actions
284 """
284 """
285 name = 'repo-push'
285 name = 'repo-push'
286 display_name = lazy_ugettext('repository push')
286 display_name = lazy_ugettext('repository push')
287
287
288 def __init__(self, repo_name, pushed_commit_ids, extras):
288 def __init__(self, repo_name, pushed_commit_ids, extras):
289 super(RepoPushEvent, self).__init__(repo_name, extras)
289 super(RepoPushEvent, self).__init__(repo_name, extras)
290 self.pushed_commit_ids = pushed_commit_ids
290 self.pushed_commit_ids = pushed_commit_ids
291 self.new_refs = extras.new_refs
291 self.new_refs = extras.new_refs
292
292
293 def as_dict(self):
293 def as_dict(self):
294 data = super(RepoPushEvent, self).as_dict()
294 data = super(RepoPushEvent, self).as_dict()
295
295
296 def branch_url(branch_name):
296 def branch_url(branch_name):
297 return '{}/changelog?branch={}'.format(
297 return '{}/changelog?branch={}'.format(
298 data['repo']['url'], branch_name)
298 data['repo']['url'], branch_name)
299
299
300 def tag_url(tag_name):
300 def tag_url(tag_name):
301 return '{}/files/{}/'.format(
301 return '{}/files/{}/'.format(
302 data['repo']['url'], tag_name)
302 data['repo']['url'], tag_name)
303
303
304 commits = _commits_as_dict(
304 commits = _commits_as_dict(
305 self, commit_ids=self.pushed_commit_ids, repos=[self.repo])
305 self, commit_ids=self.pushed_commit_ids, repos=[self.repo])
306
306
307 last_branch = None
307 last_branch = None
308 for commit in reversed(commits):
308 for commit in reversed(commits):
309 commit['branch'] = commit['branch'] or last_branch
309 commit['branch'] = commit['branch'] or last_branch
310 last_branch = commit['branch']
310 last_branch = commit['branch']
311 issues = _issues_as_dict(commits)
311 issues = _issues_as_dict(commits)
312
312
313 branches = set()
313 branches = set()
314 tags = set()
314 tags = set()
315 for commit in commits:
315 for commit in commits:
316 if commit['refs']['tags']:
316 if commit['refs']['tags']:
317 for tag in commit['refs']['tags']:
317 for tag in commit['refs']['tags']:
318 tags.add(tag)
318 tags.add(tag)
319 if commit['branch']:
319 if commit['branch']:
320 branches.add(commit['branch'])
320 branches.add(commit['branch'])
321
321
322 # maybe we have branches in new_refs ?
322 # maybe we have branches in new_refs ?
323 try:
323 try:
324 branches = branches.union(set(self.new_refs['branches']))
324 branches = branches.union(set(self.new_refs['branches']))
325 except Exception:
325 except Exception:
326 pass
326 pass
327
327
328 branches = [
328 branches = [
329 {
329 {
330 'name': branch,
330 'name': branch,
331 'url': branch_url(branch)
331 'url': branch_url(branch)
332 }
332 }
333 for branch in branches
333 for branch in branches
334 ]
334 ]
335
335
336 # maybe we have branches in new_refs ?
336 # maybe we have branches in new_refs ?
337 try:
337 try:
338 tags = tags.union(set(self.new_refs['tags']))
338 tags = tags.union(set(self.new_refs['tags']))
339 except Exception:
339 except Exception:
340 pass
340 pass
341
341
342 tags = [
342 tags = [
343 {
343 {
344 'name': tag,
344 'name': tag,
345 'url': tag_url(tag)
345 'url': tag_url(tag)
346 }
346 }
347 for tag in tags
347 for tag in tags
348 ]
348 ]
349
349
350 data['push'] = {
350 data['push'] = {
351 'commits': commits,
351 'commits': commits,
352 'issues': issues,
352 'issues': issues,
353 'branches': branches,
353 'branches': branches,
354 'tags': tags,
354 'tags': tags,
355 }
355 }
356 return data
356 return data
@@ -1,80 +1,80 b''
1 # Copyright (C) 2016-2018 RhodeCode GmbH
1 # Copyright (C) 2016-2018 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
5 # (only), as published by the Free Software Foundation.
5 # (only), as published by the Free Software Foundation.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU Affero General Public License
12 # You should have received a copy of the GNU Affero General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 #
14 #
15 # This program is dual-licensed. If you wish to learn more about the
15 # This program is dual-licensed. If you wish to learn more about the
16 # RhodeCode Enterprise Edition, including its added features, Support services,
16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 import logging
19 import logging
20
20
21 from rhodecode.translation import lazy_ugettext
21 from rhodecode.translation import lazy_ugettext
22 from rhodecode.events.base import RhodecodeEvent
22 from rhodecode.events.base import RhodeCodeIntegrationEvent
23
23
24
24
25 log = logging.getLogger(__name__)
25 log = logging.getLogger(__name__)
26
26
27
27
28 class RepoGroupEvent(RhodecodeEvent):
28 class RepoGroupEvent(RhodeCodeIntegrationEvent):
29 """
29 """
30 Base class for events acting on a repository group.
30 Base class for events acting on a repository group.
31
31
32 :param repo: a :class:`RepositoryGroup` instance
32 :param repo: a :class:`RepositoryGroup` instance
33 """
33 """
34
34
35 def __init__(self, repo_group):
35 def __init__(self, repo_group):
36 super(RepoGroupEvent, self).__init__()
36 super(RepoGroupEvent, self).__init__()
37 self.repo_group = repo_group
37 self.repo_group = repo_group
38
38
39 def as_dict(self):
39 def as_dict(self):
40 data = super(RepoGroupEvent, self).as_dict()
40 data = super(RepoGroupEvent, self).as_dict()
41 data.update({
41 data.update({
42 'repo_group': {
42 'repo_group': {
43 'group_id': self.repo_group.group_id,
43 'group_id': self.repo_group.group_id,
44 'group_name': self.repo_group.group_name,
44 'group_name': self.repo_group.group_name,
45 'group_parent_id': self.repo_group.group_parent_id,
45 'group_parent_id': self.repo_group.group_parent_id,
46 'group_description': self.repo_group.group_description,
46 'group_description': self.repo_group.group_description,
47 'user_id': self.repo_group.user_id,
47 'user_id': self.repo_group.user_id,
48 'created_by': self.repo_group.user.username,
48 'created_by': self.repo_group.user.username,
49 'created_on': self.repo_group.created_on,
49 'created_on': self.repo_group.created_on,
50 'enable_locking': self.repo_group.enable_locking,
50 'enable_locking': self.repo_group.enable_locking,
51 }
51 }
52 })
52 })
53 return data
53 return data
54
54
55
55
56 class RepoGroupCreateEvent(RepoGroupEvent):
56 class RepoGroupCreateEvent(RepoGroupEvent):
57 """
57 """
58 An instance of this class is emitted as an :term:`event` whenever a
58 An instance of this class is emitted as an :term:`event` whenever a
59 repository group is created.
59 repository group is created.
60 """
60 """
61 name = 'repo-group-create'
61 name = 'repo-group-create'
62 display_name = lazy_ugettext('repository group created')
62 display_name = lazy_ugettext('repository group created')
63
63
64
64
65 class RepoGroupDeleteEvent(RepoGroupEvent):
65 class RepoGroupDeleteEvent(RepoGroupEvent):
66 """
66 """
67 An instance of this class is emitted as an :term:`event` whenever a
67 An instance of this class is emitted as an :term:`event` whenever a
68 repository group is deleted.
68 repository group is deleted.
69 """
69 """
70 name = 'repo-group-delete'
70 name = 'repo-group-delete'
71 display_name = lazy_ugettext('repository group deleted')
71 display_name = lazy_ugettext('repository group deleted')
72
72
73
73
74 class RepoGroupUpdateEvent(RepoGroupEvent):
74 class RepoGroupUpdateEvent(RepoGroupEvent):
75 """
75 """
76 An instance of this class is emitted as an :term:`event` whenever a
76 An instance of this class is emitted as an :term:`event` whenever a
77 repository group is updated.
77 repository group is updated.
78 """
78 """
79 name = 'repo-group-update'
79 name = 'repo-group-update'
80 display_name = lazy_ugettext('repository group update')
80 display_name = lazy_ugettext('repository group update')
@@ -1,104 +1,104 b''
1 # Copyright (C) 2016-2018 RhodeCode GmbH
1 # Copyright (C) 2016-2018 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
5 # (only), as published by the Free Software Foundation.
5 # (only), as published by the Free Software Foundation.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU Affero General Public License
12 # You should have received a copy of the GNU Affero General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 #
14 #
15 # This program is dual-licensed. If you wish to learn more about the
15 # This program is dual-licensed. If you wish to learn more about the
16 # RhodeCode Enterprise Edition, including its added features, Support services,
16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 import logging
18 import logging
19
19
20 from zope.interface import implementer
20 from zope.interface import implementer
21
21
22 from rhodecode.translation import lazy_ugettext
22 from rhodecode.translation import lazy_ugettext
23 from rhodecode.events.base import RhodecodeEvent
23 from rhodecode.events.base import RhodecodeEvent, RhodeCodeIntegrationEvent
24 from rhodecode.events.interfaces import (
24 from rhodecode.events.interfaces import (
25 IUserRegistered, IUserPreCreate, IUserPreUpdate)
25 IUserRegistered, IUserPreCreate, IUserPreUpdate)
26
26
27 log = logging.getLogger(__name__)
27 log = logging.getLogger(__name__)
28
28
29
29
30 @implementer(IUserRegistered)
30 @implementer(IUserRegistered)
31 class UserRegistered(RhodecodeEvent):
31 class UserRegistered(RhodeCodeIntegrationEvent):
32 """
32 """
33 An instance of this class is emitted as an :term:`event` whenever a user
33 An instance of this class is emitted as an :term:`event` whenever a user
34 account is registered.
34 account is registered.
35 """
35 """
36 name = 'user-register'
36 name = 'user-register'
37 display_name = lazy_ugettext('user registered')
37 display_name = lazy_ugettext('user registered')
38
38
39 def __init__(self, user, session):
39 def __init__(self, user, session):
40 super(UserRegistered, self).__init__()
40 super(UserRegistered, self).__init__()
41 self.user = user
41 self.user = user
42 self.session = session
42 self.session = session
43
43
44
44
45 @implementer(IUserPreCreate)
45 @implementer(IUserPreCreate)
46 class UserPreCreate(RhodecodeEvent):
46 class UserPreCreate(RhodeCodeIntegrationEvent):
47 """
47 """
48 An instance of this class is emitted as an :term:`event` before a new user
48 An instance of this class is emitted as an :term:`event` before a new user
49 object is created.
49 object is created.
50 """
50 """
51 name = 'user-pre-create'
51 name = 'user-pre-create'
52 display_name = lazy_ugettext('user pre create')
52 display_name = lazy_ugettext('user pre create')
53
53
54 def __init__(self, user_data):
54 def __init__(self, user_data):
55 super(UserPreCreate, self).__init__()
55 super(UserPreCreate, self).__init__()
56 self.user_data = user_data
56 self.user_data = user_data
57
57
58
58
59 @implementer(IUserPreCreate)
59 @implementer(IUserPreCreate)
60 class UserPostCreate(RhodecodeEvent):
60 class UserPostCreate(RhodeCodeIntegrationEvent):
61 """
61 """
62 An instance of this class is emitted as an :term:`event` after a new user
62 An instance of this class is emitted as an :term:`event` after a new user
63 object is created.
63 object is created.
64 """
64 """
65 name = 'user-post-create'
65 name = 'user-post-create'
66 display_name = lazy_ugettext('user post create')
66 display_name = lazy_ugettext('user post create')
67
67
68 def __init__(self, user_data):
68 def __init__(self, user_data):
69 super(UserPostCreate, self).__init__()
69 super(UserPostCreate, self).__init__()
70 self.user_data = user_data
70 self.user_data = user_data
71
71
72
72
73 @implementer(IUserPreUpdate)
73 @implementer(IUserPreUpdate)
74 class UserPreUpdate(RhodecodeEvent):
74 class UserPreUpdate(RhodeCodeIntegrationEvent):
75 """
75 """
76 An instance of this class is emitted as an :term:`event` before a user
76 An instance of this class is emitted as an :term:`event` before a user
77 object is updated.
77 object is updated.
78 """
78 """
79 name = 'user-pre-update'
79 name = 'user-pre-update'
80 display_name = lazy_ugettext('user pre update')
80 display_name = lazy_ugettext('user pre update')
81
81
82 def __init__(self, user, user_data):
82 def __init__(self, user, user_data):
83 super(UserPreUpdate, self).__init__()
83 super(UserPreUpdate, self).__init__()
84 self.user = user
84 self.user = user
85 self.user_data = user_data
85 self.user_data = user_data
86
86
87
87
88 class UserPermissionsChange(RhodecodeEvent):
88 class UserPermissionsChange(RhodecodeEvent):
89 """
89 """
90 This event should be triggered on an event that permissions of user might changed.
90 This event should be triggered on an event that permissions of user might changed.
91 Currently this should be triggered on:
91 Currently this should be triggered on:
92
92
93 - user added/removed from user group
93 - user added/removed from user group
94 - repo permissions changed
94 - repo permissions changed
95 - repo group permissions changed
95 - repo group permissions changed
96 - user group permissions changed
96 - user group permissions changed
97
97
98 """
98 """
99 name = 'user-permissions-change'
99 name = 'user-permissions-change'
100 display_name = lazy_ugettext('user permissions change')
100 display_name = lazy_ugettext('user permissions change')
101
101
102 def __init__(self, user_ids):
102 def __init__(self, user_ids):
103 super(UserPermissionsChange, self).__init__()
103 super(UserPermissionsChange, self).__init__()
104 self.user_ids = user_ids
104 self.user_ids = user_ids
General Comments 0
You need to be logged in to leave comments. Login now