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