##// END OF EJS Templates
added option to do a checkout after cloning a repository
marcink -
r1742:40c4f735 beta
parent child Browse files
Show More
@@ -1,405 +1,407 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.celerylib.tasks
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 RhodeCode task modules, containing all task that suppose to be run
7 7 by celery daemon
8 8
9 9 :created_on: Oct 6, 2010
10 10 :author: marcink
11 11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
12 12 :license: GPLv3, see COPYING for more details.
13 13 """
14 14 # This program is free software: you can redistribute it and/or modify
15 15 # it under the terms of the GNU General Public License as published by
16 16 # the Free Software Foundation, either version 3 of the License, or
17 17 # (at your option) any later version.
18 18 #
19 19 # This program is distributed in the hope that it will be useful,
20 20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 22 # GNU General Public License for more details.
23 23 #
24 24 # You should have received a copy of the GNU General Public License
25 25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 26 from celery.decorators import task
27 27
28 28 import os
29 29 import traceback
30 30 import logging
31 31 from os.path import join as jn
32 32
33 33 from time import mktime
34 34 from operator import itemgetter
35 35 from string import lower
36 36
37 37 from pylons import config, url
38 38 from pylons.i18n.translation import _
39 39
40 40 from vcs import get_backend
41 41
42 42 from rhodecode import CELERY_ON
43 43 from rhodecode.lib import LANGUAGES_EXTENSIONS_MAP, safe_str
44 44 from rhodecode.lib.celerylib import run_task, locked_task, str2bool, \
45 45 __get_lockkey, LockHeld, DaemonLock
46 46 from rhodecode.lib.helpers import person
47 47 from rhodecode.lib.rcmail.smtp_mailer import SmtpMailer
48 48 from rhodecode.lib.utils import add_cache, action_logger
49 49 from rhodecode.lib.compat import json, OrderedDict
50 50
51 51 from rhodecode.model import init_model
52 52 from rhodecode.model import meta
53 53 from rhodecode.model.db import Statistics, Repository, User
54 54
55 55 from sqlalchemy import engine_from_config
56 56
57 57 add_cache(config)
58 58
59 59 __all__ = ['whoosh_index', 'get_commits_stats',
60 60 'reset_user_password', 'send_email']
61 61
62 62
63 63 def get_session():
64 64 if CELERY_ON:
65 65 engine = engine_from_config(config, 'sqlalchemy.db1.')
66 66 init_model(engine)
67 67 sa = meta.Session()
68 68 return sa
69 69
70 70 def get_logger(cls):
71 71 if CELERY_ON:
72 72 try:
73 73 log = cls.get_logger()
74 74 except:
75 75 log = logging.getLogger(__name__)
76 76 else:
77 77 log = logging.getLogger(__name__)
78 78
79 79 return log
80 80
81 81 @task(ignore_result=True)
82 82 @locked_task
83 83 def whoosh_index(repo_location, full_index):
84 84 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
85 85
86 86 #log = whoosh_index.get_logger()
87 87
88 88 index_location = config['index_dir']
89 89 WhooshIndexingDaemon(index_location=index_location,
90 90 repo_location=repo_location, sa=get_session())\
91 91 .run(full_index=full_index)
92 92
93 93
94 94 @task(ignore_result=True)
95 95 def get_commits_stats(repo_name, ts_min_y, ts_max_y):
96 96 log = get_logger(get_commits_stats)
97 97
98 98 lockkey = __get_lockkey('get_commits_stats', repo_name, ts_min_y,
99 99 ts_max_y)
100 100 lockkey_path = config['here']
101 101
102 102 log.info('running task with lockkey %s', lockkey)
103 103 try:
104 104 sa = get_session()
105 105 lock = l = DaemonLock(file_=jn(lockkey_path, lockkey))
106 106
107 107 # for js data compatibilty cleans the key for person from '
108 108 akc = lambda k: person(k).replace('"', "")
109 109
110 110 co_day_auth_aggr = {}
111 111 commits_by_day_aggregate = {}
112 112 repo = Repository.get_by_repo_name(repo_name).scm_instance
113 113 repo_size = len(repo.revisions)
114 114 #return if repo have no revisions
115 115 if repo_size < 1:
116 116 lock.release()
117 117 return True
118 118
119 119 skip_date_limit = True
120 120 parse_limit = int(config['app_conf'].get('commit_parse_limit'))
121 121 last_rev = 0
122 122 last_cs = None
123 123 timegetter = itemgetter('time')
124 124
125 125 dbrepo = sa.query(Repository)\
126 126 .filter(Repository.repo_name == repo_name).scalar()
127 127 cur_stats = sa.query(Statistics)\
128 128 .filter(Statistics.repository == dbrepo).scalar()
129 129
130 130 if cur_stats is not None:
131 131 last_rev = cur_stats.stat_on_revision
132 132
133 133 if last_rev == repo.get_changeset().revision and repo_size > 1:
134 134 # pass silently without any work if we're not on first revision or
135 135 # current state of parsing revision(from db marker) is the
136 136 # last revision
137 137 lock.release()
138 138 return True
139 139
140 140 if cur_stats:
141 141 commits_by_day_aggregate = OrderedDict(json.loads(
142 142 cur_stats.commit_activity_combined))
143 143 co_day_auth_aggr = json.loads(cur_stats.commit_activity)
144 144
145 145 log.debug('starting parsing %s', parse_limit)
146 146 lmktime = mktime
147 147
148 148 last_rev = last_rev + 1 if last_rev > 0 else last_rev
149 149
150 150 for cs in repo[last_rev:last_rev + parse_limit]:
151 151 last_cs = cs # remember last parsed changeset
152 152 k = lmktime([cs.date.timetuple()[0], cs.date.timetuple()[1],
153 153 cs.date.timetuple()[2], 0, 0, 0, 0, 0, 0])
154 154
155 155 if akc(cs.author) in co_day_auth_aggr:
156 156 try:
157 157 l = [timegetter(x) for x in
158 158 co_day_auth_aggr[akc(cs.author)]['data']]
159 159 time_pos = l.index(k)
160 160 except ValueError:
161 161 time_pos = False
162 162
163 163 if time_pos >= 0 and time_pos is not False:
164 164
165 165 datadict = \
166 166 co_day_auth_aggr[akc(cs.author)]['data'][time_pos]
167 167
168 168 datadict["commits"] += 1
169 169 datadict["added"] += len(cs.added)
170 170 datadict["changed"] += len(cs.changed)
171 171 datadict["removed"] += len(cs.removed)
172 172
173 173 else:
174 174 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
175 175
176 176 datadict = {"time": k,
177 177 "commits": 1,
178 178 "added": len(cs.added),
179 179 "changed": len(cs.changed),
180 180 "removed": len(cs.removed),
181 181 }
182 182 co_day_auth_aggr[akc(cs.author)]['data']\
183 183 .append(datadict)
184 184
185 185 else:
186 186 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
187 187 co_day_auth_aggr[akc(cs.author)] = {
188 188 "label": akc(cs.author),
189 189 "data": [{"time":k,
190 190 "commits":1,
191 191 "added":len(cs.added),
192 192 "changed":len(cs.changed),
193 193 "removed":len(cs.removed),
194 194 }],
195 195 "schema": ["commits"],
196 196 }
197 197
198 198 #gather all data by day
199 199 if k in commits_by_day_aggregate:
200 200 commits_by_day_aggregate[k] += 1
201 201 else:
202 202 commits_by_day_aggregate[k] = 1
203 203
204 204 overview_data = sorted(commits_by_day_aggregate.items(),
205 205 key=itemgetter(0))
206 206
207 207 if not co_day_auth_aggr:
208 208 co_day_auth_aggr[akc(repo.contact)] = {
209 209 "label": akc(repo.contact),
210 210 "data": [0, 1],
211 211 "schema": ["commits"],
212 212 }
213 213
214 214 stats = cur_stats if cur_stats else Statistics()
215 215 stats.commit_activity = json.dumps(co_day_auth_aggr)
216 216 stats.commit_activity_combined = json.dumps(overview_data)
217 217
218 218 log.debug('last revison %s', last_rev)
219 219 leftovers = len(repo.revisions[last_rev:])
220 220 log.debug('revisions to parse %s', leftovers)
221 221
222 222 if last_rev == 0 or leftovers < parse_limit:
223 223 log.debug('getting code trending stats')
224 224 stats.languages = json.dumps(__get_codes_stats(repo_name))
225 225
226 226 try:
227 227 stats.repository = dbrepo
228 228 stats.stat_on_revision = last_cs.revision if last_cs else 0
229 229 sa.add(stats)
230 230 sa.commit()
231 231 except:
232 232 log.error(traceback.format_exc())
233 233 sa.rollback()
234 234 lock.release()
235 235 return False
236 236
237 237 #final release
238 238 lock.release()
239 239
240 240 #execute another task if celery is enabled
241 241 if len(repo.revisions) > 1 and CELERY_ON:
242 242 run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y)
243 243 return True
244 244 except LockHeld:
245 245 log.info('LockHeld')
246 246 return 'Task with key %s already running' % lockkey
247 247
248 248 @task(ignore_result=True)
249 249 def send_password_link(user_email):
250 250 from rhodecode.model.notification import EmailNotificationModel
251 251
252 252 log = get_logger(send_password_link)
253 253
254 254 try:
255 255 sa = get_session()
256 256 user = User.get_by_email(user_email)
257 257 if user:
258 258 log.debug('password reset user found %s' % user)
259 259 link = url('reset_password_confirmation', key=user.api_key,
260 260 qualified=True)
261 261 reg_type = EmailNotificationModel.TYPE_PASSWORD_RESET
262 262 body = EmailNotificationModel().get_email_tmpl(reg_type,
263 263 **{'user':user.short_contact,
264 264 'reset_url':link})
265 265 log.debug('sending email')
266 266 run_task(send_email, user_email,
267 267 _("password reset link"), body)
268 268 log.info('send new password mail to %s', user_email)
269 269 else:
270 270 log.debug("password reset email %s not found" % user_email)
271 271 except:
272 272 log.error(traceback.format_exc())
273 273 return False
274 274
275 275 return True
276 276
277 277 @task(ignore_result=True)
278 278 def reset_user_password(user_email):
279 279 from rhodecode.lib import auth
280 280
281 281 log = get_logger(reset_user_password)
282 282
283 283 try:
284 284 try:
285 285 sa = get_session()
286 286 user = User.get_by_email(user_email)
287 287 new_passwd = auth.PasswordGenerator().gen_password(8,
288 288 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
289 289 if user:
290 290 user.password = auth.get_crypt_password(new_passwd)
291 291 user.api_key = auth.generate_api_key(user.username)
292 292 sa.add(user)
293 293 sa.commit()
294 294 log.info('change password for %s', user_email)
295 295 if new_passwd is None:
296 296 raise Exception('unable to generate new password')
297 297 except:
298 298 log.error(traceback.format_exc())
299 299 sa.rollback()
300 300
301 301 run_task(send_email, user_email,
302 302 'Your new password',
303 303 'Your new RhodeCode password:%s' % (new_passwd))
304 304 log.info('send new password mail to %s', user_email)
305 305
306 306 except:
307 307 log.error('Failed to update user password')
308 308 log.error(traceback.format_exc())
309 309
310 310 return True
311 311
312 312
313 313 @task(ignore_result=True)
314 314 def send_email(recipients, subject, body, html_body=''):
315 315 """
316 316 Sends an email with defined parameters from the .ini files.
317 317
318 318 :param recipients: list of recipients, it this is empty the defined email
319 319 address from field 'email_to' is used instead
320 320 :param subject: subject of the mail
321 321 :param body: body of the mail
322 322 :param html_body: html version of body
323 323 """
324 324 log = get_logger(send_email)
325 325 sa = get_session()
326 326 email_config = config
327 327 subject = "%s %s" % (email_config.get('email_prefix'), subject)
328 328 if not recipients:
329 329 # if recipients are not defined we send to email_config + all admins
330 330 admins = [u.email for u in User.query()
331 331 .filter(User.admin == True).all()]
332 332 recipients = [email_config.get('email_to')] + admins
333 333
334 334 mail_from = email_config.get('app_email_from', 'RhodeCode')
335 335 user = email_config.get('smtp_username')
336 336 passwd = email_config.get('smtp_password')
337 337 mail_server = email_config.get('smtp_server')
338 338 mail_port = email_config.get('smtp_port')
339 339 tls = str2bool(email_config.get('smtp_use_tls'))
340 340 ssl = str2bool(email_config.get('smtp_use_ssl'))
341 341 debug = str2bool(config.get('debug'))
342 342 smtp_auth = email_config.get('smtp_auth')
343 343
344 344 try:
345 345 m = SmtpMailer(mail_from, user, passwd, mail_server, smtp_auth,
346 346 mail_port, ssl, tls, debug=debug)
347 347 m.send(recipients, subject, body, html_body)
348 348 except:
349 349 log.error('Mail sending failed')
350 350 log.error(traceback.format_exc())
351 351 return False
352 352 return True
353 353
354 354
355 355 @task(ignore_result=True)
356 356 def create_repo_fork(form_data, cur_user):
357 357 """
358 358 Creates a fork of repository using interval VCS methods
359 359
360 360 :param form_data:
361 361 :param cur_user:
362 362 """
363 363 from rhodecode.model.repo import RepoModel
364 364
365 365 log = get_logger(create_repo_fork)
366 366
367 367 Session = get_session()
368 368 base_path = Repository.base_path()
369 369
370 370 RepoModel(Session).create(form_data, cur_user, just_db=True, fork=True)
371 371
372 372 alias = form_data['repo_type']
373 373 org_repo_name = form_data['org_path']
374 374 fork_name = form_data['repo_name_full']
375 update_after_clone = form_data['update_after_clone']
375 376 source_repo_path = os.path.join(base_path, org_repo_name)
376 377 destination_fork_path = os.path.join(base_path, fork_name)
377 378
378 379 log.info('creating fork of %s as %s', source_repo_path,
379 380 destination_fork_path)
380 381 backend = get_backend(alias)
381 382 backend(safe_str(destination_fork_path), create=True,
382 src_url=safe_str(source_repo_path))
383 src_url=safe_str(source_repo_path),
384 update_after_clone=update_after_clone)
383 385 action_logger(cur_user, 'user_forked_repo:%s' % fork_name,
384 386 org_repo_name, '', Session)
385 387 # finally commit at latest possible stage
386 388 Session.commit()
387 389
388 390 def __get_codes_stats(repo_name):
389 391 repo = Repository.get_by_repo_name(repo_name).scm_instance
390 392
391 393 tip = repo.get_changeset()
392 394 code_stats = {}
393 395
394 396 def aggregate(cs):
395 397 for f in cs[2]:
396 398 ext = lower(f.extension)
397 399 if ext in LANGUAGES_EXTENSIONS_MAP.keys() and not f.is_binary:
398 400 if ext in code_stats:
399 401 code_stats[ext] += 1
400 402 else:
401 403 code_stats[ext] = 1
402 404
403 405 map(aggregate, tip.walk('/'))
404 406
405 407 return code_stats or {}
@@ -1,683 +1,684 b''
1 1 """ this is forms validation classes
2 2 http://formencode.org/module-formencode.validators.html
3 3 for list off all availible validators
4 4
5 5 we can create our own validators
6 6
7 7 The table below outlines the options which can be used in a schema in addition to the validators themselves
8 8 pre_validators [] These validators will be applied before the schema
9 9 chained_validators [] These validators will be applied after the schema
10 10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
11 11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
12 12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
13 13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
14 14
15 15
16 16 <name> = formencode.validators.<name of validator>
17 17 <name> must equal form name
18 18 list=[1,2,3,4,5]
19 19 for SELECT use formencode.All(OneOf(list), Int())
20 20
21 21 """
22 22 import os
23 23 import re
24 24 import logging
25 25 import traceback
26 26
27 27 import formencode
28 28 from formencode import All
29 29 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
30 30 Email, Bool, StringBoolean, Set
31 31
32 32 from pylons.i18n.translation import _
33 33 from webhelpers.pylonslib.secure_form import authentication_token
34 34
35 35 from rhodecode.config.routing import ADMIN_PREFIX
36 36 from rhodecode.lib.utils import repo_name_slug
37 37 from rhodecode.lib.auth import authenticate, get_crypt_password
38 38 from rhodecode.lib.exceptions import LdapImportError
39 39 from rhodecode.model.user import UserModel
40 40 from rhodecode.model.repo import RepoModel
41 41 from rhodecode.model.db import User, UsersGroup, RepoGroup
42 42 from rhodecode import BACKENDS
43 43
44 44 log = logging.getLogger(__name__)
45 45
46 46 #this is needed to translate the messages using _() in validators
47 47 class State_obj(object):
48 48 _ = staticmethod(_)
49 49
50 50 #==============================================================================
51 51 # VALIDATORS
52 52 #==============================================================================
53 53 class ValidAuthToken(formencode.validators.FancyValidator):
54 54 messages = {'invalid_token':_('Token mismatch')}
55 55
56 56 def validate_python(self, value, state):
57 57
58 58 if value != authentication_token():
59 59 raise formencode.Invalid(self.message('invalid_token', state,
60 60 search_number=value), value, state)
61 61
62 62 def ValidUsername(edit, old_data):
63 63 class _ValidUsername(formencode.validators.FancyValidator):
64 64
65 65 def validate_python(self, value, state):
66 66 if value in ['default', 'new_user']:
67 67 raise formencode.Invalid(_('Invalid username'), value, state)
68 68 #check if user is unique
69 69 old_un = None
70 70 if edit:
71 71 old_un = UserModel().get(old_data.get('user_id')).username
72 72
73 73 if old_un != value or not edit:
74 74 if User.get_by_username(value, case_insensitive=True):
75 75 raise formencode.Invalid(_('This username already '
76 76 'exists') , value, state)
77 77
78 78 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
79 79 raise formencode.Invalid(_('Username may only contain '
80 80 'alphanumeric characters '
81 81 'underscores, periods or dashes '
82 82 'and must begin with alphanumeric '
83 83 'character'), value, state)
84 84
85 85 return _ValidUsername
86 86
87 87
88 88 def ValidUsersGroup(edit, old_data):
89 89
90 90 class _ValidUsersGroup(formencode.validators.FancyValidator):
91 91
92 92 def validate_python(self, value, state):
93 93 if value in ['default']:
94 94 raise formencode.Invalid(_('Invalid group name'), value, state)
95 95 #check if group is unique
96 96 old_ugname = None
97 97 if edit:
98 98 old_ugname = UsersGroup.get(
99 99 old_data.get('users_group_id')).users_group_name
100 100
101 101 if old_ugname != value or not edit:
102 102 if UsersGroup.get_by_group_name(value, cache=False,
103 103 case_insensitive=True):
104 104 raise formencode.Invalid(_('This users group '
105 105 'already exists') , value,
106 106 state)
107 107
108 108
109 109 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
110 110 raise formencode.Invalid(_('RepoGroup name may only contain '
111 111 'alphanumeric characters '
112 112 'underscores, periods or dashes '
113 113 'and must begin with alphanumeric '
114 114 'character'), value, state)
115 115
116 116 return _ValidUsersGroup
117 117
118 118
119 119 def ValidReposGroup(edit, old_data):
120 120 class _ValidReposGroup(formencode.validators.FancyValidator):
121 121
122 122 def validate_python(self, value, state):
123 123 # TODO WRITE VALIDATIONS
124 124 group_name = value.get('group_name')
125 125 group_parent_id = value.get('group_parent_id')
126 126
127 127 # slugify repo group just in case :)
128 128 slug = repo_name_slug(group_name)
129 129
130 130 # check for parent of self
131 131 parent_of_self = lambda:(old_data['group_id'] == int(group_parent_id)
132 132 if group_parent_id else False)
133 133 if edit and parent_of_self():
134 134 e_dict = {'group_parent_id':_('Cannot assign this group '
135 135 'as parent')}
136 136 raise formencode.Invalid('', value, state,
137 137 error_dict=e_dict)
138 138
139 139 old_gname = None
140 140 if edit:
141 141 old_gname = RepoGroup.get(old_data.get('group_id')).group_name
142 142
143 143 if old_gname != group_name or not edit:
144 144
145 145 # check filesystem
146 146 gr = RepoGroup.query().filter(RepoGroup.group_name == slug)\
147 147 .filter(RepoGroup.group_parent_id == group_parent_id).scalar()
148 148
149 149 if gr:
150 150 e_dict = {'group_name':_('This group already exists')}
151 151 raise formencode.Invalid('', value, state,
152 152 error_dict=e_dict)
153 153
154 154 return _ValidReposGroup
155 155
156 156 class ValidPassword(formencode.validators.FancyValidator):
157 157
158 158 def to_python(self, value, state):
159 159
160 160 if value:
161 161
162 162 if value.get('password'):
163 163 try:
164 164 value['password'] = get_crypt_password(value['password'])
165 165 except UnicodeEncodeError:
166 166 e_dict = {'password':_('Invalid characters in password')}
167 167 raise formencode.Invalid('', value, state, error_dict=e_dict)
168 168
169 169 if value.get('password_confirmation'):
170 170 try:
171 171 value['password_confirmation'] = \
172 172 get_crypt_password(value['password_confirmation'])
173 173 except UnicodeEncodeError:
174 174 e_dict = {'password_confirmation':_('Invalid characters in password')}
175 175 raise formencode.Invalid('', value, state, error_dict=e_dict)
176 176
177 177 if value.get('new_password'):
178 178 try:
179 179 value['new_password'] = \
180 180 get_crypt_password(value['new_password'])
181 181 except UnicodeEncodeError:
182 182 e_dict = {'new_password':_('Invalid characters in password')}
183 183 raise formencode.Invalid('', value, state, error_dict=e_dict)
184 184
185 185 return value
186 186
187 187 class ValidPasswordsMatch(formencode.validators.FancyValidator):
188 188
189 189 def validate_python(self, value, state):
190 190
191 191 pass_val = value.get('password') or value.get('new_password')
192 192 if pass_val != value['password_confirmation']:
193 193 e_dict = {'password_confirmation':
194 194 _('Passwords do not match')}
195 195 raise formencode.Invalid('', value, state, error_dict=e_dict)
196 196
197 197 class ValidAuth(formencode.validators.FancyValidator):
198 198 messages = {
199 199 'invalid_password':_('invalid password'),
200 200 'invalid_login':_('invalid user name'),
201 201 'disabled_account':_('Your account is disabled')
202 202 }
203 203
204 204 # error mapping
205 205 e_dict = {'username':messages['invalid_login'],
206 206 'password':messages['invalid_password']}
207 207 e_dict_disable = {'username':messages['disabled_account']}
208 208
209 209 def validate_python(self, value, state):
210 210 password = value['password']
211 211 username = value['username']
212 212 user = User.get_by_username(username)
213 213
214 214 if authenticate(username, password):
215 215 return value
216 216 else:
217 217 if user and user.active is False:
218 218 log.warning('user %s is disabled', username)
219 219 raise formencode.Invalid(self.message('disabled_account',
220 220 state=State_obj),
221 221 value, state,
222 222 error_dict=self.e_dict_disable)
223 223 else:
224 224 log.warning('user %s not authenticated', username)
225 225 raise formencode.Invalid(self.message('invalid_password',
226 226 state=State_obj), value, state,
227 227 error_dict=self.e_dict)
228 228
229 229 class ValidRepoUser(formencode.validators.FancyValidator):
230 230
231 231 def to_python(self, value, state):
232 232 try:
233 233 User.query().filter(User.active == True)\
234 234 .filter(User.username == value).one()
235 235 except Exception:
236 236 raise formencode.Invalid(_('This username is not valid'),
237 237 value, state)
238 238 return value
239 239
240 240 def ValidRepoName(edit, old_data):
241 241 class _ValidRepoName(formencode.validators.FancyValidator):
242 242 def to_python(self, value, state):
243 243
244 244 repo_name = value.get('repo_name')
245 245
246 246 slug = repo_name_slug(repo_name)
247 247 if slug in [ADMIN_PREFIX, '']:
248 248 e_dict = {'repo_name': _('This repository name is disallowed')}
249 249 raise formencode.Invalid('', value, state, error_dict=e_dict)
250 250
251 251
252 252 if value.get('repo_group'):
253 253 gr = RepoGroup.get(value.get('repo_group'))
254 254 group_path = gr.full_path
255 255 # value needs to be aware of group name in order to check
256 256 # db key This is an actual just the name to store in the
257 257 # database
258 258 repo_name_full = group_path + RepoGroup.url_sep() + repo_name
259 259
260 260 else:
261 261 group_path = ''
262 262 repo_name_full = repo_name
263 263
264 264
265 265 value['repo_name_full'] = repo_name_full
266 266 rename = old_data.get('repo_name') != repo_name_full
267 267 create = not edit
268 268 if rename or create:
269 269
270 270 if group_path != '':
271 271 if RepoModel().get_by_repo_name(repo_name_full,):
272 272 e_dict = {'repo_name':_('This repository already '
273 273 'exists in a group "%s"') %
274 274 gr.group_name}
275 275 raise formencode.Invalid('', value, state,
276 276 error_dict=e_dict)
277 277 elif RepoGroup.get_by_group_name(repo_name_full):
278 278 e_dict = {'repo_name':_('There is a group with this'
279 279 ' name already "%s"') %
280 280 repo_name_full}
281 281 raise formencode.Invalid('', value, state,
282 282 error_dict=e_dict)
283 283
284 284 elif RepoModel().get_by_repo_name(repo_name_full):
285 285 e_dict = {'repo_name':_('This repository '
286 286 'already exists')}
287 287 raise formencode.Invalid('', value, state,
288 288 error_dict=e_dict)
289 289
290 290 return value
291 291
292 292 return _ValidRepoName
293 293
294 294 def ValidForkName(*args, **kwargs):
295 295 return ValidRepoName(*args, **kwargs)
296 296
297 297
298 298 def SlugifyName():
299 299 class _SlugifyName(formencode.validators.FancyValidator):
300 300
301 301 def to_python(self, value, state):
302 302 return repo_name_slug(value)
303 303
304 304 return _SlugifyName
305 305
306 306 def ValidCloneUri():
307 307 from mercurial.httprepo import httprepository, httpsrepository
308 308 from rhodecode.lib.utils import make_ui
309 309
310 310 class _ValidCloneUri(formencode.validators.FancyValidator):
311 311
312 312 def to_python(self, value, state):
313 313 if not value:
314 314 pass
315 315 elif value.startswith('https'):
316 316 try:
317 317 httpsrepository(make_ui('db'), value).capabilities
318 318 except Exception, e:
319 319 log.error(traceback.format_exc())
320 320 raise formencode.Invalid(_('invalid clone url'), value,
321 321 state)
322 322 elif value.startswith('http'):
323 323 try:
324 324 httprepository(make_ui('db'), value).capabilities
325 325 except Exception, e:
326 326 log.error(traceback.format_exc())
327 327 raise formencode.Invalid(_('invalid clone url'), value,
328 328 state)
329 329 else:
330 330 raise formencode.Invalid(_('Invalid clone url, provide a '
331 331 'valid clone http\s url'), value,
332 332 state)
333 333 return value
334 334
335 335 return _ValidCloneUri
336 336
337 337 def ValidForkType(old_data):
338 338 class _ValidForkType(formencode.validators.FancyValidator):
339 339
340 340 def to_python(self, value, state):
341 341 if old_data['repo_type'] != value:
342 342 raise formencode.Invalid(_('Fork have to be the same '
343 343 'type as original'), value, state)
344 344
345 345 return value
346 346 return _ValidForkType
347 347
348 348 class ValidPerms(formencode.validators.FancyValidator):
349 349 messages = {'perm_new_member_name':_('This username or users group name'
350 350 ' is not valid')}
351 351
352 352 def to_python(self, value, state):
353 353 perms_update = []
354 354 perms_new = []
355 355 #build a list of permission to update and new permission to create
356 356 for k, v in value.items():
357 357 #means new added member to permissions
358 358 if k.startswith('perm_new_member'):
359 359 new_perm = value.get('perm_new_member', False)
360 360 new_member = value.get('perm_new_member_name', False)
361 361 new_type = value.get('perm_new_member_type')
362 362
363 363 if new_member and new_perm:
364 364 if (new_member, new_perm, new_type) not in perms_new:
365 365 perms_new.append((new_member, new_perm, new_type))
366 366 elif k.startswith('u_perm_') or k.startswith('g_perm_'):
367 367 member = k[7:]
368 368 t = {'u':'user',
369 369 'g':'users_group'}[k[0]]
370 370 if member == 'default':
371 371 if value['private']:
372 372 #set none for default when updating to private repo
373 373 v = 'repository.none'
374 374 perms_update.append((member, v, t))
375 375
376 376 value['perms_updates'] = perms_update
377 377 value['perms_new'] = perms_new
378 378
379 379 #update permissions
380 380 for k, v, t in perms_new:
381 381 try:
382 382 if t is 'user':
383 383 self.user_db = User.query()\
384 384 .filter(User.active == True)\
385 385 .filter(User.username == k).one()
386 386 if t is 'users_group':
387 387 self.user_db = UsersGroup.query()\
388 388 .filter(UsersGroup.users_group_active == True)\
389 389 .filter(UsersGroup.users_group_name == k).one()
390 390
391 391 except Exception:
392 392 msg = self.message('perm_new_member_name',
393 393 state=State_obj)
394 394 raise formencode.Invalid(msg, value, state,
395 395 error_dict={'perm_new_member_name':msg})
396 396 return value
397 397
398 398 class ValidSettings(formencode.validators.FancyValidator):
399 399
400 400 def to_python(self, value, state):
401 401 #settings form can't edit user
402 402 if value.has_key('user'):
403 403 del['value']['user']
404 404
405 405 return value
406 406
407 407 class ValidPath(formencode.validators.FancyValidator):
408 408 def to_python(self, value, state):
409 409
410 410 if not os.path.isdir(value):
411 411 msg = _('This is not a valid path')
412 412 raise formencode.Invalid(msg, value, state,
413 413 error_dict={'paths_root_path':msg})
414 414 return value
415 415
416 416 def UniqSystemEmail(old_data):
417 417 class _UniqSystemEmail(formencode.validators.FancyValidator):
418 418 def to_python(self, value, state):
419 419 value = value.lower()
420 420 if old_data.get('email') != value:
421 421 user = User.query().filter(User.email == value).scalar()
422 422 if user:
423 423 raise formencode.Invalid(
424 424 _("This e-mail address is already taken"),
425 425 value, state)
426 426 return value
427 427
428 428 return _UniqSystemEmail
429 429
430 430 class ValidSystemEmail(formencode.validators.FancyValidator):
431 431 def to_python(self, value, state):
432 432 value = value.lower()
433 433 user = User.query().filter(User.email == value).scalar()
434 434 if user is None:
435 435 raise formencode.Invalid(_("This e-mail address doesn't exist.") ,
436 436 value, state)
437 437
438 438 return value
439 439
440 440 class LdapLibValidator(formencode.validators.FancyValidator):
441 441
442 442 def to_python(self, value, state):
443 443
444 444 try:
445 445 import ldap
446 446 except ImportError:
447 447 raise LdapImportError
448 448 return value
449 449
450 450 class AttrLoginValidator(formencode.validators.FancyValidator):
451 451
452 452 def to_python(self, value, state):
453 453
454 454 if not value or not isinstance(value, (str, unicode)):
455 455 raise formencode.Invalid(_("The LDAP Login attribute of the CN "
456 456 "must be specified - this is the name "
457 457 "of the attribute that is equivalent "
458 458 "to 'username'"),
459 459 value, state)
460 460
461 461 return value
462 462
463 463 #===============================================================================
464 464 # FORMS
465 465 #===============================================================================
466 466 class LoginForm(formencode.Schema):
467 467 allow_extra_fields = True
468 468 filter_extra_fields = True
469 469 username = UnicodeString(
470 470 strip=True,
471 471 min=1,
472 472 not_empty=True,
473 473 messages={
474 474 'empty':_('Please enter a login'),
475 475 'tooShort':_('Enter a value %(min)i characters long or more')}
476 476 )
477 477
478 478 password = UnicodeString(
479 479 strip=True,
480 480 min=3,
481 481 not_empty=True,
482 482 messages={
483 483 'empty':_('Please enter a password'),
484 484 'tooShort':_('Enter %(min)i characters or more')}
485 485 )
486 486
487 487 chained_validators = [ValidAuth]
488 488
489 489 def UserForm(edit=False, old_data={}):
490 490 class _UserForm(formencode.Schema):
491 491 allow_extra_fields = True
492 492 filter_extra_fields = True
493 493 username = All(UnicodeString(strip=True, min=1, not_empty=True),
494 494 ValidUsername(edit, old_data))
495 495 if edit:
496 496 new_password = All(UnicodeString(strip=True, min=6, not_empty=False))
497 497 password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=False))
498 498 admin = StringBoolean(if_missing=False)
499 499 else:
500 500 password = All(UnicodeString(strip=True, min=6, not_empty=True))
501 501 password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=False))
502 502
503 503 active = StringBoolean(if_missing=False)
504 504 name = UnicodeString(strip=True, min=1, not_empty=True)
505 505 lastname = UnicodeString(strip=True, min=1, not_empty=True)
506 506 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
507 507
508 508 chained_validators = [ValidPasswordsMatch, ValidPassword]
509 509
510 510 return _UserForm
511 511
512 512
513 513 def UsersGroupForm(edit=False, old_data={}, available_members=[]):
514 514 class _UsersGroupForm(formencode.Schema):
515 515 allow_extra_fields = True
516 516 filter_extra_fields = True
517 517
518 518 users_group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
519 519 ValidUsersGroup(edit, old_data))
520 520
521 521 users_group_active = StringBoolean(if_missing=False)
522 522
523 523 if edit:
524 524 users_group_members = OneOf(available_members, hideList=False,
525 525 testValueList=True,
526 526 if_missing=None, not_empty=False)
527 527
528 528 return _UsersGroupForm
529 529
530 530 def ReposGroupForm(edit=False, old_data={}, available_groups=[]):
531 531 class _ReposGroupForm(formencode.Schema):
532 532 allow_extra_fields = True
533 533 filter_extra_fields = True
534 534
535 535 group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
536 536 SlugifyName())
537 537 group_description = UnicodeString(strip=True, min=1,
538 538 not_empty=True)
539 539 group_parent_id = OneOf(available_groups, hideList=False,
540 540 testValueList=True,
541 541 if_missing=None, not_empty=False)
542 542
543 543 chained_validators = [ValidReposGroup(edit, old_data)]
544 544
545 545 return _ReposGroupForm
546 546
547 547 def RegisterForm(edit=False, old_data={}):
548 548 class _RegisterForm(formencode.Schema):
549 549 allow_extra_fields = True
550 550 filter_extra_fields = True
551 551 username = All(ValidUsername(edit, old_data),
552 552 UnicodeString(strip=True, min=1, not_empty=True))
553 553 password = All(UnicodeString(strip=True, min=6, not_empty=True))
554 554 password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=True))
555 555 active = StringBoolean(if_missing=False)
556 556 name = UnicodeString(strip=True, min=1, not_empty=True)
557 557 lastname = UnicodeString(strip=True, min=1, not_empty=True)
558 558 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
559 559
560 560 chained_validators = [ValidPasswordsMatch, ValidPassword]
561 561
562 562 return _RegisterForm
563 563
564 564 def PasswordResetForm():
565 565 class _PasswordResetForm(formencode.Schema):
566 566 allow_extra_fields = True
567 567 filter_extra_fields = True
568 568 email = All(ValidSystemEmail(), Email(not_empty=True))
569 569 return _PasswordResetForm
570 570
571 571 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
572 572 repo_groups=[]):
573 573 class _RepoForm(formencode.Schema):
574 574 allow_extra_fields = True
575 575 filter_extra_fields = False
576 576 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
577 577 SlugifyName())
578 578 clone_uri = All(UnicodeString(strip=True, min=1, not_empty=False),
579 579 ValidCloneUri()())
580 580 repo_group = OneOf(repo_groups, hideList=True)
581 581 repo_type = OneOf(supported_backends)
582 582 description = UnicodeString(strip=True, min=1, not_empty=True)
583 583 private = StringBoolean(if_missing=False)
584 584 enable_statistics = StringBoolean(if_missing=False)
585 585 enable_downloads = StringBoolean(if_missing=False)
586 586
587 587 if edit:
588 588 #this is repo owner
589 589 user = All(UnicodeString(not_empty=True), ValidRepoUser)
590 590
591 591 chained_validators = [ValidRepoName(edit, old_data), ValidPerms]
592 592 return _RepoForm
593 593
594 594 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
595 595 repo_groups=[]):
596 596 class _RepoForkForm(formencode.Schema):
597 597 allow_extra_fields = True
598 598 filter_extra_fields = False
599 599 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
600 600 SlugifyName())
601 601 repo_group = OneOf(repo_groups, hideList=True)
602 602 repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
603 603 description = UnicodeString(strip=True, min=1, not_empty=True)
604 604 private = StringBoolean(if_missing=False)
605 605 copy_permissions = StringBoolean(if_missing=False)
606 update_after_clone = StringBoolean(if_missing=False)
606 607 fork_parent_id = UnicodeString()
607 608 chained_validators = [ValidForkName(edit, old_data)]
608 609
609 610 return _RepoForkForm
610 611
611 612 def RepoSettingsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
612 613 repo_groups=[]):
613 614 class _RepoForm(formencode.Schema):
614 615 allow_extra_fields = True
615 616 filter_extra_fields = False
616 617 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
617 618 SlugifyName())
618 619 description = UnicodeString(strip=True, min=1, not_empty=True)
619 620 repo_group = OneOf(repo_groups, hideList=True)
620 621 private = StringBoolean(if_missing=False)
621 622
622 623 chained_validators = [ValidRepoName(edit, old_data), ValidPerms,
623 624 ValidSettings]
624 625 return _RepoForm
625 626
626 627
627 628 def ApplicationSettingsForm():
628 629 class _ApplicationSettingsForm(formencode.Schema):
629 630 allow_extra_fields = True
630 631 filter_extra_fields = False
631 632 rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
632 633 rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
633 634 rhodecode_ga_code = UnicodeString(strip=True, min=1, not_empty=False)
634 635
635 636 return _ApplicationSettingsForm
636 637
637 638 def ApplicationUiSettingsForm():
638 639 class _ApplicationUiSettingsForm(formencode.Schema):
639 640 allow_extra_fields = True
640 641 filter_extra_fields = False
641 642 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
642 643 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
643 644 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
644 645 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
645 646 hooks_pretxnchangegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
646 647 hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
647 648
648 649 return _ApplicationUiSettingsForm
649 650
650 651 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
651 652 class _DefaultPermissionsForm(formencode.Schema):
652 653 allow_extra_fields = True
653 654 filter_extra_fields = True
654 655 overwrite_default = StringBoolean(if_missing=False)
655 656 anonymous = OneOf(['True', 'False'], if_missing=False)
656 657 default_perm = OneOf(perms_choices)
657 658 default_register = OneOf(register_choices)
658 659 default_create = OneOf(create_choices)
659 660
660 661 return _DefaultPermissionsForm
661 662
662 663
663 664 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices, tls_kind_choices):
664 665 class _LdapSettingsForm(formencode.Schema):
665 666 allow_extra_fields = True
666 667 filter_extra_fields = True
667 668 pre_validators = [LdapLibValidator]
668 669 ldap_active = StringBoolean(if_missing=False)
669 670 ldap_host = UnicodeString(strip=True,)
670 671 ldap_port = Number(strip=True,)
671 672 ldap_tls_kind = OneOf(tls_kind_choices)
672 673 ldap_tls_reqcert = OneOf(tls_reqcert_choices)
673 674 ldap_dn_user = UnicodeString(strip=True,)
674 675 ldap_dn_pass = UnicodeString(strip=True,)
675 676 ldap_base_dn = UnicodeString(strip=True,)
676 677 ldap_filter = UnicodeString(strip=True,)
677 678 ldap_search_scope = OneOf(search_scope_choices)
678 679 ldap_attr_login = All(AttrLoginValidator, UnicodeString(strip=True,))
679 680 ldap_attr_firstname = UnicodeString(strip=True,)
680 681 ldap_attr_lastname = UnicodeString(strip=True,)
681 682 ldap_attr_email = UnicodeString(strip=True,)
682 683
683 684 return _LdapSettingsForm
@@ -1,78 +1,86 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3
4 4 <%def name="title()">
5 5 ${c.repo_name} ${_('Fork')} - ${c.rhodecode_name}
6 6 </%def>
7 7
8 8 <%def name="breadcrumbs_links()">
9 9 ${h.link_to(u'Home',h.url('/'))}
10 10 &raquo;
11 11 ${h.link_to(c.repo_info.repo_name,h.url('summary_home',repo_name=c.repo_info.repo_name))}
12 12 &raquo;
13 13 ${_('fork')}
14 14 </%def>
15 15
16 16 <%def name="page_nav()">
17 17 ${self.menu('')}
18 18 </%def>
19 19 <%def name="main()">
20 20 <div class="box">
21 21 <!-- box / title -->
22 22 <div class="title">
23 23 ${self.breadcrumbs()}
24 24 </div>
25 25 ${h.form(url('repo_fork_create_home',repo_name=c.repo_info.repo_name))}
26 26 <div class="form">
27 27 <!-- fields -->
28 28 <div class="fields">
29 29 <div class="field">
30 30 <div class="label">
31 31 <label for="repo_name">${_('Fork name')}:</label>
32 32 </div>
33 33 <div class="input">
34 34 ${h.text('repo_name',class_="small")}
35 35 ${h.hidden('repo_type',c.repo_info.repo_type)}
36 36 ${h.hidden('fork_parent_id',c.repo_info.repo_id)}
37 37 </div>
38 38 </div>
39 39 <div class="field">
40 40 <div class="label">
41 41 <label for="repo_group">${_('Repository group')}:</label>
42 42 </div>
43 43 <div class="input">
44 44 ${h.select('repo_group','',c.repo_groups,class_="medium")}
45 45 </div>
46 46 </div>
47 47 <div class="field">
48 48 <div class="label label-textarea">
49 49 <label for="description">${_('Description')}:</label>
50 50 </div>
51 51 <div class="textarea text-area editor">
52 52 ${h.textarea('description',cols=23,rows=5)}
53 53 </div>
54 54 </div>
55 55 <div class="field">
56 56 <div class="label label-checkbox">
57 57 <label for="private">${_('Private')}:</label>
58 58 </div>
59 59 <div class="checkboxes">
60 60 ${h.checkbox('private',value="True")}
61 61 </div>
62 62 </div>
63 63 <div class="field">
64 64 <div class="label label-checkbox">
65 65 <label for="private">${_('Copy permissions')}:</label>
66 66 </div>
67 67 <div class="checkboxes">
68 68 ${h.checkbox('copy_permissions',value="True")}
69 69 </div>
70 </div>
70 </div>
71 <div class="field">
72 <div class="label label-checkbox">
73 <label for="private">${_('Update after clone')}:</label>
74 </div>
75 <div class="checkboxes">
76 ${h.checkbox('update_after_clone',value="True")}
77 </div>
78 </div>
71 79 <div class="buttons">
72 80 ${h.submit('',_('fork this repository'),class_="ui-button")}
73 81 </div>
74 82 </div>
75 83 </div>
76 84 ${h.end_form()}
77 85 </div>
78 86 </%def>
General Comments 0
You need to be logged in to leave comments. Login now