##// END OF EJS Templates
events: added new fts build event
milka -
r4583:d1df4ac3 stable
parent child Browse files
Show More

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

@@ -1,80 +1,83 b''
1 # Copyright (C) 2016-2020 RhodeCode GmbH
1 # Copyright (C) 2016-2020 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 RhodeCodeIntegrationEvent
21 from rhodecode.events.base import RhodeCodeIntegrationEvent
22
22
23
23 from rhodecode.events.base import ( # pragma: no cover
24 log = logging.getLogger(__name__)
24 FtsBuild
25
25 )
26
27 def trigger(event, registry=None):
28 """
29 Helper method to send an event. This wraps the pyramid logic to send an
30 event.
31 """
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
34 # passing the registry as an argument to get rid of it.
35 event_name = event.__class__
36 log.debug('event %s sent for execution', event_name)
37 registry = registry or get_current_registry()
38 registry.notify(event)
39 log.debug('event %s triggered using registry %s', event_name, registry)
40
41 # Send the events to integrations directly
42 from rhodecode.integrations import integrations_event_handler
43 if isinstance(event, RhodeCodeIntegrationEvent):
44 integrations_event_handler(event)
45
46
26
47 from rhodecode.events.user import ( # pragma: no cover
27 from rhodecode.events.user import ( # pragma: no cover
48 UserPreCreate,
28 UserPreCreate,
49 UserPostCreate,
29 UserPostCreate,
50 UserPreUpdate,
30 UserPreUpdate,
51 UserRegistered,
31 UserRegistered,
52 UserPermissionsChange,
32 UserPermissionsChange,
53 )
33 )
54
34
55 from rhodecode.events.repo import ( # pragma: no cover
35 from rhodecode.events.repo import ( # pragma: no cover
56 RepoEvent,
36 RepoEvent,
57 RepoCommitCommentEvent, RepoCommitCommentEditEvent,
37 RepoCommitCommentEvent, RepoCommitCommentEditEvent,
58 RepoPreCreateEvent, RepoCreateEvent,
38 RepoPreCreateEvent, RepoCreateEvent,
59 RepoPreDeleteEvent, RepoDeleteEvent,
39 RepoPreDeleteEvent, RepoDeleteEvent,
60 RepoPrePushEvent, RepoPushEvent,
40 RepoPrePushEvent, RepoPushEvent,
61 RepoPrePullEvent, RepoPullEvent,
41 RepoPrePullEvent, RepoPullEvent,
62 )
42 )
63
43
64 from rhodecode.events.repo_group import ( # pragma: no cover
44 from rhodecode.events.repo_group import ( # pragma: no cover
65 RepoGroupEvent,
45 RepoGroupEvent,
66 RepoGroupCreateEvent,
46 RepoGroupCreateEvent,
67 RepoGroupUpdateEvent,
47 RepoGroupUpdateEvent,
68 RepoGroupDeleteEvent,
48 RepoGroupDeleteEvent,
69 )
49 )
70
50
71 from rhodecode.events.pullrequest import ( # pragma: no cover
51 from rhodecode.events.pullrequest import ( # pragma: no cover
72 PullRequestEvent,
52 PullRequestEvent,
73 PullRequestCreateEvent,
53 PullRequestCreateEvent,
74 PullRequestUpdateEvent,
54 PullRequestUpdateEvent,
75 PullRequestCommentEvent,
55 PullRequestCommentEvent,
76 PullRequestCommentEditEvent,
56 PullRequestCommentEditEvent,
77 PullRequestReviewEvent,
57 PullRequestReviewEvent,
78 PullRequestMergeEvent,
58 PullRequestMergeEvent,
79 PullRequestCloseEvent,
59 PullRequestCloseEvent,
80 )
60 )
61
62
63 log = logging.getLogger(__name__)
64
65
66 def trigger(event, registry=None):
67 """
68 Helper method to send an event. This wraps the pyramid logic to send an
69 event.
70 """
71 # For the first step we are using pyramids thread locals here. If the
72 # event mechanism works out as a good solution we should think about
73 # passing the registry as an argument to get rid of it.
74 event_name = event.__class__
75 log.debug('event %s sent for execution', event_name)
76 registry = registry or get_current_registry()
77 registry.notify(event)
78 log.debug('event %s triggered using registry %s', event_name, registry)
79
80 # Send the events to integrations directly
81 from rhodecode.integrations import integrations_event_handler
82 if isinstance(event, RhodeCodeIntegrationEvent):
83 integrations_event_handler(event)
@@ -1,122 +1,130 b''
1 # Copyright (C) 2016-2020 RhodeCode GmbH
1 # Copyright (C) 2016-2020 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 __repr__(self):
47 def __repr__(self):
48 return '<%s:(%s)>' % (self.__class__.__name__, self.name)
48 return '<%s:(%s)>' % (self.__class__.__name__, self.name)
49
49
50 def get_request(self):
50 def get_request(self):
51 if self._request:
51 if self._request:
52 return self._request
52 return self._request
53 return get_current_request()
53 return get_current_request()
54
54
55 @LazyProperty
55 @LazyProperty
56 def request(self):
56 def request(self):
57 return self.get_request()
57 return self.get_request()
58
58
59 @property
59 @property
60 def auth_user(self):
60 def auth_user(self):
61 if not self.request:
61 if not self.request:
62 return
62 return
63
63
64 user = getattr(self.request, 'user', None)
64 user = getattr(self.request, 'user', None)
65 if user:
65 if user:
66 return user
66 return user
67
67
68 api_user = getattr(self.request, 'rpc_user', None)
68 api_user = getattr(self.request, 'rpc_user', None)
69 if api_user:
69 if api_user:
70 return api_user
70 return api_user
71
71
72 @property
72 @property
73 def actor(self):
73 def actor(self):
74 auth_user = self.auth_user
74 auth_user = self.auth_user
75 if auth_user:
75 if auth_user:
76 instance = auth_user.get_instance()
76 instance = auth_user.get_instance()
77 if not instance:
77 if not instance:
78 return AttributeDict(dict(
78 return AttributeDict(dict(
79 username=auth_user.username,
79 username=auth_user.username,
80 user_id=auth_user.user_id,
80 user_id=auth_user.user_id,
81 ))
81 ))
82 return instance
82 return instance
83
83
84 return SYSTEM_USER
84 return SYSTEM_USER
85
85
86 @property
86 @property
87 def actor_ip(self):
87 def actor_ip(self):
88 auth_user = self.auth_user
88 auth_user = self.auth_user
89 if auth_user:
89 if auth_user:
90 return auth_user.ip_addr
90 return auth_user.ip_addr
91 return '<no ip available>'
91 return '<no ip available>'
92
92
93 @property
93 @property
94 def server_url(self):
94 def server_url(self):
95 if self.request:
95 if self.request:
96 try:
96 try:
97 return self.request.route_url('home')
97 return self.request.route_url('home')
98 except Exception:
98 except Exception:
99 log.exception('Failed to fetch URL for server')
99 log.exception('Failed to fetch URL for server')
100 return self.no_url_set
100 return self.no_url_set
101
101
102 return self.no_url_set
102 return self.no_url_set
103
103
104 def as_dict(self):
104 def as_dict(self):
105 data = {
105 data = {
106 'name': self.name,
106 'name': self.name,
107 'utc_timestamp': self.utc_timestamp,
107 'utc_timestamp': self.utc_timestamp,
108 'actor_ip': self.actor_ip,
108 'actor_ip': self.actor_ip,
109 'actor': {
109 'actor': {
110 'username': self.actor.username,
110 'username': self.actor.username,
111 'user_id': self.actor.user_id
111 'user_id': self.actor.user_id
112 },
112 },
113 'server_url': self.server_url
113 'server_url': self.server_url
114 }
114 }
115 return data
115 return data
116
116
117
117
118 class RhodeCodeIntegrationEvent(RhodecodeEvent):
118 class RhodeCodeIntegrationEvent(RhodecodeEvent):
119 """
119 """
120 Special subclass for Integration events
120 Special subclass for Integration events
121 """
121 """
122 description = ''
122 description = ''
123
124
125 class FtsBuild(RhodecodeEvent):
126 """
127 This event will be triggered when FTS Build is triggered
128 """
129 name = 'fts-build'
130 display_name = 'Start FTS Build'
@@ -1,379 +1,379 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2020 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 RhodeCode task modules, containing all task that suppose to be run
22 RhodeCode task modules, containing all task that suppose to be run
23 by celery daemon
23 by celery daemon
24 """
24 """
25
25
26 import os
26 import os
27 import time
27 import time
28
28
29 from pyramid import compat
29 from pyramid import compat
30 from pyramid_mailer.mailer import Mailer
30 from pyramid_mailer.mailer import Mailer
31 from pyramid_mailer.message import Message
31 from pyramid_mailer.message import Message
32 from email.utils import formatdate
32 from email.utils import formatdate
33
33
34 import rhodecode
34 import rhodecode
35 from rhodecode.lib import audit_logger
35 from rhodecode.lib import audit_logger
36 from rhodecode.lib.celerylib import get_logger, async_task, RequestContextTask
36 from rhodecode.lib.celerylib import get_logger, async_task, RequestContextTask
37 from rhodecode.lib import hooks_base
37 from rhodecode.lib import hooks_base
38 from rhodecode.lib.utils2 import safe_int, str2bool
38 from rhodecode.lib.utils2 import safe_int, str2bool
39 from rhodecode.model.db import (
39 from rhodecode.model.db import (
40 Session, IntegrityError, true, Repository, RepoGroup, User)
40 Session, IntegrityError, true, Repository, RepoGroup, User)
41
41
42
42
43 @async_task(ignore_result=True, base=RequestContextTask)
43 @async_task(ignore_result=True, base=RequestContextTask)
44 def send_email(recipients, subject, body='', html_body='', email_config=None,
44 def send_email(recipients, subject, body='', html_body='', email_config=None,
45 extra_headers=None):
45 extra_headers=None):
46 """
46 """
47 Sends an email with defined parameters from the .ini files.
47 Sends an email with defined parameters from the .ini files.
48
48
49 :param recipients: list of recipients, it this is empty the defined email
49 :param recipients: list of recipients, it this is empty the defined email
50 address from field 'email_to' is used instead
50 address from field 'email_to' is used instead
51 :param subject: subject of the mail
51 :param subject: subject of the mail
52 :param body: body of the mail
52 :param body: body of the mail
53 :param html_body: html version of body
53 :param html_body: html version of body
54 :param email_config: specify custom configuration for mailer
54 :param email_config: specify custom configuration for mailer
55 :param extra_headers: specify custom headers
55 :param extra_headers: specify custom headers
56 """
56 """
57 log = get_logger(send_email)
57 log = get_logger(send_email)
58
58
59 email_config = email_config or rhodecode.CONFIG
59 email_config = email_config or rhodecode.CONFIG
60
60
61 mail_server = email_config.get('smtp_server') or None
61 mail_server = email_config.get('smtp_server') or None
62 if mail_server is None:
62 if mail_server is None:
63 log.error("SMTP server information missing. Sending email failed. "
63 log.error("SMTP server information missing. Sending email failed. "
64 "Make sure that `smtp_server` variable is configured "
64 "Make sure that `smtp_server` variable is configured "
65 "inside the .ini file")
65 "inside the .ini file")
66 return False
66 return False
67
67
68 subject = "%s %s" % (email_config.get('email_prefix', ''), subject)
68 subject = "%s %s" % (email_config.get('email_prefix', ''), subject)
69
69
70 if recipients:
70 if recipients:
71 if isinstance(recipients, compat.string_types):
71 if isinstance(recipients, compat.string_types):
72 recipients = recipients.split(',')
72 recipients = recipients.split(',')
73 else:
73 else:
74 # if recipients are not defined we send to email_config + all admins
74 # if recipients are not defined we send to email_config + all admins
75 admins = []
75 admins = []
76 for u in User.query().filter(User.admin == true()).all():
76 for u in User.query().filter(User.admin == true()).all():
77 if u.email:
77 if u.email:
78 admins.append(u.email)
78 admins.append(u.email)
79 recipients = []
79 recipients = []
80 config_email = email_config.get('email_to')
80 config_email = email_config.get('email_to')
81 if config_email:
81 if config_email:
82 recipients += [config_email]
82 recipients += [config_email]
83 recipients += admins
83 recipients += admins
84
84
85 # translate our LEGACY config into the one that pyramid_mailer supports
85 # translate our LEGACY config into the one that pyramid_mailer supports
86 email_conf = dict(
86 email_conf = dict(
87 host=mail_server,
87 host=mail_server,
88 port=email_config.get('smtp_port', 25),
88 port=email_config.get('smtp_port', 25),
89 username=email_config.get('smtp_username'),
89 username=email_config.get('smtp_username'),
90 password=email_config.get('smtp_password'),
90 password=email_config.get('smtp_password'),
91
91
92 tls=str2bool(email_config.get('smtp_use_tls')),
92 tls=str2bool(email_config.get('smtp_use_tls')),
93 ssl=str2bool(email_config.get('smtp_use_ssl')),
93 ssl=str2bool(email_config.get('smtp_use_ssl')),
94
94
95 # SSL key file
95 # SSL key file
96 # keyfile='',
96 # keyfile='',
97
97
98 # SSL certificate file
98 # SSL certificate file
99 # certfile='',
99 # certfile='',
100
100
101 # Location of maildir
101 # Location of maildir
102 # queue_path='',
102 # queue_path='',
103
103
104 default_sender=email_config.get('app_email_from', 'RhodeCode'),
104 default_sender=email_config.get('app_email_from', 'RhodeCode'),
105
105
106 debug=str2bool(email_config.get('smtp_debug')),
106 debug=str2bool(email_config.get('smtp_debug')),
107 # /usr/sbin/sendmail Sendmail executable
107 # /usr/sbin/sendmail Sendmail executable
108 # sendmail_app='',
108 # sendmail_app='',
109
109
110 # {sendmail_app} -t -i -f {sender} Template for sendmail execution
110 # {sendmail_app} -t -i -f {sender} Template for sendmail execution
111 # sendmail_template='',
111 # sendmail_template='',
112 )
112 )
113
113
114 if extra_headers is None:
114 if extra_headers is None:
115 extra_headers = {}
115 extra_headers = {}
116
116
117 extra_headers.setdefault('Date', formatdate(time.time()))
117 extra_headers.setdefault('Date', formatdate(time.time()))
118
118
119 if 'thread_ids' in extra_headers:
119 if 'thread_ids' in extra_headers:
120 thread_ids = extra_headers.pop('thread_ids')
120 thread_ids = extra_headers.pop('thread_ids')
121 extra_headers['References'] = ' '.join('<{}>'.format(t) for t in thread_ids)
121 extra_headers['References'] = ' '.join('<{}>'.format(t) for t in thread_ids)
122
122
123 try:
123 try:
124 mailer = Mailer(**email_conf)
124 mailer = Mailer(**email_conf)
125
125
126 message = Message(subject=subject,
126 message = Message(subject=subject,
127 sender=email_conf['default_sender'],
127 sender=email_conf['default_sender'],
128 recipients=recipients,
128 recipients=recipients,
129 body=body, html=html_body,
129 body=body, html=html_body,
130 extra_headers=extra_headers)
130 extra_headers=extra_headers)
131 mailer.send_immediately(message)
131 mailer.send_immediately(message)
132
132
133 except Exception:
133 except Exception:
134 log.exception('Mail sending failed')
134 log.exception('Mail sending failed')
135 return False
135 return False
136 return True
136 return True
137
137
138
138
139 @async_task(ignore_result=True, base=RequestContextTask)
139 @async_task(ignore_result=True, base=RequestContextTask)
140 def create_repo(form_data, cur_user):
140 def create_repo(form_data, cur_user):
141 from rhodecode.model.repo import RepoModel
141 from rhodecode.model.repo import RepoModel
142 from rhodecode.model.user import UserModel
142 from rhodecode.model.user import UserModel
143 from rhodecode.model.scm import ScmModel
143 from rhodecode.model.scm import ScmModel
144 from rhodecode.model.settings import SettingsModel
144 from rhodecode.model.settings import SettingsModel
145
145
146 log = get_logger(create_repo)
146 log = get_logger(create_repo)
147
147
148 cur_user = UserModel()._get_user(cur_user)
148 cur_user = UserModel()._get_user(cur_user)
149 owner = cur_user
149 owner = cur_user
150
150
151 repo_name = form_data['repo_name']
151 repo_name = form_data['repo_name']
152 repo_name_full = form_data['repo_name_full']
152 repo_name_full = form_data['repo_name_full']
153 repo_type = form_data['repo_type']
153 repo_type = form_data['repo_type']
154 description = form_data['repo_description']
154 description = form_data['repo_description']
155 private = form_data['repo_private']
155 private = form_data['repo_private']
156 clone_uri = form_data.get('clone_uri')
156 clone_uri = form_data.get('clone_uri')
157 repo_group = safe_int(form_data['repo_group'])
157 repo_group = safe_int(form_data['repo_group'])
158 copy_fork_permissions = form_data.get('copy_permissions')
158 copy_fork_permissions = form_data.get('copy_permissions')
159 copy_group_permissions = form_data.get('repo_copy_permissions')
159 copy_group_permissions = form_data.get('repo_copy_permissions')
160 fork_of = form_data.get('fork_parent_id')
160 fork_of = form_data.get('fork_parent_id')
161 state = form_data.get('repo_state', Repository.STATE_PENDING)
161 state = form_data.get('repo_state', Repository.STATE_PENDING)
162
162
163 # repo creation defaults, private and repo_type are filled in form
163 # repo creation defaults, private and repo_type are filled in form
164 defs = SettingsModel().get_default_repo_settings(strip_prefix=True)
164 defs = SettingsModel().get_default_repo_settings(strip_prefix=True)
165 enable_statistics = form_data.get(
165 enable_statistics = form_data.get(
166 'enable_statistics', defs.get('repo_enable_statistics'))
166 'enable_statistics', defs.get('repo_enable_statistics'))
167 enable_locking = form_data.get(
167 enable_locking = form_data.get(
168 'enable_locking', defs.get('repo_enable_locking'))
168 'enable_locking', defs.get('repo_enable_locking'))
169 enable_downloads = form_data.get(
169 enable_downloads = form_data.get(
170 'enable_downloads', defs.get('repo_enable_downloads'))
170 'enable_downloads', defs.get('repo_enable_downloads'))
171
171
172 # set landing rev based on default branches for SCM
172 # set landing rev based on default branches for SCM
173 landing_ref, _label = ScmModel.backend_landing_ref(repo_type)
173 landing_ref, _label = ScmModel.backend_landing_ref(repo_type)
174
174
175 try:
175 try:
176 RepoModel()._create_repo(
176 RepoModel()._create_repo(
177 repo_name=repo_name_full,
177 repo_name=repo_name_full,
178 repo_type=repo_type,
178 repo_type=repo_type,
179 description=description,
179 description=description,
180 owner=owner,
180 owner=owner,
181 private=private,
181 private=private,
182 clone_uri=clone_uri,
182 clone_uri=clone_uri,
183 repo_group=repo_group,
183 repo_group=repo_group,
184 landing_rev=landing_ref,
184 landing_rev=landing_ref,
185 fork_of=fork_of,
185 fork_of=fork_of,
186 copy_fork_permissions=copy_fork_permissions,
186 copy_fork_permissions=copy_fork_permissions,
187 copy_group_permissions=copy_group_permissions,
187 copy_group_permissions=copy_group_permissions,
188 enable_statistics=enable_statistics,
188 enable_statistics=enable_statistics,
189 enable_locking=enable_locking,
189 enable_locking=enable_locking,
190 enable_downloads=enable_downloads,
190 enable_downloads=enable_downloads,
191 state=state
191 state=state
192 )
192 )
193 Session().commit()
193 Session().commit()
194
194
195 # now create this repo on Filesystem
195 # now create this repo on Filesystem
196 RepoModel()._create_filesystem_repo(
196 RepoModel()._create_filesystem_repo(
197 repo_name=repo_name,
197 repo_name=repo_name,
198 repo_type=repo_type,
198 repo_type=repo_type,
199 repo_group=RepoModel()._get_repo_group(repo_group),
199 repo_group=RepoModel()._get_repo_group(repo_group),
200 clone_uri=clone_uri,
200 clone_uri=clone_uri,
201 )
201 )
202 repo = Repository.get_by_repo_name(repo_name_full)
202 repo = Repository.get_by_repo_name(repo_name_full)
203 hooks_base.create_repository(created_by=owner.username, **repo.get_dict())
203 hooks_base.create_repository(created_by=owner.username, **repo.get_dict())
204
204
205 # update repo commit caches initially
205 # update repo commit caches initially
206 repo.update_commit_cache()
206 repo.update_commit_cache()
207
207
208 # set new created state
208 # set new created state
209 repo.set_state(Repository.STATE_CREATED)
209 repo.set_state(Repository.STATE_CREATED)
210 repo_id = repo.repo_id
210 repo_id = repo.repo_id
211 repo_data = repo.get_api_data()
211 repo_data = repo.get_api_data()
212
212
213 audit_logger.store(
213 audit_logger.store(
214 'repo.create', action_data={'data': repo_data},
214 'repo.create', action_data={'data': repo_data},
215 user=cur_user,
215 user=cur_user,
216 repo=audit_logger.RepoWrap(repo_name=repo_name, repo_id=repo_id))
216 repo=audit_logger.RepoWrap(repo_name=repo_name, repo_id=repo_id))
217
217
218 Session().commit()
218 Session().commit()
219 except Exception as e:
219 except Exception as e:
220 log.warning('Exception occurred when creating repository, '
220 log.warning('Exception occurred when creating repository, '
221 'doing cleanup...', exc_info=True)
221 'doing cleanup...', exc_info=True)
222 if isinstance(e, IntegrityError):
222 if isinstance(e, IntegrityError):
223 Session().rollback()
223 Session().rollback()
224
224
225 # rollback things manually !
225 # rollback things manually !
226 repo = Repository.get_by_repo_name(repo_name_full)
226 repo = Repository.get_by_repo_name(repo_name_full)
227 if repo:
227 if repo:
228 Repository.delete(repo.repo_id)
228 Repository.delete(repo.repo_id)
229 Session().commit()
229 Session().commit()
230 RepoModel()._delete_filesystem_repo(repo)
230 RepoModel()._delete_filesystem_repo(repo)
231 log.info('Cleanup of repo %s finished', repo_name_full)
231 log.info('Cleanup of repo %s finished', repo_name_full)
232 raise
232 raise
233
233
234 return True
234 return True
235
235
236
236
237 @async_task(ignore_result=True, base=RequestContextTask)
237 @async_task(ignore_result=True, base=RequestContextTask)
238 def create_repo_fork(form_data, cur_user):
238 def create_repo_fork(form_data, cur_user):
239 """
239 """
240 Creates a fork of repository using internal VCS methods
240 Creates a fork of repository using internal VCS methods
241 """
241 """
242 from rhodecode.model.repo import RepoModel
242 from rhodecode.model.repo import RepoModel
243 from rhodecode.model.user import UserModel
243 from rhodecode.model.user import UserModel
244
244
245 log = get_logger(create_repo_fork)
245 log = get_logger(create_repo_fork)
246
246
247 cur_user = UserModel()._get_user(cur_user)
247 cur_user = UserModel()._get_user(cur_user)
248 owner = cur_user
248 owner = cur_user
249
249
250 repo_name = form_data['repo_name'] # fork in this case
250 repo_name = form_data['repo_name'] # fork in this case
251 repo_name_full = form_data['repo_name_full']
251 repo_name_full = form_data['repo_name_full']
252 repo_type = form_data['repo_type']
252 repo_type = form_data['repo_type']
253 description = form_data['description']
253 description = form_data['description']
254 private = form_data['private']
254 private = form_data['private']
255 clone_uri = form_data.get('clone_uri')
255 clone_uri = form_data.get('clone_uri')
256 repo_group = safe_int(form_data['repo_group'])
256 repo_group = safe_int(form_data['repo_group'])
257 landing_ref = form_data['landing_rev']
257 landing_ref = form_data['landing_rev']
258 copy_fork_permissions = form_data.get('copy_permissions')
258 copy_fork_permissions = form_data.get('copy_permissions')
259 fork_id = safe_int(form_data.get('fork_parent_id'))
259 fork_id = safe_int(form_data.get('fork_parent_id'))
260
260
261 try:
261 try:
262 fork_of = RepoModel()._get_repo(fork_id)
262 fork_of = RepoModel()._get_repo(fork_id)
263 RepoModel()._create_repo(
263 RepoModel()._create_repo(
264 repo_name=repo_name_full,
264 repo_name=repo_name_full,
265 repo_type=repo_type,
265 repo_type=repo_type,
266 description=description,
266 description=description,
267 owner=owner,
267 owner=owner,
268 private=private,
268 private=private,
269 clone_uri=clone_uri,
269 clone_uri=clone_uri,
270 repo_group=repo_group,
270 repo_group=repo_group,
271 landing_rev=landing_ref,
271 landing_rev=landing_ref,
272 fork_of=fork_of,
272 fork_of=fork_of,
273 copy_fork_permissions=copy_fork_permissions
273 copy_fork_permissions=copy_fork_permissions
274 )
274 )
275
275
276 Session().commit()
276 Session().commit()
277
277
278 base_path = Repository.base_path()
278 base_path = Repository.base_path()
279 source_repo_path = os.path.join(base_path, fork_of.repo_name)
279 source_repo_path = os.path.join(base_path, fork_of.repo_name)
280
280
281 # now create this repo on Filesystem
281 # now create this repo on Filesystem
282 RepoModel()._create_filesystem_repo(
282 RepoModel()._create_filesystem_repo(
283 repo_name=repo_name,
283 repo_name=repo_name,
284 repo_type=repo_type,
284 repo_type=repo_type,
285 repo_group=RepoModel()._get_repo_group(repo_group),
285 repo_group=RepoModel()._get_repo_group(repo_group),
286 clone_uri=source_repo_path,
286 clone_uri=source_repo_path,
287 )
287 )
288 repo = Repository.get_by_repo_name(repo_name_full)
288 repo = Repository.get_by_repo_name(repo_name_full)
289 hooks_base.create_repository(created_by=owner.username, **repo.get_dict())
289 hooks_base.create_repository(created_by=owner.username, **repo.get_dict())
290
290
291 # update repo commit caches initially
291 # update repo commit caches initially
292 config = repo._config
292 config = repo._config
293 config.set('extensions', 'largefiles', '')
293 config.set('extensions', 'largefiles', '')
294 repo.update_commit_cache(config=config)
294 repo.update_commit_cache(config=config)
295
295
296 # set new created state
296 # set new created state
297 repo.set_state(Repository.STATE_CREATED)
297 repo.set_state(Repository.STATE_CREATED)
298
298
299 repo_id = repo.repo_id
299 repo_id = repo.repo_id
300 repo_data = repo.get_api_data()
300 repo_data = repo.get_api_data()
301 audit_logger.store(
301 audit_logger.store(
302 'repo.fork', action_data={'data': repo_data},
302 'repo.fork', action_data={'data': repo_data},
303 user=cur_user,
303 user=cur_user,
304 repo=audit_logger.RepoWrap(repo_name=repo_name, repo_id=repo_id))
304 repo=audit_logger.RepoWrap(repo_name=repo_name, repo_id=repo_id))
305
305
306 Session().commit()
306 Session().commit()
307 except Exception as e:
307 except Exception as e:
308 log.warning('Exception occurred when forking repository, '
308 log.warning('Exception occurred when forking repository, '
309 'doing cleanup...', exc_info=True)
309 'doing cleanup...', exc_info=True)
310 if isinstance(e, IntegrityError):
310 if isinstance(e, IntegrityError):
311 Session().rollback()
311 Session().rollback()
312
312
313 # rollback things manually !
313 # rollback things manually !
314 repo = Repository.get_by_repo_name(repo_name_full)
314 repo = Repository.get_by_repo_name(repo_name_full)
315 if repo:
315 if repo:
316 Repository.delete(repo.repo_id)
316 Repository.delete(repo.repo_id)
317 Session().commit()
317 Session().commit()
318 RepoModel()._delete_filesystem_repo(repo)
318 RepoModel()._delete_filesystem_repo(repo)
319 log.info('Cleanup of repo %s finished', repo_name_full)
319 log.info('Cleanup of repo %s finished', repo_name_full)
320 raise
320 raise
321
321
322 return True
322 return True
323
323
324
324
325 @async_task(ignore_result=True)
325 @async_task(ignore_result=True)
326 def repo_maintenance(repoid):
326 def repo_maintenance(repoid):
327 from rhodecode.lib import repo_maintenance as repo_maintenance_lib
327 from rhodecode.lib import repo_maintenance as repo_maintenance_lib
328 log = get_logger(repo_maintenance)
328 log = get_logger(repo_maintenance)
329 repo = Repository.get_by_id_or_repo_name(repoid)
329 repo = Repository.get_by_id_or_repo_name(repoid)
330 if repo:
330 if repo:
331 maintenance = repo_maintenance_lib.RepoMaintenance()
331 maintenance = repo_maintenance_lib.RepoMaintenance()
332 tasks = maintenance.get_tasks_for_repo(repo)
332 tasks = maintenance.get_tasks_for_repo(repo)
333 log.debug('Executing %s tasks on repo `%s`', tasks, repoid)
333 log.debug('Executing %s tasks on repo `%s`', tasks, repoid)
334 executed_types = maintenance.execute(repo)
334 executed_types = maintenance.execute(repo)
335 log.debug('Got execution results %s', executed_types)
335 log.debug('Got execution results %s', executed_types)
336 else:
336 else:
337 log.debug('Repo `%s` not found or without a clone_url', repoid)
337 log.debug('Repo `%s` not found or without a clone_url', repoid)
338
338
339
339
340 @async_task(ignore_result=True)
340 @async_task(ignore_result=True)
341 def check_for_update():
341 def check_for_update():
342 from rhodecode.model.update import UpdateModel
342 from rhodecode.model.update import UpdateModel
343 update_url = UpdateModel().get_update_url()
343 update_url = UpdateModel().get_update_url()
344 cur_ver = rhodecode.__version__
344 cur_ver = rhodecode.__version__
345
345
346 try:
346 try:
347 data = UpdateModel().get_update_data(update_url)
347 data = UpdateModel().get_update_data(update_url)
348 latest = data['versions'][0]
348 latest = data['versions'][0]
349 UpdateModel().store_version(latest['version'])
349 UpdateModel().store_version(latest['version'])
350 except Exception:
350 except Exception:
351 pass
351 pass
352
352
353
353
354 @async_task(ignore_result=False)
354 @async_task(ignore_result=False)
355 def beat_check(*args, **kwargs):
355 def beat_check(*args, **kwargs):
356 log = get_logger(beat_check)
356 log = get_logger(beat_check)
357 log.info('Got args: %r and kwargs %r', args, kwargs)
357 log.info('%r: Got args: %r and kwargs %r', beat_check, args, kwargs)
358 return time.time()
358 return time.time()
359
359
360
360
361 @async_task(ignore_result=True)
361 @async_task(ignore_result=True)
362 def sync_last_update(*args, **kwargs):
362 def sync_last_update(*args, **kwargs):
363
363
364 skip_repos = kwargs.get('skip_repos')
364 skip_repos = kwargs.get('skip_repos')
365 if not skip_repos:
365 if not skip_repos:
366 repos = Repository.query() \
366 repos = Repository.query() \
367 .order_by(Repository.group_id.asc())
367 .order_by(Repository.group_id.asc())
368
368
369 for repo in repos:
369 for repo in repos:
370 repo.update_commit_cache()
370 repo.update_commit_cache()
371
371
372 skip_groups = kwargs.get('skip_groups')
372 skip_groups = kwargs.get('skip_groups')
373 if not skip_groups:
373 if not skip_groups:
374 repo_groups = RepoGroup.query() \
374 repo_groups = RepoGroup.query() \
375 .filter(RepoGroup.group_parent_id == None)
375 .filter(RepoGroup.group_parent_id == None)
376
376
377 for root_gr in repo_groups:
377 for root_gr in repo_groups:
378 for repo_gr in reversed(root_gr.recursive_groups()):
378 for repo_gr in reversed(root_gr.recursive_groups()):
379 repo_gr.update_commit_cache()
379 repo_gr.update_commit_cache()
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,152 +1,160 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2020 RhodeCode GmbH
3 # Copyright (C) 2011-2020 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 re
22 import re
23 import logging
23 import logging
24
24
25
25
26 import ipaddress
26 import ipaddress
27 import colander
27 import colander
28
28
29 from rhodecode.translation import _
29 from rhodecode.translation import _
30 from rhodecode.lib.utils2 import glob2re, safe_unicode
30 from rhodecode.lib.utils2 import glob2re, safe_unicode
31 from rhodecode.lib.ext_json import json
31 from rhodecode.lib.ext_json import json
32
32
33 log = logging.getLogger(__name__)
33 log = logging.getLogger(__name__)
34
34
35
35
36 def ip_addr_validator(node, value):
36 def ip_addr_validator(node, value):
37 try:
37 try:
38 # this raises an ValueError if address is not IpV4 or IpV6
38 # this raises an ValueError if address is not IpV4 or IpV6
39 ipaddress.ip_network(safe_unicode(value), strict=False)
39 ipaddress.ip_network(safe_unicode(value), strict=False)
40 except ValueError:
40 except ValueError:
41 msg = _(u'Please enter a valid IPv4 or IpV6 address')
41 msg = _(u'Please enter a valid IPv4 or IpV6 address')
42 raise colander.Invalid(node, msg)
42 raise colander.Invalid(node, msg)
43
43
44
44
45 class IpAddrValidator(object):
45 class IpAddrValidator(object):
46 def __init__(self, strict=True):
46 def __init__(self, strict=True):
47 self.strict = strict
47 self.strict = strict
48
48
49 def __call__(self, node, value):
49 def __call__(self, node, value):
50 try:
50 try:
51 # this raises an ValueError if address is not IpV4 or IpV6
51 # this raises an ValueError if address is not IpV4 or IpV6
52 ipaddress.ip_network(safe_unicode(value), strict=self.strict)
52 ipaddress.ip_network(safe_unicode(value), strict=self.strict)
53 except ValueError:
53 except ValueError:
54 msg = _(u'Please enter a valid IPv4 or IpV6 address')
54 msg = _(u'Please enter a valid IPv4 or IpV6 address')
55 raise colander.Invalid(node, msg)
55 raise colander.Invalid(node, msg)
56
56
57
57
58 def glob_validator(node, value):
58 def glob_validator(node, value):
59 try:
59 try:
60 re.compile('^' + glob2re(value) + '$')
60 re.compile('^' + glob2re(value) + '$')
61 except Exception:
61 except Exception:
62 msg = _(u'Invalid glob pattern')
62 msg = _(u'Invalid glob pattern')
63 raise colander.Invalid(node, msg)
63 raise colander.Invalid(node, msg)
64
64
65
65
66 def valid_name_validator(node, value):
66 def valid_name_validator(node, value):
67 from rhodecode.model.validation_schema import types
67 from rhodecode.model.validation_schema import types
68 if value is types.RootLocation:
68 if value is types.RootLocation:
69 return
69 return
70
70
71 msg = _('Name must start with a letter or number. Got `{}`').format(value)
71 msg = _('Name must start with a letter or number. Got `{}`').format(value)
72 if not re.match(r'^[a-zA-z0-9]{1,}', value):
72 if not re.match(r'^[a-zA-z0-9]{1,}', value):
73 raise colander.Invalid(node, msg)
73 raise colander.Invalid(node, msg)
74
74
75
75
76 class InvalidCloneUrl(Exception):
76 class InvalidCloneUrl(Exception):
77 allowed_prefixes = ()
77 allowed_prefixes = ()
78
78
79
79
80 def url_validator(url, repo_type, config):
80 def url_validator(url, repo_type, config):
81 from rhodecode.lib.vcs.backends.hg import MercurialRepository
81 from rhodecode.lib.vcs.backends.hg import MercurialRepository
82 from rhodecode.lib.vcs.backends.git import GitRepository
82 from rhodecode.lib.vcs.backends.git import GitRepository
83 from rhodecode.lib.vcs.backends.svn import SubversionRepository
83 from rhodecode.lib.vcs.backends.svn import SubversionRepository
84
84
85 if repo_type == 'hg':
85 if repo_type == 'hg':
86 allowed_prefixes = ('http', 'svn+http', 'git+http')
86 allowed_prefixes = ('http', 'svn+http', 'git+http')
87
87
88 if 'http' in url[:4]:
88 if 'http' in url[:4]:
89 # initially check if it's at least the proper URL
89 # initially check if it's at least the proper URL
90 # or does it pass basic auth
90 # or does it pass basic auth
91
91
92 return MercurialRepository.check_url(url, config)
92 return MercurialRepository.check_url(url, config)
93 elif 'svn+http' in url[:8]: # svn->hg import
93 elif 'svn+http' in url[:8]: # svn->hg import
94 SubversionRepository.check_url(url, config)
94 SubversionRepository.check_url(url, config)
95 elif 'git+http' in url[:8]: # git->hg import
95 elif 'git+http' in url[:8]: # git->hg import
96 raise NotImplementedError()
96 raise NotImplementedError()
97 else:
97 else:
98 exc = InvalidCloneUrl('Clone from URI %s not allowed. '
98 exc = InvalidCloneUrl('Clone from URI %s not allowed. '
99 'Allowed url must start with one of %s'
99 'Allowed url must start with one of %s'
100 % (url, ','.join(allowed_prefixes)))
100 % (url, ','.join(allowed_prefixes)))
101 exc.allowed_prefixes = allowed_prefixes
101 exc.allowed_prefixes = allowed_prefixes
102 raise exc
102 raise exc
103
103
104 elif repo_type == 'git':
104 elif repo_type == 'git':
105 allowed_prefixes = ('http', 'svn+http', 'hg+http')
105 allowed_prefixes = ('http', 'svn+http', 'hg+http')
106 if 'http' in url[:4]:
106 if 'http' in url[:4]:
107 # initially check if it's at least the proper URL
107 # initially check if it's at least the proper URL
108 # or does it pass basic auth
108 # or does it pass basic auth
109 return GitRepository.check_url(url, config)
109 return GitRepository.check_url(url, config)
110 elif 'svn+http' in url[:8]: # svn->git import
110 elif 'svn+http' in url[:8]: # svn->git import
111 raise NotImplementedError()
111 raise NotImplementedError()
112 elif 'hg+http' in url[:8]: # hg->git import
112 elif 'hg+http' in url[:8]: # hg->git import
113 raise NotImplementedError()
113 raise NotImplementedError()
114 else:
114 else:
115 exc = InvalidCloneUrl('Clone from URI %s not allowed. '
115 exc = InvalidCloneUrl('Clone from URI %s not allowed. '
116 'Allowed url must start with one of %s'
116 'Allowed url must start with one of %s'
117 % (url, ','.join(allowed_prefixes)))
117 % (url, ','.join(allowed_prefixes)))
118 exc.allowed_prefixes = allowed_prefixes
118 exc.allowed_prefixes = allowed_prefixes
119 raise exc
119 raise exc
120 elif repo_type == 'svn':
120 elif repo_type == 'svn':
121 # no validation for SVN yet
121 # no validation for SVN yet
122 return
122 return
123
123
124 raise InvalidCloneUrl('Invalid repo type specified: `{}`'.format(repo_type))
124 raise InvalidCloneUrl('Invalid repo type specified: `{}`'.format(repo_type))
125
125
126
126
127 class CloneUriValidator(object):
127 class CloneUriValidator(object):
128 def __init__(self, repo_type):
128 def __init__(self, repo_type):
129 self.repo_type = repo_type
129 self.repo_type = repo_type
130
130
131 def __call__(self, node, value):
131 def __call__(self, node, value):
132
132
133 from rhodecode.lib.utils import make_db_config
133 from rhodecode.lib.utils import make_db_config
134 try:
134 try:
135 config = make_db_config(clear_session=False)
135 config = make_db_config(clear_session=False)
136 url_validator(value, self.repo_type, config)
136 url_validator(value, self.repo_type, config)
137 except InvalidCloneUrl as e:
137 except InvalidCloneUrl as e:
138 log.warning(e)
138 log.warning(e)
139 raise colander.Invalid(node, e.message)
139 raise colander.Invalid(node, e.message)
140 except Exception:
140 except Exception:
141 log.exception('Url validation failed')
141 log.exception('Url validation failed')
142 msg = _(u'invalid clone url for {repo_type} repository').format(
142 msg = _(u'invalid clone url for {repo_type} repository').format(
143 repo_type=self.repo_type)
143 repo_type=self.repo_type)
144 raise colander.Invalid(node, msg)
144 raise colander.Invalid(node, msg)
145
145
146
146
147 def json_validator(node, value):
147 def json_validator(node, value):
148 try:
148 try:
149 json.loads(value)
149 json.loads(value)
150 except (Exception,):
150 except (Exception,) as e:
151 msg = _(u'Please enter a valid json object')
151 msg = _(u'Please enter a valid json object')
152 raise colander.Invalid(node, msg)
152 raise colander.Invalid(node, msg)
153
154
155 def json_validator_with_exc(node, value):
156 try:
157 json.loads(value)
158 except (Exception,) as e:
159 msg = _(u'Please enter a valid json object: `{}`'.format(e))
160 raise colander.Invalid(node, msg)
General Comments 0
You need to be logged in to leave comments. Login now