##// END OF EJS Templates
Fixed some issues with edit form...
marcink -
r3089:4cc9bb83 beta
parent child Browse files
Show More
@@ -1,511 +1,520 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.admin.repos
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Repositories controller for RhodeCode
7 7
8 8 :created_on: Apr 7, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27 import traceback
28 28 import formencode
29 29 from formencode import htmlfill
30 30
31 31 from webob.exc import HTTPInternalServerError
32 32 from pylons import request, session, tmpl_context as c, url
33 33 from pylons.controllers.util import redirect
34 34 from pylons.i18n.translation import _
35 35 from sqlalchemy.exc import IntegrityError
36 36
37 37 import rhodecode
38 38 from rhodecode.lib import helpers as h
39 39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
40 40 HasPermissionAnyDecorator, HasRepoPermissionAllDecorator
41 41 from rhodecode.lib.base import BaseController, render
42 42 from rhodecode.lib.utils import invalidate_cache, action_logger, repo_name_slug
43 43 from rhodecode.lib.helpers import get_token
44 44 from rhodecode.model.meta import Session
45 45 from rhodecode.model.db import User, Repository, UserFollowing, RepoGroup,\
46 46 RhodeCodeSetting
47 47 from rhodecode.model.forms import RepoForm
48 48 from rhodecode.model.scm import ScmModel
49 49 from rhodecode.model.repo import RepoModel
50 50 from rhodecode.lib.compat import json
51 51 from sqlalchemy.sql.expression import func
52 52
53 53 log = logging.getLogger(__name__)
54 54
55 55
56 56 class ReposController(BaseController):
57 57 """
58 58 REST Controller styled on the Atom Publishing Protocol"""
59 59 # To properly map this controller, ensure your config/routing.py
60 60 # file has a resource setup:
61 61 # map.resource('repo', 'repos')
62 62
63 63 @LoginRequired()
64 64 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
65 65 def __before__(self):
66 66 c.admin_user = session.get('admin_user')
67 67 c.admin_username = session.get('admin_username')
68 68 super(ReposController, self).__before__()
69 69
70 70 def __load_defaults(self):
71 71 c.repo_groups = RepoGroup.groups_choices(check_perms=True)
72 72 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
73 73
74 74 repo_model = RepoModel()
75 75 c.users_array = repo_model.get_users_js()
76 76 c.users_groups_array = repo_model.get_users_groups_js()
77 77 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
78 78 c.landing_revs_choices = choices
79 79
80 80 def __load_data(self, repo_name=None):
81 81 """
82 82 Load defaults settings for edit, and update
83 83
84 84 :param repo_name:
85 85 """
86 86 self.__load_defaults()
87 87
88 88 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
89 89 repo = db_repo.scm_instance
90 90
91 91 if c.repo_info is None:
92 92 h.flash(_('%s repository is not mapped to db perhaps'
93 93 ' it was created or renamed from the filesystem'
94 94 ' please run the application again'
95 95 ' in order to rescan repositories') % repo_name,
96 96 category='error')
97 97
98 98 return redirect(url('repos'))
99 99
100 ##override defaults for exact repo info here git/hg etc
100 101 choices, c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info)
101 102 c.landing_revs_choices = choices
102 103
103 104 c.default_user_id = User.get_by_username('default').user_id
104 105 c.in_public_journal = UserFollowing.query()\
105 106 .filter(UserFollowing.user_id == c.default_user_id)\
106 107 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
107 108
108 109 if c.repo_info.stats:
109 110 # this is on what revision we ended up so we add +1 for count
110 111 last_rev = c.repo_info.stats.stat_on_revision + 1
111 112 else:
112 113 last_rev = 0
113 114 c.stats_revision = last_rev
114 115
115 116 c.repo_last_rev = repo.count() if repo.revisions else 0
116 117
117 118 if last_rev == 0 or c.repo_last_rev == 0:
118 119 c.stats_percentage = 0
119 120 else:
120 121 c.stats_percentage = '%.2f' % ((float((last_rev)) /
121 122 c.repo_last_rev) * 100)
122 123
123 124 defaults = RepoModel()._get_defaults(repo_name)
124 125
125 126 c.repos_list = [('', _('--REMOVE FORK--'))]
126 127 c.repos_list += [(x.repo_id, x.repo_name) for x in
127 128 Repository.query().order_by(Repository.repo_name).all()
128 129 if x.repo_id != c.repo_info.repo_id]
129 130
130 131 defaults['id_fork_of'] = db_repo.fork.repo_id if db_repo.fork else ''
131 132 return defaults
132 133
133 134 @HasPermissionAllDecorator('hg.admin')
134 135 def index(self, format='html'):
135 136 """GET /repos: All items in the collection"""
136 137 # url('repos')
137 138
138 139 c.repos_list = Repository.query()\
139 140 .order_by(func.lower(Repository.repo_name))\
140 141 .all()
141 142
142 143 repos_data = []
143 144 total_records = len(c.repos_list)
144 145
145 146 _tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
146 147 template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
147 148
148 149 quick_menu = lambda repo_name: (template.get_def("quick_menu")
149 150 .render(repo_name, _=_, h=h, c=c))
150 151 repo_lnk = lambda name, rtype, private, fork_of: (
151 152 template.get_def("repo_name")
152 153 .render(name, rtype, private, fork_of, short_name=False,
153 154 admin=True, _=_, h=h, c=c))
154 155
155 156 repo_actions = lambda repo_name: (template.get_def("repo_actions")
156 157 .render(repo_name, _=_, h=h, c=c))
157 158
158 159 for repo in c.repos_list:
159 160 repos_data.append({
160 161 "menu": quick_menu(repo.repo_name),
161 162 "raw_name": repo.repo_name.lower(),
162 163 "name": repo_lnk(repo.repo_name, repo.repo_type,
163 164 repo.private, repo.fork),
164 165 "desc": repo.description,
165 166 "owner": repo.user.username,
166 167 "action": repo_actions(repo.repo_name),
167 168 })
168 169
169 170 c.data = json.dumps({
170 171 "totalRecords": total_records,
171 172 "startIndex": 0,
172 173 "sort": "name",
173 174 "dir": "asc",
174 175 "records": repos_data
175 176 })
176 177
177 178 return render('admin/repos/repos.html')
178 179
179 180 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
180 181 def create(self):
181 182 """
182 183 POST /repos: Create a new item"""
183 184 # url('repos')
184 185
185 186 self.__load_defaults()
186 187 form_result = {}
187 188 try:
188 189 form_result = RepoForm(repo_groups=c.repo_groups_choices,
189 190 landing_revs=c.landing_revs_choices)()\
190 191 .to_python(dict(request.POST))
191 192 new_repo = RepoModel().create(form_result,
192 193 self.rhodecode_user.user_id)
193 194 if form_result['clone_uri']:
194 195 h.flash(_('created repository %s from %s') \
195 196 % (form_result['repo_name'], form_result['clone_uri']),
196 197 category='success')
197 198 else:
198 199 h.flash(_('created repository %s') % form_result['repo_name'],
199 200 category='success')
200 201
201 202 if request.POST.get('user_created'):
202 203 # created by regular non admin user
203 204 action_logger(self.rhodecode_user, 'user_created_repo',
204 205 form_result['repo_name_full'], self.ip_addr,
205 206 self.sa)
206 207 else:
207 208 action_logger(self.rhodecode_user, 'admin_created_repo',
208 209 form_result['repo_name_full'], self.ip_addr,
209 210 self.sa)
210 211 Session().commit()
211 212 except formencode.Invalid, errors:
212 213
213 214 c.new_repo = errors.value['repo_name']
214 215
215 216 if request.POST.get('user_created'):
216 217 r = render('admin/repos/repo_add_create_repository.html')
217 218 else:
218 219 r = render('admin/repos/repo_add.html')
219 220
220 221 return htmlfill.render(
221 222 r,
222 223 defaults=errors.value,
223 224 errors=errors.error_dict or {},
224 225 prefix_error=False,
225 226 encoding="UTF-8")
226 227
227 228 except Exception:
228 229 log.error(traceback.format_exc())
229 230 msg = _('error occurred during creation of repository %s') \
230 231 % form_result.get('repo_name')
231 232 h.flash(msg, category='error')
232 233 return redirect(url('repos'))
233 234 #redirect to our new repo !
234 235 return redirect(url('summary_home', repo_name=new_repo.repo_name))
235 236
236 237 @HasPermissionAllDecorator('hg.admin')
237 238 def new(self, format='html'):
238 239 """GET /repos/new: Form to create a new item"""
239 240 new_repo = request.GET.get('repo', '')
240 241 c.new_repo = repo_name_slug(new_repo)
241 242 self.__load_defaults()
242 return render('admin/repos/repo_add.html')
243 ## apply the defaults from defaults page
244 defaults = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
245 return htmlfill.render(
246 render('admin/repos/repo_add.html'),
247 defaults=defaults,
248 errors={},
249 prefix_error=False,
250 encoding="UTF-8"
251 )
243 252
244 253 @HasPermissionAllDecorator('hg.admin')
245 254 def update(self, repo_name):
246 255 """
247 256 PUT /repos/repo_name: Update an existing item"""
248 257 # Forms posted to this method should contain a hidden field:
249 258 # <input type="hidden" name="_method" value="PUT" />
250 259 # Or using helpers:
251 260 # h.form(url('repo', repo_name=ID),
252 261 # method='put')
253 262 # url('repo', repo_name=ID)
254 263 self.__load_defaults()
255 264 repo_model = RepoModel()
256 265 changed_name = repo_name
257 266 #override the choices with extracted revisions !
258 267 choices, c.landing_revs = ScmModel().get_repo_landing_revs(repo_name)
259 268 c.landing_revs_choices = choices
260 269
261 270 _form = RepoForm(edit=True, old_data={'repo_name': repo_name},
262 271 repo_groups=c.repo_groups_choices,
263 272 landing_revs=c.landing_revs_choices)()
264 273 try:
265 274 form_result = _form.to_python(dict(request.POST))
266 repo = repo_model.update(repo_name, form_result)
275 repo = repo_model.update(repo_name, **form_result)
267 276 invalidate_cache('get_repo_cached_%s' % repo_name)
268 277 h.flash(_('Repository %s updated successfully') % repo_name,
269 278 category='success')
270 279 changed_name = repo.repo_name
271 280 action_logger(self.rhodecode_user, 'admin_updated_repo',
272 281 changed_name, self.ip_addr, self.sa)
273 282 Session().commit()
274 283 except formencode.Invalid, errors:
275 284 defaults = self.__load_data(repo_name)
276 285 defaults.update(errors.value)
277 286 return htmlfill.render(
278 287 render('admin/repos/repo_edit.html'),
279 288 defaults=defaults,
280 289 errors=errors.error_dict or {},
281 290 prefix_error=False,
282 291 encoding="UTF-8")
283 292
284 293 except Exception:
285 294 log.error(traceback.format_exc())
286 295 h.flash(_('error occurred during update of repository %s') \
287 296 % repo_name, category='error')
288 297 return redirect(url('edit_repo', repo_name=changed_name))
289 298
290 299 @HasPermissionAllDecorator('hg.admin')
291 300 def delete(self, repo_name):
292 301 """
293 302 DELETE /repos/repo_name: Delete an existing item"""
294 303 # Forms posted to this method should contain a hidden field:
295 304 # <input type="hidden" name="_method" value="DELETE" />
296 305 # Or using helpers:
297 306 # h.form(url('repo', repo_name=ID),
298 307 # method='delete')
299 308 # url('repo', repo_name=ID)
300 309
301 310 repo_model = RepoModel()
302 311 repo = repo_model.get_by_repo_name(repo_name)
303 312 if not repo:
304 313 h.flash(_('%s repository is not mapped to db perhaps'
305 314 ' it was moved or renamed from the filesystem'
306 315 ' please run the application again'
307 316 ' in order to rescan repositories') % repo_name,
308 317 category='error')
309 318
310 319 return redirect(url('repos'))
311 320 try:
312 321 action_logger(self.rhodecode_user, 'admin_deleted_repo',
313 322 repo_name, self.ip_addr, self.sa)
314 323 repo_model.delete(repo)
315 324 invalidate_cache('get_repo_cached_%s' % repo_name)
316 325 h.flash(_('deleted repository %s') % repo_name, category='success')
317 326 Session().commit()
318 327 except IntegrityError, e:
319 328 if e.message.find('repositories_fork_id_fkey') != -1:
320 329 log.error(traceback.format_exc())
321 330 h.flash(_('Cannot delete %s it still contains attached '
322 331 'forks') % repo_name,
323 332 category='warning')
324 333 else:
325 334 log.error(traceback.format_exc())
326 335 h.flash(_('An error occurred during '
327 336 'deletion of %s') % repo_name,
328 337 category='error')
329 338
330 339 except Exception, e:
331 340 log.error(traceback.format_exc())
332 341 h.flash(_('An error occurred during deletion of %s') % repo_name,
333 342 category='error')
334 343
335 344 return redirect(url('repos'))
336 345
337 346 @HasRepoPermissionAllDecorator('repository.admin')
338 347 def delete_perm_user(self, repo_name):
339 348 """
340 349 DELETE an existing repository permission user
341 350
342 351 :param repo_name:
343 352 """
344 353 try:
345 354 RepoModel().revoke_user_permission(repo=repo_name,
346 355 user=request.POST['user_id'])
347 356 Session().commit()
348 357 except Exception:
349 358 log.error(traceback.format_exc())
350 359 h.flash(_('An error occurred during deletion of repository user'),
351 360 category='error')
352 361 raise HTTPInternalServerError()
353 362
354 363 @HasRepoPermissionAllDecorator('repository.admin')
355 364 def delete_perm_users_group(self, repo_name):
356 365 """
357 366 DELETE an existing repository permission users group
358 367
359 368 :param repo_name:
360 369 """
361 370
362 371 try:
363 372 RepoModel().revoke_users_group_permission(
364 373 repo=repo_name, group_name=request.POST['users_group_id']
365 374 )
366 375 Session().commit()
367 376 except Exception:
368 377 log.error(traceback.format_exc())
369 378 h.flash(_('An error occurred during deletion of repository'
370 379 ' users groups'),
371 380 category='error')
372 381 raise HTTPInternalServerError()
373 382
374 383 @HasPermissionAllDecorator('hg.admin')
375 384 def repo_stats(self, repo_name):
376 385 """
377 386 DELETE an existing repository statistics
378 387
379 388 :param repo_name:
380 389 """
381 390
382 391 try:
383 392 RepoModel().delete_stats(repo_name)
384 393 Session().commit()
385 394 except Exception, e:
386 395 log.error(traceback.format_exc())
387 396 h.flash(_('An error occurred during deletion of repository stats'),
388 397 category='error')
389 398 return redirect(url('edit_repo', repo_name=repo_name))
390 399
391 400 @HasPermissionAllDecorator('hg.admin')
392 401 def repo_cache(self, repo_name):
393 402 """
394 403 INVALIDATE existing repository cache
395 404
396 405 :param repo_name:
397 406 """
398 407
399 408 try:
400 409 ScmModel().mark_for_invalidation(repo_name)
401 410 Session().commit()
402 411 except Exception, e:
403 412 log.error(traceback.format_exc())
404 413 h.flash(_('An error occurred during cache invalidation'),
405 414 category='error')
406 415 return redirect(url('edit_repo', repo_name=repo_name))
407 416
408 417 @HasPermissionAllDecorator('hg.admin')
409 418 def repo_locking(self, repo_name):
410 419 """
411 420 Unlock repository when it is locked !
412 421
413 422 :param repo_name:
414 423 """
415 424
416 425 try:
417 426 repo = Repository.get_by_repo_name(repo_name)
418 427 if request.POST.get('set_lock'):
419 428 Repository.lock(repo, c.rhodecode_user.user_id)
420 429 elif request.POST.get('set_unlock'):
421 430 Repository.unlock(repo)
422 431 except Exception, e:
423 432 log.error(traceback.format_exc())
424 433 h.flash(_('An error occurred during unlocking'),
425 434 category='error')
426 435 return redirect(url('edit_repo', repo_name=repo_name))
427 436
428 437 @HasPermissionAllDecorator('hg.admin')
429 438 def repo_public_journal(self, repo_name):
430 439 """
431 440 Set's this repository to be visible in public journal,
432 441 in other words assing default user to follow this repo
433 442
434 443 :param repo_name:
435 444 """
436 445
437 446 cur_token = request.POST.get('auth_token')
438 447 token = get_token()
439 448 if cur_token == token:
440 449 try:
441 450 repo_id = Repository.get_by_repo_name(repo_name).repo_id
442 451 user_id = User.get_by_username('default').user_id
443 452 self.scm_model.toggle_following_repo(repo_id, user_id)
444 453 h.flash(_('Updated repository visibility in public journal'),
445 454 category='success')
446 455 Session().commit()
447 456 except:
448 457 h.flash(_('An error occurred during setting this'
449 458 ' repository in public journal'),
450 459 category='error')
451 460
452 461 else:
453 462 h.flash(_('Token mismatch'), category='error')
454 463 return redirect(url('edit_repo', repo_name=repo_name))
455 464
456 465 @HasPermissionAllDecorator('hg.admin')
457 466 def repo_pull(self, repo_name):
458 467 """
459 468 Runs task to update given repository with remote changes,
460 469 ie. make pull on remote location
461 470
462 471 :param repo_name:
463 472 """
464 473 try:
465 474 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
466 475 h.flash(_('Pulled from remote location'), category='success')
467 476 except Exception, e:
468 477 h.flash(_('An error occurred during pull from remote location'),
469 478 category='error')
470 479
471 480 return redirect(url('edit_repo', repo_name=repo_name))
472 481
473 482 @HasPermissionAllDecorator('hg.admin')
474 483 def repo_as_fork(self, repo_name):
475 484 """
476 485 Mark given repository as a fork of another
477 486
478 487 :param repo_name:
479 488 """
480 489 try:
481 490 fork_id = request.POST.get('id_fork_of')
482 491 repo = ScmModel().mark_as_fork(repo_name, fork_id,
483 492 self.rhodecode_user.username)
484 493 fork = repo.fork.repo_name if repo.fork else _('Nothing')
485 494 Session().commit()
486 495 h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork),
487 496 category='success')
488 497 except Exception, e:
489 498 log.error(traceback.format_exc())
490 499 h.flash(_('An error occurred during this operation'),
491 500 category='error')
492 501
493 502 return redirect(url('edit_repo', repo_name=repo_name))
494 503
495 504 @HasPermissionAllDecorator('hg.admin')
496 505 def show(self, repo_name, format='html'):
497 506 """GET /repos/repo_name: Show a specific item"""
498 507 # url('repo', repo_name=ID)
499 508
500 509 @HasPermissionAllDecorator('hg.admin')
501 510 def edit(self, repo_name, format='html'):
502 511 """GET /repos/repo_name/edit: Form to edit an existing item"""
503 512 # url('edit_repo', repo_name=ID)
504 513 defaults = self.__load_data(repo_name)
505 514
506 515 return htmlfill.render(
507 516 render('admin/repos/repo_edit.html'),
508 517 defaults=defaults,
509 518 encoding="UTF-8",
510 519 force_defaults=False
511 520 )
@@ -1,190 +1,206 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.settings
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Settings controller for rhodecode
7 7
8 8 :created_on: Jun 30, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27 import traceback
28 28 import formencode
29 29
30 30 from formencode import htmlfill
31 31
32 32 from pylons import tmpl_context as c, request, url
33 33 from pylons.controllers.util import redirect
34 34 from pylons.i18n.translation import _
35 35
36 36 import rhodecode.lib.helpers as h
37 37
38 38 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator,\
39 39 HasRepoPermissionAnyDecorator
40 40 from rhodecode.lib.base import BaseRepoController, render
41 41 from rhodecode.lib.utils import invalidate_cache, action_logger
42 42
43 43 from rhodecode.model.forms import RepoSettingsForm
44 44 from rhodecode.model.repo import RepoModel
45 45 from rhodecode.model.db import RepoGroup, Repository
46 46 from rhodecode.model.meta import Session
47 47 from rhodecode.model.scm import ScmModel
48 48
49 49 log = logging.getLogger(__name__)
50 50
51 51
52 52 class SettingsController(BaseRepoController):
53 53
54 54 @LoginRequired()
55 55 def __before__(self):
56 56 super(SettingsController, self).__before__()
57 57
58 58 def __load_defaults(self):
59 59 c.repo_groups = RepoGroup.groups_choices(check_perms=True)
60 60 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
61 61
62 62 repo_model = RepoModel()
63 63 c.users_array = repo_model.get_users_js()
64 64 c.users_groups_array = repo_model.get_users_groups_js()
65 65 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
66 66 c.landing_revs_choices = choices
67 67
68 @HasRepoPermissionAllDecorator('repository.admin')
69 def index(self, repo_name):
70 repo_model = RepoModel()
71 c.repo_info = repo = repo_model.get_by_repo_name(repo_name)
72 if not repo:
68 def __load_data(self, repo_name=None):
69 """
70 Load defaults settings for edit, and update
71
72 :param repo_name:
73 """
74 self.__load_defaults()
75
76 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
77 repo = db_repo.scm_instance
78
79 if c.repo_info is None:
73 80 h.flash(_('%s repository is not mapped to db perhaps'
74 ' it was created or renamed from the file system'
81 ' it was created or renamed from the filesystem'
75 82 ' please run the application again'
76 83 ' in order to rescan repositories') % repo_name,
77 84 category='error')
78 85
79 86 return redirect(url('home'))
80 87
81 self.__load_defaults()
88 ##override defaults for exact repo info here git/hg etc
89 choices, c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info)
90 c.landing_revs_choices = choices
82 91
83 92 defaults = RepoModel()._get_defaults(repo_name)
84 93
94 return defaults
95
96 @HasRepoPermissionAllDecorator('repository.admin')
97 def index(self, repo_name):
98 defaults = self.__load_data(repo_name)
99
85 100 return htmlfill.render(
86 101 render('settings/repo_settings.html'),
87 102 defaults=defaults,
88 103 encoding="UTF-8",
89 104 force_defaults=False
90 105 )
91 106
92 107 @HasRepoPermissionAllDecorator('repository.admin')
93 108 def update(self, repo_name):
109 self.__load_defaults()
94 110 repo_model = RepoModel()
95 111 changed_name = repo_name
96
97 self.__load_defaults()
112 #override the choices with extracted revisions !
113 choices, c.landing_revs = ScmModel().get_repo_landing_revs(repo_name)
114 c.landing_revs_choices = choices
98 115
99 116 _form = RepoSettingsForm(edit=True,
100 117 old_data={'repo_name': repo_name},
101 118 repo_groups=c.repo_groups_choices,
102 119 landing_revs=c.landing_revs_choices)()
103 120 try:
104 121 form_result = _form.to_python(dict(request.POST))
105
106 repo_model.update(repo_name, form_result)
122 repo_model.update(repo_name, **form_result)
107 123 invalidate_cache('get_repo_cached_%s' % repo_name)
108 124 h.flash(_('Repository %s updated successfully') % repo_name,
109 125 category='success')
110 126 changed_name = form_result['repo_name_full']
111 127 action_logger(self.rhodecode_user, 'user_updated_repo',
112 128 changed_name, self.ip_addr, self.sa)
113 129 Session().commit()
114 130 except formencode.Invalid, errors:
115 c.repo_info = repo_model.get_by_repo_name(repo_name)
116 c.users_array = repo_model.get_users_js()
117 errors.value.update({'user': c.repo_info.user.username})
131 defaults = self.__load_data(repo_name)
132 defaults.update(errors.value)
118 133 return htmlfill.render(
119 134 render('settings/repo_settings.html'),
120 135 defaults=errors.value,
121 136 errors=errors.error_dict or {},
122 137 prefix_error=False,
123 138 encoding="UTF-8")
139
124 140 except Exception:
125 141 log.error(traceback.format_exc())
126 142 h.flash(_('error occurred during update of repository %s') \
127 143 % repo_name, category='error')
128 144
129 145 return redirect(url('repo_settings_home', repo_name=changed_name))
130 146
131 147 @HasRepoPermissionAllDecorator('repository.admin')
132 148 def delete(self, repo_name):
133 149 """DELETE /repos/repo_name: Delete an existing item"""
134 150 # Forms posted to this method should contain a hidden field:
135 151 # <input type="hidden" name="_method" value="DELETE" />
136 152 # Or using helpers:
137 153 # h.form(url('repo_settings_delete', repo_name=ID),
138 154 # method='delete')
139 155 # url('repo_settings_delete', repo_name=ID)
140 156
141 157 repo_model = RepoModel()
142 158 repo = repo_model.get_by_repo_name(repo_name)
143 159 if not repo:
144 160 h.flash(_('%s repository is not mapped to db perhaps'
145 161 ' it was moved or renamed from the filesystem'
146 162 ' please run the application again'
147 163 ' in order to rescan repositories') % repo_name,
148 164 category='error')
149 165
150 166 return redirect(url('home'))
151 167 try:
152 168 action_logger(self.rhodecode_user, 'user_deleted_repo',
153 169 repo_name, self.ip_addr, self.sa)
154 170 repo_model.delete(repo)
155 171 invalidate_cache('get_repo_cached_%s' % repo_name)
156 172 h.flash(_('deleted repository %s') % repo_name, category='success')
157 173 Session().commit()
158 174 except Exception:
159 175 log.error(traceback.format_exc())
160 176 h.flash(_('An error occurred during deletion of %s') % repo_name,
161 177 category='error')
162 178
163 179 return redirect(url('admin_settings_my_account', anchor='my'))
164 180
165 181 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
166 182 def toggle_locking(self, repo_name):
167 183 """
168 184 Toggle locking of repository by simple GET call to url
169 185
170 186 :param repo_name:
171 187 """
172 188
173 189 try:
174 190 repo = Repository.get_by_repo_name(repo_name)
175 191
176 192 if repo.enable_locking:
177 193 if repo.locked[0]:
178 194 Repository.unlock(repo)
179 195 action = _('unlocked')
180 196 else:
181 197 Repository.lock(repo, c.rhodecode_user.user_id)
182 198 action = _('locked')
183 199
184 200 h.flash(_('Repository has been %s') % action,
185 201 category='success')
186 202 except Exception, e:
187 203 log.error(traceback.format_exc())
188 204 h.flash(_('An error occurred during unlocking'),
189 205 category='error')
190 206 return redirect(url('summary_home', repo_name=repo_name))
@@ -1,364 +1,369 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 logging
23 23
24 24 import formencode
25 25 from formencode import All
26 26
27 27 from pylons.i18n.translation import _
28 28
29 29 from rhodecode.model import validators as v
30 30 from rhodecode import BACKENDS
31 31
32 32 log = logging.getLogger(__name__)
33 33
34 34
35 35 class LoginForm(formencode.Schema):
36 36 allow_extra_fields = True
37 37 filter_extra_fields = True
38 38 username = v.UnicodeString(
39 39 strip=True,
40 40 min=1,
41 41 not_empty=True,
42 42 messages={
43 43 'empty': _(u'Please enter a login'),
44 44 'tooShort': _(u'Enter a value %(min)i characters long or more')}
45 45 )
46 46
47 47 password = v.UnicodeString(
48 48 strip=False,
49 49 min=3,
50 50 not_empty=True,
51 51 messages={
52 52 'empty': _(u'Please enter a password'),
53 53 'tooShort': _(u'Enter %(min)i characters or more')}
54 54 )
55 55
56 56 remember = v.StringBoolean(if_missing=False)
57 57
58 58 chained_validators = [v.ValidAuth()]
59 59
60 60
61 61 def UserForm(edit=False, old_data={}):
62 62 class _UserForm(formencode.Schema):
63 63 allow_extra_fields = True
64 64 filter_extra_fields = True
65 65 username = All(v.UnicodeString(strip=True, min=1, not_empty=True),
66 66 v.ValidUsername(edit, old_data))
67 67 if edit:
68 68 new_password = All(
69 69 v.ValidPassword(),
70 70 v.UnicodeString(strip=False, min=6, not_empty=False)
71 71 )
72 72 password_confirmation = All(
73 73 v.ValidPassword(),
74 74 v.UnicodeString(strip=False, min=6, not_empty=False),
75 75 )
76 76 admin = v.StringBoolean(if_missing=False)
77 77 else:
78 78 password = All(
79 79 v.ValidPassword(),
80 80 v.UnicodeString(strip=False, min=6, not_empty=True)
81 81 )
82 82 password_confirmation = All(
83 83 v.ValidPassword(),
84 84 v.UnicodeString(strip=False, min=6, not_empty=False)
85 85 )
86 86
87 87 active = v.StringBoolean(if_missing=False)
88 88 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
89 89 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
90 90 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
91 91
92 92 chained_validators = [v.ValidPasswordsMatch()]
93 93
94 94 return _UserForm
95 95
96 96
97 97 def UsersGroupForm(edit=False, old_data={}, available_members=[]):
98 98 class _UsersGroupForm(formencode.Schema):
99 99 allow_extra_fields = True
100 100 filter_extra_fields = True
101 101
102 102 users_group_name = All(
103 103 v.UnicodeString(strip=True, min=1, not_empty=True),
104 104 v.ValidUsersGroup(edit, old_data)
105 105 )
106 106
107 107 users_group_active = v.StringBoolean(if_missing=False)
108 108
109 109 if edit:
110 110 users_group_members = v.OneOf(
111 111 available_members, hideList=False, testValueList=True,
112 112 if_missing=None, not_empty=False
113 113 )
114 114
115 115 return _UsersGroupForm
116 116
117 117
118 118 def ReposGroupForm(edit=False, old_data={}, available_groups=[]):
119 119 class _ReposGroupForm(formencode.Schema):
120 120 allow_extra_fields = True
121 121 filter_extra_fields = False
122 122
123 123 group_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
124 124 v.SlugifyName())
125 125 group_description = v.UnicodeString(strip=True, min=1,
126 126 not_empty=True)
127 127 group_parent_id = v.OneOf(available_groups, hideList=False,
128 128 testValueList=True,
129 129 if_missing=None, not_empty=False)
130 130 enable_locking = v.StringBoolean(if_missing=False)
131 131 recursive = v.StringBoolean(if_missing=False)
132 132 chained_validators = [v.ValidReposGroup(edit, old_data),
133 133 v.ValidPerms('group')]
134 134
135 135 return _ReposGroupForm
136 136
137 137
138 138 def RegisterForm(edit=False, old_data={}):
139 139 class _RegisterForm(formencode.Schema):
140 140 allow_extra_fields = True
141 141 filter_extra_fields = True
142 142 username = All(
143 143 v.ValidUsername(edit, old_data),
144 144 v.UnicodeString(strip=True, min=1, not_empty=True)
145 145 )
146 146 password = All(
147 147 v.ValidPassword(),
148 148 v.UnicodeString(strip=False, min=6, not_empty=True)
149 149 )
150 150 password_confirmation = All(
151 151 v.ValidPassword(),
152 152 v.UnicodeString(strip=False, min=6, not_empty=True)
153 153 )
154 154 active = v.StringBoolean(if_missing=False)
155 155 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
156 156 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
157 157 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
158 158
159 159 chained_validators = [v.ValidPasswordsMatch()]
160 160
161 161 return _RegisterForm
162 162
163 163
164 164 def PasswordResetForm():
165 165 class _PasswordResetForm(formencode.Schema):
166 166 allow_extra_fields = True
167 167 filter_extra_fields = True
168 168 email = All(v.ValidSystemEmail(), v.Email(not_empty=True))
169 169 return _PasswordResetForm
170 170
171 171
172 172 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
173 173 repo_groups=[], landing_revs=[]):
174 174 class _RepoForm(formencode.Schema):
175 175 allow_extra_fields = True
176 filter_extra_fields = True
176 filter_extra_fields = False
177 177 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
178 178 v.SlugifyName())
179 179 repo_group = All(v.CanWriteGroup(),
180 180 v.OneOf(repo_groups, hideList=True))
181 181 repo_type = v.OneOf(supported_backends)
182 182 repo_description = v.UnicodeString(strip=True, min=1, not_empty=False)
183 183 repo_private = v.StringBoolean(if_missing=False)
184 repo_landing_rev = v.OneOf(landing_revs, hideList=True)
185 clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
186
184 187 repo_enable_statistics = v.StringBoolean(if_missing=False)
185 188 repo_enable_downloads = v.StringBoolean(if_missing=False)
186 189 repo_enable_locking = v.StringBoolean(if_missing=False)
187 repo_landing_rev = v.OneOf(landing_revs, hideList=True)
188 clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
189 190
190 191 if edit:
191 192 #this is repo owner
192 193 user = All(v.UnicodeString(not_empty=True), v.ValidRepoUser())
193 194
194 195 chained_validators = [v.ValidCloneUri(),
195 196 v.ValidRepoName(edit, old_data),
196 197 v.ValidPerms()]
197 198 return _RepoForm
198 199
199 200
201 def RepoSettingsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
202 repo_groups=[], landing_revs=[]):
203 class _RepoForm(formencode.Schema):
204 allow_extra_fields = True
205 filter_extra_fields = False
206 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
207 v.SlugifyName())
208 repo_group = All(v.CanWriteGroup(),
209 v.OneOf(repo_groups, hideList=True))
210 repo_description = v.UnicodeString(strip=True, min=1, not_empty=False)
211 repo_private = v.StringBoolean(if_missing=False)
212 repo_landing_rev = v.OneOf(landing_revs, hideList=True)
213 clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
214
215 chained_validators = [v.ValidCloneUri(),
216 v.ValidRepoName(edit, old_data),
217 v.ValidPerms(),
218 v.ValidSettings()]
219 return _RepoForm
220
221
200 222 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
201 223 repo_groups=[], landing_revs=[]):
202 224 class _RepoForkForm(formencode.Schema):
203 225 allow_extra_fields = True
204 226 filter_extra_fields = False
205 227 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
206 228 v.SlugifyName())
207 229 repo_group = All(v.CanWriteGroup(),
208 230 v.OneOf(repo_groups, hideList=True))
209 231 repo_type = All(v.ValidForkType(old_data), v.OneOf(supported_backends))
210 232 description = v.UnicodeString(strip=True, min=1, not_empty=True)
211 233 private = v.StringBoolean(if_missing=False)
212 234 copy_permissions = v.StringBoolean(if_missing=False)
213 235 update_after_clone = v.StringBoolean(if_missing=False)
214 236 fork_parent_id = v.UnicodeString()
215 237 chained_validators = [v.ValidForkName(edit, old_data)]
216 238 landing_rev = v.OneOf(landing_revs, hideList=True)
217 239
218 240 return _RepoForkForm
219 241
220 242
221 def RepoSettingsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
222 repo_groups=[], landing_revs=[]):
223 class _RepoForm(formencode.Schema):
224 allow_extra_fields = True
225 filter_extra_fields = False
226 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
227 v.SlugifyName())
228 description = v.UnicodeString(strip=True, min=1, not_empty=True)
229 repo_group = All(v.CanWriteGroup(),
230 v.OneOf(repo_groups, hideList=True))
231 private = v.StringBoolean(if_missing=False)
232 landing_rev = v.OneOf(landing_revs, hideList=True)
233 chained_validators = [v.ValidRepoName(edit, old_data), v.ValidPerms(),
234 v.ValidSettings()]
235 return _RepoForm
236
237
238 243 def ApplicationSettingsForm():
239 244 class _ApplicationSettingsForm(formencode.Schema):
240 245 allow_extra_fields = True
241 246 filter_extra_fields = False
242 247 rhodecode_title = v.UnicodeString(strip=True, min=1, not_empty=True)
243 248 rhodecode_realm = v.UnicodeString(strip=True, min=1, not_empty=True)
244 249 rhodecode_ga_code = v.UnicodeString(strip=True, min=1, not_empty=False)
245 250
246 251 return _ApplicationSettingsForm
247 252
248 253
249 254 def ApplicationVisualisationForm():
250 255 class _ApplicationVisualisationForm(formencode.Schema):
251 256 allow_extra_fields = True
252 257 filter_extra_fields = False
253 258 rhodecode_show_public_icon = v.StringBoolean(if_missing=False)
254 259 rhodecode_show_private_icon = v.StringBoolean(if_missing=False)
255 260 rhodecode_stylify_metatags = v.StringBoolean(if_missing=False)
256 261
257 262 rhodecode_lightweight_dashboard = v.StringBoolean(if_missing=False)
258 263 rhodecode_lightweight_journal = v.StringBoolean(if_missing=False)
259 264
260 265 return _ApplicationVisualisationForm
261 266
262 267
263 268 def ApplicationUiSettingsForm():
264 269 class _ApplicationUiSettingsForm(formencode.Schema):
265 270 allow_extra_fields = True
266 271 filter_extra_fields = False
267 272 web_push_ssl = v.StringBoolean(if_missing=False)
268 273 paths_root_path = All(
269 274 v.ValidPath(),
270 275 v.UnicodeString(strip=True, min=1, not_empty=True)
271 276 )
272 277 hooks_changegroup_update = v.StringBoolean(if_missing=False)
273 278 hooks_changegroup_repo_size = v.StringBoolean(if_missing=False)
274 279 hooks_changegroup_push_logger = v.StringBoolean(if_missing=False)
275 280 hooks_outgoing_pull_logger = v.StringBoolean(if_missing=False)
276 281
277 282 extensions_largefiles = v.StringBoolean(if_missing=False)
278 283 extensions_hgsubversion = v.StringBoolean(if_missing=False)
279 284 extensions_hggit = v.StringBoolean(if_missing=False)
280 285
281 286 return _ApplicationUiSettingsForm
282 287
283 288
284 289 def DefaultPermissionsForm(repo_perms_choices, group_perms_choices,
285 290 register_choices, create_choices, fork_choices):
286 291 class _DefaultPermissionsForm(formencode.Schema):
287 292 allow_extra_fields = True
288 293 filter_extra_fields = True
289 294 overwrite_default_repo = v.StringBoolean(if_missing=False)
290 295 overwrite_default_group = v.StringBoolean(if_missing=False)
291 296 anonymous = v.StringBoolean(if_missing=False)
292 297 default_repo_perm = v.OneOf(repo_perms_choices)
293 298 default_group_perm = v.OneOf(group_perms_choices)
294 299 default_register = v.OneOf(register_choices)
295 300 default_create = v.OneOf(create_choices)
296 301 default_fork = v.OneOf(fork_choices)
297 302
298 303 return _DefaultPermissionsForm
299 304
300 305
301 306 def DefaultsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
302 307 class _DefaultsForm(formencode.Schema):
303 308 allow_extra_fields = True
304 309 filter_extra_fields = True
305 310 default_repo_type = v.OneOf(supported_backends)
306 311 default_repo_private = v.StringBoolean(if_missing=False)
307 312 default_repo_enable_statistics = v.StringBoolean(if_missing=False)
308 313 default_repo_enable_downloads = v.StringBoolean(if_missing=False)
309 314 default_repo_enable_locking = v.StringBoolean(if_missing=False)
310 315
311 316 return _DefaultsForm
312 317
313 318
314 319 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices,
315 320 tls_kind_choices):
316 321 class _LdapSettingsForm(formencode.Schema):
317 322 allow_extra_fields = True
318 323 filter_extra_fields = True
319 324 #pre_validators = [LdapLibValidator]
320 325 ldap_active = v.StringBoolean(if_missing=False)
321 326 ldap_host = v.UnicodeString(strip=True,)
322 327 ldap_port = v.Number(strip=True,)
323 328 ldap_tls_kind = v.OneOf(tls_kind_choices)
324 329 ldap_tls_reqcert = v.OneOf(tls_reqcert_choices)
325 330 ldap_dn_user = v.UnicodeString(strip=True,)
326 331 ldap_dn_pass = v.UnicodeString(strip=True,)
327 332 ldap_base_dn = v.UnicodeString(strip=True,)
328 333 ldap_filter = v.UnicodeString(strip=True,)
329 334 ldap_search_scope = v.OneOf(search_scope_choices)
330 335 ldap_attr_login = All(
331 336 v.AttrLoginValidator(),
332 337 v.UnicodeString(strip=True,)
333 338 )
334 339 ldap_attr_firstname = v.UnicodeString(strip=True,)
335 340 ldap_attr_lastname = v.UnicodeString(strip=True,)
336 341 ldap_attr_email = v.UnicodeString(strip=True,)
337 342
338 343 return _LdapSettingsForm
339 344
340 345
341 346 def UserExtraEmailForm():
342 347 class _UserExtraEmailForm(formencode.Schema):
343 348 email = All(v.UniqSystemEmail(), v.Email)
344 349
345 350 return _UserExtraEmailForm
346 351
347 352
348 353 def PullRequestForm(repo_id):
349 354 class _PullRequestForm(formencode.Schema):
350 355 allow_extra_fields = True
351 356 filter_extra_fields = True
352 357
353 358 user = v.UnicodeString(strip=True, required=True)
354 359 org_repo = v.UnicodeString(strip=True, required=True)
355 360 org_ref = v.UnicodeString(strip=True, required=True)
356 361 other_repo = v.UnicodeString(strip=True, required=True)
357 362 other_ref = v.UnicodeString(strip=True, required=True)
358 363 revisions = All(v.NotReviewedRevisions(repo_id)(), v.UniqueList(not_empty=True))
359 364 review_members = v.UniqueList(not_empty=True)
360 365
361 366 pullrequest_title = v.UnicodeString(strip=True, required=True, min=3)
362 367 pullrequest_desc = v.UnicodeString(strip=True, required=False)
363 368
364 369 return _PullRequestForm
@@ -1,564 +1,579 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.repo
4 4 ~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Repository model for rhodecode
7 7
8 8 :created_on: Jun 5, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25 from __future__ import with_statement
26 26 import os
27 27 import shutil
28 28 import logging
29 29 import traceback
30 30 from datetime import datetime
31 31
32 32 from rhodecode.lib.vcs.backends import get_backend
33 33 from rhodecode.lib.compat import json
34 from rhodecode.lib.utils2 import LazyProperty, safe_str, safe_unicode
34 from rhodecode.lib.utils2 import LazyProperty, safe_str, safe_unicode,\
35 remove_prefix
35 36 from rhodecode.lib.caching_query import FromCache
36 37 from rhodecode.lib.hooks import log_create_repository, log_delete_repository
37 38
38 39 from rhodecode.model import BaseModel
39 40 from rhodecode.model.db import Repository, UserRepoToPerm, User, Permission, \
40 41 Statistics, UsersGroup, UsersGroupRepoToPerm, RhodeCodeUi, RepoGroup,\
41 42 RhodeCodeSetting
42 43 from rhodecode.lib import helpers as h
43 44
44 45
45 46 log = logging.getLogger(__name__)
46 47
47 48
48 49 class RepoModel(BaseModel):
49 50
50 51 cls = Repository
51 52 URL_SEPARATOR = Repository.url_sep()
52 53
53 54 def __get_users_group(self, users_group):
54 55 return self._get_instance(UsersGroup, users_group,
55 56 callback=UsersGroup.get_by_group_name)
56 57
57 58 def _get_repos_group(self, repos_group):
58 59 return self._get_instance(RepoGroup, repos_group,
59 60 callback=RepoGroup.get_by_group_name)
60 61
61 62 @LazyProperty
62 63 def repos_path(self):
63 64 """
64 65 Get's the repositories root path from database
65 66 """
66 67
67 68 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
68 69 return q.ui_value
69 70
70 71 def get(self, repo_id, cache=False):
71 72 repo = self.sa.query(Repository)\
72 73 .filter(Repository.repo_id == repo_id)
73 74
74 75 if cache:
75 76 repo = repo.options(FromCache("sql_cache_short",
76 77 "get_repo_%s" % repo_id))
77 78 return repo.scalar()
78 79
79 80 def get_repo(self, repository):
80 81 return self._get_repo(repository)
81 82
82 83 def get_by_repo_name(self, repo_name, cache=False):
83 84 repo = self.sa.query(Repository)\
84 85 .filter(Repository.repo_name == repo_name)
85 86
86 87 if cache:
87 88 repo = repo.options(FromCache("sql_cache_short",
88 89 "get_repo_%s" % repo_name))
89 90 return repo.scalar()
90 91
91 92 def get_users_js(self):
92 93 users = self.sa.query(User).filter(User.active == True).all()
93 94 return json.dumps([
94 95 {
95 96 'id': u.user_id,
96 97 'fname': u.name,
97 98 'lname': u.lastname,
98 99 'nname': u.username,
99 100 'gravatar_lnk': h.gravatar_url(u.email, 14)
100 101 } for u in users]
101 102 )
102 103
103 104 def get_users_groups_js(self):
104 105 users_groups = self.sa.query(UsersGroup)\
105 106 .filter(UsersGroup.users_group_active == True).all()
106 107
107 108 return json.dumps([
108 109 {
109 110 'id': gr.users_group_id,
110 111 'grname': gr.users_group_name,
111 112 'grmembers': len(gr.members),
112 113 } for gr in users_groups]
113 114 )
114 115
115 116 def _get_defaults(self, repo_name):
116 117 """
117 118 Get's information about repository, and returns a dict for
118 119 usage in forms
119 120
120 121 :param repo_name:
121 122 """
122 123
123 124 repo_info = Repository.get_by_repo_name(repo_name)
124 125
125 126 if repo_info is None:
126 127 return None
127 128
128 129 defaults = repo_info.get_dict()
129 130 group, repo_name = repo_info.groups_and_repo
130 131 defaults['repo_name'] = repo_name
131 132 defaults['repo_group'] = getattr(group[-1] if group else None,
132 133 'group_id', None)
133 134
135 for strip, k in [(0, 'repo_type'), (1, 'repo_enable_downloads'),
136 (1, 'repo_description'), (1, 'repo_enable_locking'),
137 (1, 'repo_landing_rev'), (0, 'clone_uri'),
138 (1, 'repo_private'), (1, 'repo_enable_statistics')]:
139 attr = k
140 if strip:
141 attr = remove_prefix(k, 'repo_')
142
143 defaults[k] = defaults[attr]
144
134 145 # fill owner
135 146 if repo_info.user:
136 147 defaults.update({'user': repo_info.user.username})
137 148 else:
138 149 replacement_user = User.query().filter(User.admin ==
139 150 True).first().username
140 151 defaults.update({'user': replacement_user})
141 152
142 153 # fill repository users
143 154 for p in repo_info.repo_to_perm:
144 155 defaults.update({'u_perm_%s' % p.user.username:
145 156 p.permission.permission_name})
146 157
147 158 # fill repository groups
148 159 for p in repo_info.users_group_to_perm:
149 160 defaults.update({'g_perm_%s' % p.users_group.users_group_name:
150 161 p.permission.permission_name})
151 162
152 163 return defaults
153 164
154 def update(self, repo_name, form_data):
165 def update(self, org_repo_name, **kwargs):
155 166 try:
156 cur_repo = self.get_by_repo_name(repo_name, cache=False)
167 cur_repo = self.get_by_repo_name(org_repo_name, cache=False)
157 168
158 169 # update permissions
159 for member, perm, member_type in form_data['perms_updates']:
170 for member, perm, member_type in kwargs['perms_updates']:
160 171 if member_type == 'user':
161 172 # this updates existing one
162 173 RepoModel().grant_user_permission(
163 174 repo=cur_repo, user=member, perm=perm
164 175 )
165 176 else:
166 177 RepoModel().grant_users_group_permission(
167 178 repo=cur_repo, group_name=member, perm=perm
168 179 )
169 180 # set new permissions
170 for member, perm, member_type in form_data['perms_new']:
181 for member, perm, member_type in kwargs['perms_new']:
171 182 if member_type == 'user':
172 183 RepoModel().grant_user_permission(
173 184 repo=cur_repo, user=member, perm=perm
174 185 )
175 186 else:
176 187 RepoModel().grant_users_group_permission(
177 188 repo=cur_repo, group_name=member, perm=perm
178 189 )
179 190
180 # update current repo
181 for k, v in form_data.items():
182 if k == 'user':
183 cur_repo.user = User.get_by_username(v)
184 elif k == 'repo_name':
185 pass
186 elif k == 'repo_group':
187 cur_repo.group = RepoGroup.get(v)
191 if 'user' in kwargs:
192 cur_repo.user = User.get_by_username(kwargs['user'])
193
194 if 'repo_group' in kwargs:
195 cur_repo.group = RepoGroup.get(kwargs['repo_group'])
188 196
189 else:
190 setattr(cur_repo, k, v)
197 for strip, k in [(0, 'repo_type'), (1, 'repo_enable_downloads'),
198 (1, 'repo_description'), (1, 'repo_enable_locking'),
199 (1, 'repo_landing_rev'), (0, 'clone_uri'),
200 (1, 'repo_private'), (1, 'repo_enable_statistics')]:
201 if k in kwargs:
202 val = kwargs[k]
203 if strip:
204 k = remove_prefix(k, 'repo_')
205 setattr(cur_repo, k, val)
191 206
192 new_name = cur_repo.get_new_name(form_data['repo_name'])
207 new_name = cur_repo.get_new_name(kwargs['repo_name'])
193 208 cur_repo.repo_name = new_name
194 209
195 210 self.sa.add(cur_repo)
196 211
197 if repo_name != new_name:
212 if org_repo_name != new_name:
198 213 # rename repository
199 self.__rename_repo(old=repo_name, new=new_name)
214 self.__rename_repo(old=org_repo_name, new=new_name)
200 215
201 216 return cur_repo
202 217 except:
203 218 log.error(traceback.format_exc())
204 219 raise
205 220
206 221 def create_repo(self, repo_name, repo_type, description, owner,
207 222 private=False, clone_uri=None, repos_group=None,
208 223 landing_rev='tip', just_db=False, fork_of=None,
209 224 copy_fork_permissions=False, enable_statistics=False,
210 225 enable_locking=False, enable_downloads=False):
211 226 """
212 227 Create repository
213 228
214 229 """
215 230 from rhodecode.model.scm import ScmModel
216 231
217 232 owner = self._get_user(owner)
218 233 fork_of = self._get_repo(fork_of)
219 234 repos_group = self._get_repos_group(repos_group)
220 235 try:
221 236
222 237 # repo name is just a name of repository
223 238 # while repo_name_full is a full qualified name that is combined
224 239 # with name and path of group
225 240 repo_name_full = repo_name
226 241 repo_name = repo_name.split(self.URL_SEPARATOR)[-1]
227 242
228 243 new_repo = Repository()
229 244 new_repo.enable_statistics = False
230 245 new_repo.repo_name = repo_name_full
231 246 new_repo.repo_type = repo_type
232 247 new_repo.user = owner
233 248 new_repo.group = repos_group
234 249 new_repo.description = description or repo_name
235 250 new_repo.private = private
236 251 new_repo.clone_uri = clone_uri
237 252 new_repo.landing_rev = landing_rev
238 253
239 254 new_repo.enable_statistics = enable_statistics
240 255 new_repo.enable_locking = enable_locking
241 256 new_repo.enable_downloads = enable_downloads
242 257
243 258 if repos_group:
244 259 new_repo.enable_locking = repos_group.enable_locking
245 260
246 261 if fork_of:
247 262 parent_repo = fork_of
248 263 new_repo.fork = parent_repo
249 264
250 265 self.sa.add(new_repo)
251 266
252 267 def _create_default_perms():
253 268 # create default permission
254 269 repo_to_perm = UserRepoToPerm()
255 270 default = 'repository.read'
256 271 for p in User.get_by_username('default').user_perms:
257 272 if p.permission.permission_name.startswith('repository.'):
258 273 default = p.permission.permission_name
259 274 break
260 275
261 276 default_perm = 'repository.none' if private else default
262 277
263 278 repo_to_perm.permission_id = self.sa.query(Permission)\
264 279 .filter(Permission.permission_name == default_perm)\
265 280 .one().permission_id
266 281
267 282 repo_to_perm.repository = new_repo
268 283 repo_to_perm.user_id = User.get_by_username('default').user_id
269 284
270 285 self.sa.add(repo_to_perm)
271 286
272 287 if fork_of:
273 288 if copy_fork_permissions:
274 289 repo = fork_of
275 290 user_perms = UserRepoToPerm.query()\
276 291 .filter(UserRepoToPerm.repository == repo).all()
277 292 group_perms = UsersGroupRepoToPerm.query()\
278 293 .filter(UsersGroupRepoToPerm.repository == repo).all()
279 294
280 295 for perm in user_perms:
281 296 UserRepoToPerm.create(perm.user, new_repo,
282 297 perm.permission)
283 298
284 299 for perm in group_perms:
285 300 UsersGroupRepoToPerm.create(perm.users_group, new_repo,
286 301 perm.permission)
287 302 else:
288 303 _create_default_perms()
289 304 else:
290 305 _create_default_perms()
291 306
292 307 if not just_db:
293 308 self.__create_repo(repo_name, repo_type,
294 309 repos_group,
295 310 clone_uri)
296 311 log_create_repository(new_repo.get_dict(),
297 312 created_by=owner.username)
298 313
299 314 # now automatically start following this repository as owner
300 315 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
301 316 owner.user_id)
302 317 return new_repo
303 318 except:
304 319 log.error(traceback.format_exc())
305 320 raise
306 321
307 322 def create(self, form_data, cur_user, just_db=False, fork=None):
308 323 """
309 324 Backward compatibility function, just a wrapper on top of create_repo
310 325
311 326 :param form_data:
312 327 :param cur_user:
313 328 :param just_db:
314 329 :param fork:
315 330 """
316 331 owner = cur_user
317 332 repo_name = form_data['repo_name_full']
318 333 repo_type = form_data['repo_type']
319 334 description = form_data['repo_description']
320 335 private = form_data['repo_private']
321 336 clone_uri = form_data.get('clone_uri')
322 337 repos_group = form_data['repo_group']
323 338 landing_rev = form_data['repo_landing_rev']
324 339 copy_fork_permissions = form_data.get('copy_permissions')
325 340 fork_of = form_data.get('fork_parent_id')
326 341
327 342 ##defaults
328 343 defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
329 344 enable_statistics = defs.get('repo_enable_statistic')
330 345 enable_locking = defs.get('repo_enable_locking')
331 346 enable_downloads = defs.get('repo_enable_downloads')
332 347
333 348 return self.create_repo(
334 349 repo_name, repo_type, description, owner, private, clone_uri,
335 350 repos_group, landing_rev, just_db, fork_of, copy_fork_permissions,
336 351 enable_statistics, enable_locking, enable_downloads
337 352 )
338 353
339 354 def create_fork(self, form_data, cur_user):
340 355 """
341 356 Simple wrapper into executing celery task for fork creation
342 357
343 358 :param form_data:
344 359 :param cur_user:
345 360 """
346 361 from rhodecode.lib.celerylib import tasks, run_task
347 362 run_task(tasks.create_repo_fork, form_data, cur_user)
348 363
349 364 def delete(self, repo):
350 365 repo = self._get_repo(repo)
351 366 if repo:
352 367 old_repo_dict = repo.get_dict()
353 368 owner = repo.user
354 369 try:
355 370 self.sa.delete(repo)
356 371 self.__delete_repo(repo)
357 372 log_delete_repository(old_repo_dict,
358 373 deleted_by=owner.username)
359 374 except:
360 375 log.error(traceback.format_exc())
361 376 raise
362 377
363 378 def grant_user_permission(self, repo, user, perm):
364 379 """
365 380 Grant permission for user on given repository, or update existing one
366 381 if found
367 382
368 383 :param repo: Instance of Repository, repository_id, or repository name
369 384 :param user: Instance of User, user_id or username
370 385 :param perm: Instance of Permission, or permission_name
371 386 """
372 387 user = self._get_user(user)
373 388 repo = self._get_repo(repo)
374 389 permission = self._get_perm(perm)
375 390
376 391 # check if we have that permission already
377 392 obj = self.sa.query(UserRepoToPerm)\
378 393 .filter(UserRepoToPerm.user == user)\
379 394 .filter(UserRepoToPerm.repository == repo)\
380 395 .scalar()
381 396 if obj is None:
382 397 # create new !
383 398 obj = UserRepoToPerm()
384 399 obj.repository = repo
385 400 obj.user = user
386 401 obj.permission = permission
387 402 self.sa.add(obj)
388 403 log.debug('Granted perm %s to %s on %s' % (perm, user, repo))
389 404
390 405 def revoke_user_permission(self, repo, user):
391 406 """
392 407 Revoke permission for user on given repository
393 408
394 409 :param repo: Instance of Repository, repository_id, or repository name
395 410 :param user: Instance of User, user_id or username
396 411 """
397 412
398 413 user = self._get_user(user)
399 414 repo = self._get_repo(repo)
400 415
401 416 obj = self.sa.query(UserRepoToPerm)\
402 417 .filter(UserRepoToPerm.repository == repo)\
403 418 .filter(UserRepoToPerm.user == user)\
404 419 .scalar()
405 420 if obj:
406 421 self.sa.delete(obj)
407 422 log.debug('Revoked perm on %s on %s' % (repo, user))
408 423
409 424 def grant_users_group_permission(self, repo, group_name, perm):
410 425 """
411 426 Grant permission for users group on given repository, or update
412 427 existing one if found
413 428
414 429 :param repo: Instance of Repository, repository_id, or repository name
415 430 :param group_name: Instance of UserGroup, users_group_id,
416 431 or users group name
417 432 :param perm: Instance of Permission, or permission_name
418 433 """
419 434 repo = self._get_repo(repo)
420 435 group_name = self.__get_users_group(group_name)
421 436 permission = self._get_perm(perm)
422 437
423 438 # check if we have that permission already
424 439 obj = self.sa.query(UsersGroupRepoToPerm)\
425 440 .filter(UsersGroupRepoToPerm.users_group == group_name)\
426 441 .filter(UsersGroupRepoToPerm.repository == repo)\
427 442 .scalar()
428 443
429 444 if obj is None:
430 445 # create new
431 446 obj = UsersGroupRepoToPerm()
432 447
433 448 obj.repository = repo
434 449 obj.users_group = group_name
435 450 obj.permission = permission
436 451 self.sa.add(obj)
437 452 log.debug('Granted perm %s to %s on %s' % (perm, group_name, repo))
438 453
439 454 def revoke_users_group_permission(self, repo, group_name):
440 455 """
441 456 Revoke permission for users group on given repository
442 457
443 458 :param repo: Instance of Repository, repository_id, or repository name
444 459 :param group_name: Instance of UserGroup, users_group_id,
445 460 or users group name
446 461 """
447 462 repo = self._get_repo(repo)
448 463 group_name = self.__get_users_group(group_name)
449 464
450 465 obj = self.sa.query(UsersGroupRepoToPerm)\
451 466 .filter(UsersGroupRepoToPerm.repository == repo)\
452 467 .filter(UsersGroupRepoToPerm.users_group == group_name)\
453 468 .scalar()
454 469 if obj:
455 470 self.sa.delete(obj)
456 471 log.debug('Revoked perm to %s on %s' % (repo, group_name))
457 472
458 473 def delete_stats(self, repo_name):
459 474 """
460 475 removes stats for given repo
461 476
462 477 :param repo_name:
463 478 """
464 479 try:
465 480 obj = self.sa.query(Statistics)\
466 481 .filter(Statistics.repository ==
467 482 self.get_by_repo_name(repo_name))\
468 483 .one()
469 484 self.sa.delete(obj)
470 485 except:
471 486 log.error(traceback.format_exc())
472 487 raise
473 488
474 489 def __create_repo(self, repo_name, alias, parent, clone_uri=False):
475 490 """
476 491 makes repository on filesystem. It's group aware means it'll create
477 492 a repository within a group, and alter the paths accordingly of
478 493 group location
479 494
480 495 :param repo_name:
481 496 :param alias:
482 497 :param parent_id:
483 498 :param clone_uri:
484 499 """
485 500 from rhodecode.lib.utils import is_valid_repo, is_valid_repos_group
486 501 from rhodecode.model.scm import ScmModel
487 502
488 503 if parent:
489 504 new_parent_path = os.sep.join(parent.full_path_splitted)
490 505 else:
491 506 new_parent_path = ''
492 507
493 508 # we need to make it str for mercurial
494 509 repo_path = os.path.join(*map(lambda x: safe_str(x),
495 510 [self.repos_path, new_parent_path, repo_name]))
496 511
497 512 # check if this path is not a repository
498 513 if is_valid_repo(repo_path, self.repos_path):
499 514 raise Exception('This path %s is a valid repository' % repo_path)
500 515
501 516 # check if this path is a group
502 517 if is_valid_repos_group(repo_path, self.repos_path):
503 518 raise Exception('This path %s is a valid group' % repo_path)
504 519
505 520 log.info('creating repo %s in %s @ %s' % (
506 521 repo_name, safe_unicode(repo_path), clone_uri
507 522 )
508 523 )
509 524 backend = get_backend(alias)
510 525 if alias == 'hg':
511 526 backend(repo_path, create=True, src_url=clone_uri)
512 527 elif alias == 'git':
513 528 r = backend(repo_path, create=True, src_url=clone_uri, bare=True)
514 529 # add rhodecode hook into this repo
515 530 ScmModel().install_git_hook(repo=r)
516 531 else:
517 532 raise Exception('Undefined alias %s' % alias)
518 533
519 534 def __rename_repo(self, old, new):
520 535 """
521 536 renames repository on filesystem
522 537
523 538 :param old: old name
524 539 :param new: new name
525 540 """
526 541 log.info('renaming repo from %s to %s' % (old, new))
527 542
528 543 old_path = os.path.join(self.repos_path, old)
529 544 new_path = os.path.join(self.repos_path, new)
530 545 if os.path.isdir(new_path):
531 546 raise Exception(
532 547 'Was trying to rename to already existing dir %s' % new_path
533 548 )
534 549 shutil.move(old_path, new_path)
535 550
536 551 def __delete_repo(self, repo):
537 552 """
538 553 removes repo from filesystem, the removal is acctually made by
539 554 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
540 555 repository is no longer valid for rhodecode, can be undeleted later on
541 556 by reverting the renames on this repository
542 557
543 558 :param repo: repo object
544 559 """
545 560 rm_path = os.path.join(self.repos_path, repo.repo_name)
546 561 log.info("Removing %s" % (rm_path))
547 562 # disable hg/git internal that it doesn't get detected as repo
548 563 alias = repo.repo_type
549 564
550 565 bare = getattr(repo.scm_instance, 'bare', False)
551 566
552 567 if not bare:
553 568 # skip this for bare git repos
554 569 shutil.move(os.path.join(rm_path, '.%s' % alias),
555 570 os.path.join(rm_path, 'rm__.%s' % alias))
556 571 # disable repo
557 572 _now = datetime.now()
558 573 _ms = str(_now.microsecond).rjust(6, '0')
559 574 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
560 575 repo.just_name)
561 576 if repo.group:
562 577 args = repo.group.full_path_splitted + [_d]
563 578 _d = os.path.join(*args)
564 579 shutil.move(rm_path, os.path.join(self.repos_path, _d))
@@ -1,699 +1,708 b''
1 1 """
2 2 Set of generic validators
3 3 """
4 4 import os
5 5 import re
6 6 import formencode
7 7 import logging
8 8 from collections import defaultdict
9 9 from pylons.i18n.translation import _
10 10 from webhelpers.pylonslib.secure_form import authentication_token
11 11
12 12 from formencode.validators import (
13 13 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set,
14 14 NotEmpty
15 15 )
16 16 from rhodecode.lib.compat import OrderedSet
17 17 from rhodecode.lib.utils import repo_name_slug
18 18 from rhodecode.model.db import RepoGroup, Repository, UsersGroup, User,\
19 19 ChangesetStatus
20 20 from rhodecode.lib.exceptions import LdapImportError
21 21 from rhodecode.config.routing import ADMIN_PREFIX
22 22 from rhodecode.lib.auth import HasReposGroupPermissionAny
23 23
24 24 # silence warnings and pylint
25 25 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set, \
26 26 NotEmpty
27 27
28 28 log = logging.getLogger(__name__)
29 29
30 30
31 31 class UniqueList(formencode.FancyValidator):
32 32 """
33 33 Unique List !
34 34 """
35 35 messages = dict(
36 36 empty=_('Value cannot be an empty list'),
37 37 missing_value=_('Value cannot be an empty list'),
38 38 )
39 39
40 40 def _to_python(self, value, state):
41 41 if isinstance(value, list):
42 42 return value
43 43 elif isinstance(value, set):
44 44 return list(value)
45 45 elif isinstance(value, tuple):
46 46 return list(value)
47 47 elif value is None:
48 48 return []
49 49 else:
50 50 return [value]
51 51
52 52 def empty_value(self, value):
53 53 return []
54 54
55 55
56 56 class StateObj(object):
57 57 """
58 58 this is needed to translate the messages using _() in validators
59 59 """
60 60 _ = staticmethod(_)
61 61
62 62
63 63 def M(self, key, state=None, **kwargs):
64 64 """
65 65 returns string from self.message based on given key,
66 66 passed kw params are used to substitute %(named)s params inside
67 67 translated strings
68 68
69 69 :param msg:
70 70 :param state:
71 71 """
72 72 if state is None:
73 73 state = StateObj()
74 74 else:
75 75 state._ = staticmethod(_)
76 76 #inject validator into state object
77 77 return self.message(key, state, **kwargs)
78 78
79 79
80 80 def ValidUsername(edit=False, old_data={}):
81 81 class _validator(formencode.validators.FancyValidator):
82 82 messages = {
83 83 'username_exists': _(u'Username "%(username)s" already exists'),
84 84 'system_invalid_username':
85 85 _(u'Username "%(username)s" is forbidden'),
86 86 'invalid_username':
87 87 _(u'Username may only contain alphanumeric characters '
88 88 'underscores, periods or dashes and must begin with '
89 89 'alphanumeric character')
90 90 }
91 91
92 92 def validate_python(self, value, state):
93 93 if value in ['default', 'new_user']:
94 94 msg = M(self, 'system_invalid_username', state, username=value)
95 95 raise formencode.Invalid(msg, value, state)
96 96 #check if user is unique
97 97 old_un = None
98 98 if edit:
99 99 old_un = User.get(old_data.get('user_id')).username
100 100
101 101 if old_un != value or not edit:
102 102 if User.get_by_username(value, case_insensitive=True):
103 103 msg = M(self, 'username_exists', state, username=value)
104 104 raise formencode.Invalid(msg, value, state)
105 105
106 106 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
107 107 msg = M(self, 'invalid_username', state)
108 108 raise formencode.Invalid(msg, value, state)
109 109 return _validator
110 110
111 111
112 112 def ValidRepoUser():
113 113 class _validator(formencode.validators.FancyValidator):
114 114 messages = {
115 115 'invalid_username': _(u'Username %(username)s is not valid')
116 116 }
117 117
118 118 def validate_python(self, value, state):
119 119 try:
120 120 User.query().filter(User.active == True)\
121 121 .filter(User.username == value).one()
122 122 except Exception:
123 123 msg = M(self, 'invalid_username', state, username=value)
124 124 raise formencode.Invalid(msg, value, state,
125 125 error_dict=dict(username=msg)
126 126 )
127 127
128 128 return _validator
129 129
130 130
131 131 def ValidUsersGroup(edit=False, old_data={}):
132 132 class _validator(formencode.validators.FancyValidator):
133 133 messages = {
134 134 'invalid_group': _(u'Invalid users group name'),
135 135 'group_exist': _(u'Users group "%(usersgroup)s" already exists'),
136 136 'invalid_usersgroup_name':
137 137 _(u'users group name may only contain alphanumeric '
138 138 'characters underscores, periods or dashes and must begin '
139 139 'with alphanumeric character')
140 140 }
141 141
142 142 def validate_python(self, value, state):
143 143 if value in ['default']:
144 144 msg = M(self, 'invalid_group', state)
145 145 raise formencode.Invalid(msg, value, state,
146 146 error_dict=dict(users_group_name=msg)
147 147 )
148 148 #check if group is unique
149 149 old_ugname = None
150 150 if edit:
151 151 old_id = old_data.get('users_group_id')
152 152 old_ugname = UsersGroup.get(old_id).users_group_name
153 153
154 154 if old_ugname != value or not edit:
155 155 is_existing_group = UsersGroup.get_by_group_name(value,
156 156 case_insensitive=True)
157 157 if is_existing_group:
158 158 msg = M(self, 'group_exist', state, usersgroup=value)
159 159 raise formencode.Invalid(msg, value, state,
160 160 error_dict=dict(users_group_name=msg)
161 161 )
162 162
163 163 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
164 164 msg = M(self, 'invalid_usersgroup_name', state)
165 165 raise formencode.Invalid(msg, value, state,
166 166 error_dict=dict(users_group_name=msg)
167 167 )
168 168
169 169 return _validator
170 170
171 171
172 172 def ValidReposGroup(edit=False, old_data={}):
173 173 class _validator(formencode.validators.FancyValidator):
174 174 messages = {
175 175 'group_parent_id': _(u'Cannot assign this group as parent'),
176 176 'group_exists': _(u'Group "%(group_name)s" already exists'),
177 177 'repo_exists':
178 178 _(u'Repository with name "%(group_name)s" already exists')
179 179 }
180 180
181 181 def validate_python(self, value, state):
182 182 # TODO WRITE VALIDATIONS
183 183 group_name = value.get('group_name')
184 184 group_parent_id = value.get('group_parent_id')
185 185
186 186 # slugify repo group just in case :)
187 187 slug = repo_name_slug(group_name)
188 188
189 189 # check for parent of self
190 190 parent_of_self = lambda: (
191 191 old_data['group_id'] == int(group_parent_id)
192 192 if group_parent_id else False
193 193 )
194 194 if edit and parent_of_self():
195 195 msg = M(self, 'group_parent_id', state)
196 196 raise formencode.Invalid(msg, value, state,
197 197 error_dict=dict(group_parent_id=msg)
198 198 )
199 199
200 200 old_gname = None
201 201 if edit:
202 202 old_gname = RepoGroup.get(old_data.get('group_id')).group_name
203 203
204 204 if old_gname != group_name or not edit:
205 205
206 206 # check group
207 207 gr = RepoGroup.query()\
208 208 .filter(RepoGroup.group_name == slug)\
209 209 .filter(RepoGroup.group_parent_id == group_parent_id)\
210 210 .scalar()
211 211
212 212 if gr:
213 213 msg = M(self, 'group_exists', state, group_name=slug)
214 214 raise formencode.Invalid(msg, value, state,
215 215 error_dict=dict(group_name=msg)
216 216 )
217 217
218 218 # check for same repo
219 219 repo = Repository.query()\
220 220 .filter(Repository.repo_name == slug)\
221 221 .scalar()
222 222
223 223 if repo:
224 224 msg = M(self, 'repo_exists', state, group_name=slug)
225 225 raise formencode.Invalid(msg, value, state,
226 226 error_dict=dict(group_name=msg)
227 227 )
228 228
229 229 return _validator
230 230
231 231
232 232 def ValidPassword():
233 233 class _validator(formencode.validators.FancyValidator):
234 234 messages = {
235 235 'invalid_password':
236 236 _(u'Invalid characters (non-ascii) in password')
237 237 }
238 238
239 239 def validate_python(self, value, state):
240 240 try:
241 241 (value or '').decode('ascii')
242 242 except UnicodeError:
243 243 msg = M(self, 'invalid_password', state)
244 244 raise formencode.Invalid(msg, value, state,)
245 245 return _validator
246 246
247 247
248 248 def ValidPasswordsMatch():
249 249 class _validator(formencode.validators.FancyValidator):
250 250 messages = {
251 251 'password_mismatch': _(u'Passwords do not match'),
252 252 }
253 253
254 254 def validate_python(self, value, state):
255 255
256 256 pass_val = value.get('password') or value.get('new_password')
257 257 if pass_val != value['password_confirmation']:
258 258 msg = M(self, 'password_mismatch', state)
259 259 raise formencode.Invalid(msg, value, state,
260 260 error_dict=dict(password_confirmation=msg)
261 261 )
262 262 return _validator
263 263
264 264
265 265 def ValidAuth():
266 266 class _validator(formencode.validators.FancyValidator):
267 267 messages = {
268 268 'invalid_password': _(u'invalid password'),
269 269 'invalid_username': _(u'invalid user name'),
270 270 'disabled_account': _(u'Your account is disabled')
271 271 }
272 272
273 273 def validate_python(self, value, state):
274 274 from rhodecode.lib.auth import authenticate
275 275
276 276 password = value['password']
277 277 username = value['username']
278 278
279 279 if not authenticate(username, password):
280 280 user = User.get_by_username(username)
281 281 if user and user.active is False:
282 282 log.warning('user %s is disabled' % username)
283 283 msg = M(self, 'disabled_account', state)
284 284 raise formencode.Invalid(msg, value, state,
285 285 error_dict=dict(username=msg)
286 286 )
287 287 else:
288 288 log.warning('user %s failed to authenticate' % username)
289 289 msg = M(self, 'invalid_username', state)
290 290 msg2 = M(self, 'invalid_password', state)
291 291 raise formencode.Invalid(msg, value, state,
292 292 error_dict=dict(username=msg, password=msg2)
293 293 )
294 294 return _validator
295 295
296 296
297 297 def ValidAuthToken():
298 298 class _validator(formencode.validators.FancyValidator):
299 299 messages = {
300 300 'invalid_token': _(u'Token mismatch')
301 301 }
302 302
303 303 def validate_python(self, value, state):
304 304 if value != authentication_token():
305 305 msg = M(self, 'invalid_token', state)
306 306 raise formencode.Invalid(msg, value, state)
307 307 return _validator
308 308
309 309
310 310 def ValidRepoName(edit=False, old_data={}):
311 311 class _validator(formencode.validators.FancyValidator):
312 312 messages = {
313 313 'invalid_repo_name':
314 314 _(u'Repository name %(repo)s is disallowed'),
315 315 'repository_exists':
316 316 _(u'Repository named %(repo)s already exists'),
317 317 'repository_in_group_exists': _(u'Repository "%(repo)s" already '
318 318 'exists in group "%(group)s"'),
319 319 'same_group_exists': _(u'Repositories group with name "%(repo)s" '
320 320 'already exists')
321 321 }
322 322
323 323 def _to_python(self, value, state):
324 324 repo_name = repo_name_slug(value.get('repo_name', ''))
325 325 repo_group = value.get('repo_group')
326 326 if repo_group:
327 327 gr = RepoGroup.get(repo_group)
328 328 group_path = gr.full_path
329 329 group_name = gr.group_name
330 330 # value needs to be aware of group name in order to check
331 331 # db key This is an actual just the name to store in the
332 332 # database
333 333 repo_name_full = group_path + RepoGroup.url_sep() + repo_name
334 334 else:
335 335 group_name = group_path = ''
336 336 repo_name_full = repo_name
337 337
338 338 value['repo_name'] = repo_name
339 339 value['repo_name_full'] = repo_name_full
340 340 value['group_path'] = group_path
341 341 value['group_name'] = group_name
342 342 return value
343 343
344 344 def validate_python(self, value, state):
345 345
346 346 repo_name = value.get('repo_name')
347 347 repo_name_full = value.get('repo_name_full')
348 348 group_path = value.get('group_path')
349 349 group_name = value.get('group_name')
350 350
351 351 if repo_name in [ADMIN_PREFIX, '']:
352 352 msg = M(self, 'invalid_repo_name', state, repo=repo_name)
353 353 raise formencode.Invalid(msg, value, state,
354 354 error_dict=dict(repo_name=msg)
355 355 )
356 356
357 357 rename = old_data.get('repo_name') != repo_name_full
358 358 create = not edit
359 359 if rename or create:
360 360
361 361 if group_path != '':
362 362 if Repository.get_by_repo_name(repo_name_full):
363 363 msg = M(self, 'repository_in_group_exists', state,
364 364 repo=repo_name, group=group_name)
365 365 raise formencode.Invalid(msg, value, state,
366 366 error_dict=dict(repo_name=msg)
367 367 )
368 368 elif RepoGroup.get_by_group_name(repo_name_full):
369 369 msg = M(self, 'same_group_exists', state,
370 370 repo=repo_name)
371 371 raise formencode.Invalid(msg, value, state,
372 372 error_dict=dict(repo_name=msg)
373 373 )
374 374
375 375 elif Repository.get_by_repo_name(repo_name_full):
376 376 msg = M(self, 'repository_exists', state,
377 377 repo=repo_name)
378 378 raise formencode.Invalid(msg, value, state,
379 379 error_dict=dict(repo_name=msg)
380 380 )
381 381 return value
382 382 return _validator
383 383
384 384
385 385 def ValidForkName(*args, **kwargs):
386 386 return ValidRepoName(*args, **kwargs)
387 387
388 388
389 389 def SlugifyName():
390 390 class _validator(formencode.validators.FancyValidator):
391 391
392 392 def _to_python(self, value, state):
393 393 return repo_name_slug(value)
394 394
395 395 def validate_python(self, value, state):
396 396 pass
397 397
398 398 return _validator
399 399
400 400
401 401 def ValidCloneUri():
402 402 from rhodecode.lib.utils import make_ui
403 403
404 404 def url_handler(repo_type, url, ui=None):
405 405 if repo_type == 'hg':
406 406 from rhodecode.lib.vcs.backends.hg.repository import MercurialRepository
407 407 from mercurial.httppeer import httppeer
408 408 if url.startswith('http'):
409 409 ## initially check if it's at least the proper URL
410 410 ## or does it pass basic auth
411 411 MercurialRepository._check_url(url)
412 412 httppeer(ui, url)._capabilities()
413 413 elif url.startswith('svn+http'):
414 414 from hgsubversion.svnrepo import svnremoterepo
415 415 svnremoterepo(ui, url).capabilities
416 416 elif url.startswith('git+http'):
417 417 raise NotImplementedError()
418 418
419 419 elif repo_type == 'git':
420 420 from rhodecode.lib.vcs.backends.git.repository import GitRepository
421 421 if url.startswith('http'):
422 422 ## initially check if it's at least the proper URL
423 423 ## or does it pass basic auth
424 424 GitRepository._check_url(url)
425 425 elif url.startswith('svn+http'):
426 426 raise NotImplementedError()
427 427 elif url.startswith('hg+http'):
428 428 raise NotImplementedError()
429 429
430 430 class _validator(formencode.validators.FancyValidator):
431 431 messages = {
432 432 'clone_uri': _(u'invalid clone url'),
433 433 'invalid_clone_uri': _(u'Invalid clone url, provide a '
434 434 'valid clone http(s)/svn+http(s) url')
435 435 }
436 436
437 437 def validate_python(self, value, state):
438 438 repo_type = value.get('repo_type')
439 439 url = value.get('clone_uri')
440 440
441 441 if not url:
442 442 pass
443 443 else:
444 444 try:
445 445 url_handler(repo_type, url, make_ui('db', clear_session=False))
446 446 except Exception:
447 447 log.exception('Url validation failed')
448 448 msg = M(self, 'clone_uri')
449 449 raise formencode.Invalid(msg, value, state,
450 450 error_dict=dict(clone_uri=msg)
451 451 )
452 452 return _validator
453 453
454 454
455 455 def ValidForkType(old_data={}):
456 456 class _validator(formencode.validators.FancyValidator):
457 457 messages = {
458 458 'invalid_fork_type': _(u'Fork have to be the same type as parent')
459 459 }
460 460
461 461 def validate_python(self, value, state):
462 462 if old_data['repo_type'] != value:
463 463 msg = M(self, 'invalid_fork_type', state)
464 464 raise formencode.Invalid(msg, value, state,
465 465 error_dict=dict(repo_type=msg)
466 466 )
467 467 return _validator
468 468
469 469
470 470 def CanWriteGroup():
471 471 class _validator(formencode.validators.FancyValidator):
472 472 messages = {
473 473 'permission_denied': _(u"You don't have permissions "
474 474 "to create repository in this group")
475 475 }
476 476
477 477 def validate_python(self, value, state):
478 478 gr = RepoGroup.get(value)
479 479 if not HasReposGroupPermissionAny(
480 480 'group.write', 'group.admin'
481 481 )(gr.group_name, 'get group of repo form'):
482 482 msg = M(self, 'permission_denied', state)
483 483 raise formencode.Invalid(msg, value, state,
484 484 error_dict=dict(repo_type=msg)
485 485 )
486 486 return _validator
487 487
488 488
489 489 def ValidPerms(type_='repo'):
490 490 if type_ == 'group':
491 491 EMPTY_PERM = 'group.none'
492 492 elif type_ == 'repo':
493 493 EMPTY_PERM = 'repository.none'
494 494
495 495 class _validator(formencode.validators.FancyValidator):
496 496 messages = {
497 497 'perm_new_member_name':
498 498 _(u'This username or users group name is not valid')
499 499 }
500 500
501 501 def to_python(self, value, state):
502 502 perms_update = OrderedSet()
503 503 perms_new = OrderedSet()
504 504 # build a list of permission to update and new permission to create
505 505
506 506 #CLEAN OUT ORG VALUE FROM NEW MEMBERS, and group them using
507 507 new_perms_group = defaultdict(dict)
508 508 for k, v in value.copy().iteritems():
509 509 if k.startswith('perm_new_member'):
510 510 del value[k]
511 511 _type, part = k.split('perm_new_member_')
512 512 args = part.split('_')
513 513 if len(args) == 1:
514 514 new_perms_group[args[0]]['perm'] = v
515 515 elif len(args) == 2:
516 516 _key, pos = args
517 517 new_perms_group[pos][_key] = v
518 518
519 519 # fill new permissions in order of how they were added
520 520 for k in sorted(map(int, new_perms_group.keys())):
521 521 perm_dict = new_perms_group[str(k)]
522 522 new_member = perm_dict.get('name')
523 523 new_perm = perm_dict.get('perm')
524 524 new_type = perm_dict.get('type')
525 525 if new_member and new_perm and new_type:
526 526 perms_new.add((new_member, new_perm, new_type))
527 527
528 528 for k, v in value.iteritems():
529 529 if k.startswith('u_perm_') or k.startswith('g_perm_'):
530 530 member = k[7:]
531 531 t = {'u': 'user',
532 532 'g': 'users_group'
533 533 }[k[0]]
534 534 if member == 'default':
535 535 if value.get('private'):
536 536 # set none for default when updating to
537 537 # private repo
538 538 v = EMPTY_PERM
539 539 perms_update.add((member, v, t))
540 540
541 541 value['perms_updates'] = list(perms_update)
542 542 value['perms_new'] = list(perms_new)
543 543
544 544 # update permissions
545 545 for k, v, t in perms_new:
546 546 try:
547 547 if t is 'user':
548 548 self.user_db = User.query()\
549 549 .filter(User.active == True)\
550 550 .filter(User.username == k).one()
551 551 if t is 'users_group':
552 552 self.user_db = UsersGroup.query()\
553 553 .filter(UsersGroup.users_group_active == True)\
554 554 .filter(UsersGroup.users_group_name == k).one()
555 555
556 556 except Exception:
557 557 log.exception('Updated permission failed')
558 558 msg = M(self, 'perm_new_member_type', state)
559 559 raise formencode.Invalid(msg, value, state,
560 560 error_dict=dict(perm_new_member_name=msg)
561 561 )
562 562 return value
563 563 return _validator
564 564
565 565
566 566 def ValidSettings():
567 567 class _validator(formencode.validators.FancyValidator):
568 568 def _to_python(self, value, state):
569 # settings form can't edit user
570 if 'user' in value:
571 del value['user']
569 # settings form for users that are not admin
570 # can't edit certain parameters, it's extra backup if they mangle
571 # with forms
572
573 forbidden_params = [
574 'user', 'repo_type', 'repo_enable_locking',
575 'repo_enable_downloads', 'repo_enable_statistics'
576 ]
577
578 for param in forbidden_params:
579 if param in value:
580 del value[param]
572 581 return value
573 582
574 583 def validate_python(self, value, state):
575 584 pass
576 585 return _validator
577 586
578 587
579 588 def ValidPath():
580 589 class _validator(formencode.validators.FancyValidator):
581 590 messages = {
582 591 'invalid_path': _(u'This is not a valid path')
583 592 }
584 593
585 594 def validate_python(self, value, state):
586 595 if not os.path.isdir(value):
587 596 msg = M(self, 'invalid_path', state)
588 597 raise formencode.Invalid(msg, value, state,
589 598 error_dict=dict(paths_root_path=msg)
590 599 )
591 600 return _validator
592 601
593 602
594 603 def UniqSystemEmail(old_data={}):
595 604 class _validator(formencode.validators.FancyValidator):
596 605 messages = {
597 606 'email_taken': _(u'This e-mail address is already taken')
598 607 }
599 608
600 609 def _to_python(self, value, state):
601 610 return value.lower()
602 611
603 612 def validate_python(self, value, state):
604 613 if (old_data.get('email') or '').lower() != value:
605 614 user = User.get_by_email(value, case_insensitive=True)
606 615 if user:
607 616 msg = M(self, 'email_taken', state)
608 617 raise formencode.Invalid(msg, value, state,
609 618 error_dict=dict(email=msg)
610 619 )
611 620 return _validator
612 621
613 622
614 623 def ValidSystemEmail():
615 624 class _validator(formencode.validators.FancyValidator):
616 625 messages = {
617 626 'non_existing_email': _(u'e-mail "%(email)s" does not exist.')
618 627 }
619 628
620 629 def _to_python(self, value, state):
621 630 return value.lower()
622 631
623 632 def validate_python(self, value, state):
624 633 user = User.get_by_email(value, case_insensitive=True)
625 634 if user is None:
626 635 msg = M(self, 'non_existing_email', state, email=value)
627 636 raise formencode.Invalid(msg, value, state,
628 637 error_dict=dict(email=msg)
629 638 )
630 639
631 640 return _validator
632 641
633 642
634 643 def LdapLibValidator():
635 644 class _validator(formencode.validators.FancyValidator):
636 645 messages = {
637 646
638 647 }
639 648
640 649 def validate_python(self, value, state):
641 650 try:
642 651 import ldap
643 652 ldap # pyflakes silence !
644 653 except ImportError:
645 654 raise LdapImportError()
646 655
647 656 return _validator
648 657
649 658
650 659 def AttrLoginValidator():
651 660 class _validator(formencode.validators.FancyValidator):
652 661 messages = {
653 662 'invalid_cn':
654 663 _(u'The LDAP Login attribute of the CN must be specified - '
655 664 'this is the name of the attribute that is equivalent '
656 665 'to "username"')
657 666 }
658 667
659 668 def validate_python(self, value, state):
660 669 if not value or not isinstance(value, (str, unicode)):
661 670 msg = M(self, 'invalid_cn', state)
662 671 raise formencode.Invalid(msg, value, state,
663 672 error_dict=dict(ldap_attr_login=msg)
664 673 )
665 674
666 675 return _validator
667 676
668 677
669 678 def NotReviewedRevisions(repo_id):
670 679 class _validator(formencode.validators.FancyValidator):
671 680 messages = {
672 681 'rev_already_reviewed':
673 682 _(u'Revisions %(revs)s are already part of pull request '
674 683 'or have set status')
675 684 }
676 685
677 686 def validate_python(self, value, state):
678 687 # check revisions if they are not reviewed, or a part of another
679 688 # pull request
680 689 statuses = ChangesetStatus.query()\
681 690 .filter(ChangesetStatus.revision.in_(value))\
682 691 .filter(ChangesetStatus.repo_id == repo_id)\
683 692 .all()
684 693
685 694 errors = []
686 695 for cs in statuses:
687 696 if cs.pull_request_id:
688 697 errors.append(['pull_req', cs.revision[:12]])
689 698 elif cs.status:
690 699 errors.append(['status', cs.revision[:12]])
691 700
692 701 if errors:
693 702 revs = ','.join([x[1] for x in errors])
694 703 msg = M(self, 'rev_already_reviewed', state, revs=revs)
695 704 raise formencode.Invalid(msg, value, state,
696 705 error_dict=dict(revisions=revs)
697 706 )
698 707
699 708 return _validator
@@ -1,124 +1,132 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3
4 4 <%def name="title()">
5 5 ${_('%s Settings') % c.repo_name} - ${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 ${_('Settings')}
14 14 </%def>
15 15
16 16 <%def name="page_nav()">
17 17 ${self.menu('settings')}
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_settings_update', repo_name=c.repo_info.repo_name),method='put')}
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">${_('Name')}:</label>
32 32 </div>
33 33 <div class="input input-medium">
34 34 ${h.text('repo_name',class_="small")}
35 35 </div>
36 36 </div>
37 37 <div class="field">
38 38 <div class="label">
39 39 <label for="clone_uri">${_('Clone uri')}:</label>
40 40 </div>
41 41 <div class="input">
42 42 ${h.text('clone_uri',class_="medium")}
43 43 <span class="help-block">${_('Optional http[s] url from which repository should be cloned.')}</span>
44 44 </div>
45 45 </div>
46 46 <div class="field">
47 47 <div class="label">
48 48 <label for="repo_group">${_('Repository group')}:</label>
49 49 </div>
50 50 <div class="input">
51 51 ${h.select('repo_group','',c.repo_groups,class_="medium")}
52 52 <span class="help-block">${_('Optional select a group to put this repository into.')}</span>
53 53 </div>
54 54 </div>
55 55 <div class="field">
56 56 <div class="label">
57 57 <label for="landing_rev">${_('Landing revision')}:</label>
58 58 </div>
59 59 <div class="input">
60 ${h.select('landing_rev','',c.landing_revs,class_="medium")}
60 ${h.select('repo_landing_rev','',c.landing_revs,class_="medium")}
61 61 <span class="help-block">${_('Default revision for files page, downloads, whoosh and readme')}</span>
62 62 </div>
63 63 </div>
64 64 <div class="field">
65 65 <div class="label label-textarea">
66 <label for="description">${_('Description')}:</label>
66 <label for="repo_description">${_('Description')}:</label>
67 67 </div>
68 68 <div class="textarea text-area editor">
69 ${h.textarea('description')}
69 ${h.textarea('repo_description')}
70 70 <span class="help-block">${_('Keep it short and to the point. Use a README file for longer descriptions.')}</span>
71 71 </div>
72 72 </div>
73 73
74 74 <div class="field">
75 75 <div class="label label-checkbox">
76 <label for="private">${_('Private repository')}:</label>
76 <label for="repo_private">${_('Private repository')}:</label>
77 77 </div>
78 78 <div class="checkboxes">
79 ${h.checkbox('private',value="True")}
79 ${h.checkbox('repo_private',value="True")}
80 80 <span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span>
81 81 </div>
82 82 </div>
83 83
84 84 <div class="field">
85 85 <div class="label">
86 86 <label for="">${_('Permissions')}:</label>
87 87 </div>
88 88 <div class="input">
89 89 <%include file="../admin/repos/repo_edit_perms.html"/>
90 90 </div>
91 91 </div>
92
93 <div class="field">
94 <div class="label">
95 <label for="">${_('Remove repo')}:</label>
96 </div>
97 <div class="checkboxes">
98 ${h.form(url('repo_settings_delete', repo_name=c.repo_info.repo_name),method='delete')}
99 <div class="">
100 <div class="fields">
101 ${h.submit('remove_%s' % c.repo_info.repo_name,_('Remove this repository'),class_="ui-btn red",onclick="return confirm('"+_('Confirm to delete this repository')+"');")}
102 </div>
103 <div class="field" style="border:none;color:#888">
104 <ul>
105 <li>${_('''This repository will be renamed in a special way in order to be unaccesible for RhodeCode and VCS systems.
106 If you need fully delete it from file system please do it manually''')}
107 </li>
108 </ul>
109 </div>
110 </div>
111 ${h.end_form()}
112 </div>
113 </div>
114
92
115 93 <div class="buttons">
116 94 ${h.submit('save',_('Save'),class_="ui-btn large")}
117 95 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
118 96 </div>
119 97
120 98 </div>
121 99 ${h.end_form()}
122 100 </div>
101
102 <h3>${_('Delete repository')}</h3>
103 <div class="form">
104 <!-- fields -->
105 <div class="fields">
106
107 <div class="field">
108 <div class="label">
109 <label for="">${_('Remove repo')}:</label>
110 </div>
111 <div class="checkboxes">
112 ${h.form(url('repo_settings_delete', repo_name=c.repo_info.repo_name),method='delete')}
113 <div class="">
114 <div class="fields">
115 ${h.submit('remove_%s' % c.repo_info.repo_name,_('Remove this repository'),class_="ui-btn red",onclick="return confirm('"+_('Confirm to delete this repository')+"');")}
116 </div>
117 <div class="field" style="border:none;color:#888">
118 <ul>
119 <li>${_('''This repository will be renamed in a special way in order to be unaccesible for RhodeCode and VCS systems.
120 If you need fully delete it from file system please do it manually''')}
121 </li>
122 </ul>
123 </div>
124 </div>
125 ${h.end_form()}
126 </div>
127 </div>
128 </div>
129 </div>
130
123 131 </div>
124 132 </%def>
@@ -1,165 +1,165 b''
1 1 import os
2 2 import unittest
3 3 from rhodecode.tests import *
4 4
5 5 from rhodecode.model.repos_group import ReposGroupModel
6 6 from rhodecode.model.repo import RepoModel
7 7 from rhodecode.model.db import RepoGroup, User
8 8 from rhodecode.model.meta import Session
9 9 from sqlalchemy.exc import IntegrityError
10 10
11 11
12 12 def _make_group(path, desc='desc', parent_id=None,
13 13 skip_if_exists=False):
14 14
15 15 gr = RepoGroup.get_by_group_name(path)
16 16 if gr and skip_if_exists:
17 17 return gr
18 18 if isinstance(parent_id, RepoGroup):
19 19 parent_id = parent_id.group_id
20 20 gr = ReposGroupModel().create(path, desc, parent_id)
21 21 return gr
22 22
23 23
24 24 class TestReposGroups(unittest.TestCase):
25 25
26 26 def setUp(self):
27 27 self.g1 = _make_group('test1', skip_if_exists=True)
28 28 Session().commit()
29 29 self.g2 = _make_group('test2', skip_if_exists=True)
30 30 Session().commit()
31 31 self.g3 = _make_group('test3', skip_if_exists=True)
32 32 Session().commit()
33 33
34 34 def tearDown(self):
35 35 print 'out'
36 36
37 37 def __check_path(self, *path):
38 38 """
39 39 Checks the path for existance !
40 40 """
41 41 path = [TESTS_TMP_PATH] + list(path)
42 42 path = os.path.join(*path)
43 43 return os.path.isdir(path)
44 44
45 45 def _check_folders(self):
46 46 print os.listdir(TESTS_TMP_PATH)
47 47
48 48 def __delete_group(self, id_):
49 49 ReposGroupModel().delete(id_)
50 50
51 51 def __update_group(self, id_, path, desc='desc', parent_id=None):
52 52 form_data = dict(
53 53 group_name=path,
54 54 group_description=desc,
55 55 group_parent_id=parent_id,
56 56 perms_updates=[],
57 57 perms_new=[],
58 58 enable_locking=False,
59 59 recursive=False
60 60 )
61 61 gr = ReposGroupModel().update(id_, form_data)
62 62 return gr
63 63
64 64 def test_create_group(self):
65 65 g = _make_group('newGroup')
66 66 self.assertEqual(g.full_path, 'newGroup')
67 67
68 68 self.assertTrue(self.__check_path('newGroup'))
69 69
70 70 def test_create_same_name_group(self):
71 71 self.assertRaises(IntegrityError, lambda: _make_group('newGroup'))
72 72 Session().rollback()
73 73
74 74 def test_same_subgroup(self):
75 75 sg1 = _make_group('sub1', parent_id=self.g1.group_id)
76 76 self.assertEqual(sg1.parent_group, self.g1)
77 77 self.assertEqual(sg1.full_path, 'test1/sub1')
78 78 self.assertTrue(self.__check_path('test1', 'sub1'))
79 79
80 80 ssg1 = _make_group('subsub1', parent_id=sg1.group_id)
81 81 self.assertEqual(ssg1.parent_group, sg1)
82 82 self.assertEqual(ssg1.full_path, 'test1/sub1/subsub1')
83 83 self.assertTrue(self.__check_path('test1', 'sub1', 'subsub1'))
84 84
85 85 def test_remove_group(self):
86 86 sg1 = _make_group('deleteme')
87 87 self.__delete_group(sg1.group_id)
88 88
89 89 self.assertEqual(RepoGroup.get(sg1.group_id), None)
90 90 self.assertFalse(self.__check_path('deteteme'))
91 91
92 92 sg1 = _make_group('deleteme', parent_id=self.g1.group_id)
93 93 self.__delete_group(sg1.group_id)
94 94
95 95 self.assertEqual(RepoGroup.get(sg1.group_id), None)
96 96 self.assertFalse(self.__check_path('test1', 'deteteme'))
97 97
98 98 def test_rename_single_group(self):
99 99 sg1 = _make_group('initial')
100 100
101 101 new_sg1 = self.__update_group(sg1.group_id, 'after')
102 102 self.assertTrue(self.__check_path('after'))
103 103 self.assertEqual(RepoGroup.get_by_group_name('initial'), None)
104 104
105 105 def test_update_group_parent(self):
106 106
107 107 sg1 = _make_group('initial', parent_id=self.g1.group_id)
108 108
109 109 new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g1.group_id)
110 110 self.assertTrue(self.__check_path('test1', 'after'))
111 111 self.assertEqual(RepoGroup.get_by_group_name('test1/initial'), None)
112 112
113 113 new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g3.group_id)
114 114 self.assertTrue(self.__check_path('test3', 'after'))
115 115 self.assertEqual(RepoGroup.get_by_group_name('test3/initial'), None)
116 116
117 117 new_sg1 = self.__update_group(sg1.group_id, 'hello')
118 118 self.assertTrue(self.__check_path('hello'))
119 119
120 120 self.assertEqual(RepoGroup.get_by_group_name('hello'), new_sg1)
121 121
122 122 def test_subgrouping_with_repo(self):
123 123
124 124 g1 = _make_group('g1')
125 125 g2 = _make_group('g2')
126 126
127 127 # create new repo
128 128 form_data = _get_repo_create_params(repo_name='john')
129 129 cur_user = User.get_by_username(TEST_USER_ADMIN_LOGIN)
130 130 r = RepoModel().create(form_data, cur_user)
131 131
132 132 self.assertEqual(r.repo_name, 'john')
133 133
134 134 # put repo into group
135 135 form_data = form_data
136 136 form_data['repo_group'] = g1.group_id
137 137 form_data['perms_new'] = []
138 138 form_data['perms_updates'] = []
139 RepoModel().update(r.repo_name, form_data)
139 RepoModel().update(r.repo_name, **form_data)
140 140 self.assertEqual(r.repo_name, 'g1/john')
141 141
142 142 self.__update_group(g1.group_id, 'g1', parent_id=g2.group_id)
143 143 self.assertTrue(self.__check_path('g2', 'g1'))
144 144
145 145 # test repo
146 146 self.assertEqual(r.repo_name, RepoGroup.url_sep().join(['g2', 'g1',
147 147 r.just_name]))
148 148
149 149 def test_move_to_root(self):
150 150 g1 = _make_group('t11')
151 151 Session().commit()
152 152 g2 = _make_group('t22', parent_id=g1.group_id)
153 153 Session().commit()
154 154
155 155 self.assertEqual(g2.full_path, 't11/t22')
156 156 self.assertTrue(self.__check_path('t11', 't22'))
157 157
158 158 g2 = self.__update_group(g2.group_id, 'g22', parent_id=None)
159 159 Session().commit()
160 160
161 161 self.assertEqual(g2.group_name, 'g22')
162 162 # we moved out group from t1 to '' so it's full path should be 'g2'
163 163 self.assertEqual(g2.full_path, 'g22')
164 164 self.assertFalse(self.__check_path('t11', 't22'))
165 165 self.assertTrue(self.__check_path('g22'))
General Comments 0
You need to be logged in to leave comments. Login now