##// END OF EJS Templates
fixed #47 adding a new repo that have a group chosen had wrong paths.
marcink -
r1361:87ca1754 beta
parent child Browse files
Show More
@@ -1,420 +1,434 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.admin.repos
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Admin controller for RhodeCode
7 7
8 8 :created_on: Apr 7, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 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 operator import itemgetter
30 30 from formencode import htmlfill
31 31
32 32 from paste.httpexceptions import HTTPInternalServerError
33 33 from pylons import request, response, session, tmpl_context as c, url
34 34 from pylons.controllers.util import abort, redirect
35 35 from pylons.i18n.translation import _
36 36
37 37 from rhodecode.lib import helpers as h
38 38 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
39 39 HasPermissionAnyDecorator
40 40 from rhodecode.lib.base import BaseController, render
41 41 from rhodecode.lib.utils import invalidate_cache, action_logger, repo_name_slug
42 42 from rhodecode.lib.helpers import get_token
43 43 from rhodecode.model.db import User, Repository, UserFollowing, Group
44 44 from rhodecode.model.forms import RepoForm
45 45 from rhodecode.model.scm import ScmModel
46 46 from rhodecode.model.repo import RepoModel
47 from sqlalchemy.exc import IntegrityError
47 48
48 49 log = logging.getLogger(__name__)
49 50
50 51
51 52 class ReposController(BaseController):
52 53 """
53 54 REST Controller styled on the Atom Publishing Protocol"""
54 55 # To properly map this controller, ensure your config/routing.py
55 56 # file has a resource setup:
56 57 # map.resource('repo', 'repos')
57 58
58 59 @LoginRequired()
59 60 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
60 61 def __before__(self):
61 62 c.admin_user = session.get('admin_user')
62 63 c.admin_username = session.get('admin_username')
63 64 super(ReposController, self).__before__()
64 65
65 66 def __load_defaults(self):
66 67 repo_model = RepoModel()
67 68
68 69 c.repo_groups = [('', '')]
69 70 parents_link = lambda k: h.literal('&raquo;'.join(
70 71 map(lambda k: k.group_name,
71 72 k.parents + [k])
72 73 )
73 74 )
74 75
75 76 c.repo_groups.extend([(x.group_id, parents_link(x)) for \
76 77 x in self.sa.query(Group).all()])
77 78 c.repo_groups = sorted(c.repo_groups,
78 79 key=lambda t: t[1].split('&raquo;')[0])
79 80 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
80 81 c.users_array = repo_model.get_users_js()
81 82 c.users_groups_array = repo_model.get_users_groups_js()
82 83
83 84 def __load_data(self, repo_name=None):
84 85 """
85 86 Load defaults settings for edit, and update
86 87
87 88 :param repo_name:
88 89 """
89 90 self.__load_defaults()
90 91
91 92 repo, dbrepo = ScmModel().get(repo_name, retval='repo')
92 93
93 94 repo_model = RepoModel()
94 95 c.repo_info = repo_model.get_by_repo_name(repo_name)
95 96
96 97 if c.repo_info is None:
97 98 h.flash(_('%s repository is not mapped to db perhaps'
98 99 ' it was created or renamed from the filesystem'
99 100 ' please run the application again'
100 101 ' in order to rescan repositories') % repo_name,
101 102 category='error')
102 103
103 104 return redirect(url('repos'))
104 105
105 106 c.default_user_id = User.by_username('default').user_id
106 107 c.in_public_journal = self.sa.query(UserFollowing)\
107 108 .filter(UserFollowing.user_id == c.default_user_id)\
108 109 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
109 110
110 111 if c.repo_info.stats:
111 112 last_rev = c.repo_info.stats.stat_on_revision
112 113 else:
113 114 last_rev = 0
114 115 c.stats_revision = last_rev
115 116
116 117 c.repo_last_rev = repo.count() - 1 if repo.revisions else 0
117 118
118 119 if last_rev == 0 or c.repo_last_rev == 0:
119 120 c.stats_percentage = 0
120 121 else:
121 122 c.stats_percentage = '%.2f' % ((float((last_rev)) /
122 123 c.repo_last_rev) * 100)
123 124
124 125 defaults = c.repo_info.get_dict()
125 126 group, repo_name = c.repo_info.groups_and_repo
126 127 defaults['repo_name'] = repo_name
127 128 defaults['repo_group'] = getattr(group[-1] if group else None,
128 129 'group_id', None)
129 130
130 131 #fill owner
131 132 if c.repo_info.user:
132 133 defaults.update({'user': c.repo_info.user.username})
133 134 else:
134 135 replacement_user = self.sa.query(User)\
135 136 .filter(User.admin == True).first().username
136 137 defaults.update({'user': replacement_user})
137 138
138 139 #fill repository users
139 140 for p in c.repo_info.repo_to_perm:
140 141 defaults.update({'u_perm_%s' % p.user.username:
141 142 p.permission.permission_name})
142 143
143 144 #fill repository groups
144 145 for p in c.repo_info.users_group_to_perm:
145 146 defaults.update({'g_perm_%s' % p.users_group.users_group_name:
146 147 p.permission.permission_name})
147 148
148 149 return defaults
149 150
150 151 @HasPermissionAllDecorator('hg.admin')
151 152 def index(self, format='html'):
152 153 """GET /repos: All items in the collection"""
153 154 # url('repos')
154 155
155 156 all_repos = [r.repo_name for r in Repository.query().all()]
156 157
157 158 cached_repo_list = ScmModel().get_repos(all_repos)
158 159 c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
159 160 return render('admin/repos/repos.html')
160 161
161 162 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
162 163 def create(self):
163 164 """
164 165 POST /repos: Create a new item"""
165 166 # url('repos')
166 167 repo_model = RepoModel()
167 168 self.__load_defaults()
168 169 form_result = {}
169 170 try:
170 171 form_result = RepoForm(repo_groups=c.repo_groups_choices)()\
171 172 .to_python(dict(request.POST))
172 173 repo_model.create(form_result, self.rhodecode_user)
173 174 if form_result['clone_uri']:
174 175 h.flash(_('created repository %s from %s') \
175 176 % (form_result['repo_name'], form_result['clone_uri']),
176 177 category='success')
177 178 else:
178 179 h.flash(_('created repository %s') % form_result['repo_name'],
179 180 category='success')
180 181
181 182 if request.POST.get('user_created'):
183 #created by regular non admin user
182 184 action_logger(self.rhodecode_user, 'user_created_repo',
183 form_result['repo_name'], '', self.sa)
185 form_result['repo_name_full'], '', self.sa)
184 186 else:
185 187 action_logger(self.rhodecode_user, 'admin_created_repo',
186 form_result['repo_name'], '', self.sa)
188 form_result['repo_name_full'], '', self.sa)
187 189
188 190 except formencode.Invalid, errors:
189 191
190 192 c.new_repo = errors.value['repo_name']
191 193
192 194 if request.POST.get('user_created'):
193 195 r = render('admin/repos/repo_add_create_repository.html')
194 196 else:
195 197 r = render('admin/repos/repo_add.html')
196 198
197 199 return htmlfill.render(
198 200 r,
199 201 defaults=errors.value,
200 202 errors=errors.error_dict or {},
201 203 prefix_error=False,
202 204 encoding="UTF-8")
203 205
204 206 except Exception:
205 207 log.error(traceback.format_exc())
206 208 msg = _('error occurred during creation of repository %s') \
207 209 % form_result.get('repo_name')
208 210 h.flash(msg, category='error')
209 211 if request.POST.get('user_created'):
210 212 return redirect(url('home'))
211 213 return redirect(url('repos'))
212 214
213 215 @HasPermissionAllDecorator('hg.admin')
214 216 def new(self, format='html'):
215 217 """GET /repos/new: Form to create a new item"""
216 218 new_repo = request.GET.get('repo', '')
217 219 c.new_repo = repo_name_slug(new_repo)
218 220 self.__load_defaults()
219 221 return render('admin/repos/repo_add.html')
220 222
221 223 @HasPermissionAllDecorator('hg.admin')
222 224 def update(self, repo_name):
223 225 """
224 226 PUT /repos/repo_name: Update an existing item"""
225 227 # Forms posted to this method should contain a hidden field:
226 228 # <input type="hidden" name="_method" value="PUT" />
227 229 # Or using helpers:
228 230 # h.form(url('repo', repo_name=ID),
229 231 # method='put')
230 232 # url('repo', repo_name=ID)
231 233 self.__load_defaults()
232 234 repo_model = RepoModel()
233 235 changed_name = repo_name
234 236 _form = RepoForm(edit=True, old_data={'repo_name': repo_name},
235 237 repo_groups=c.repo_groups_choices)()
236 238 try:
237 239 form_result = _form.to_python(dict(request.POST))
238 240 repo_model.update(repo_name, form_result)
239 241 invalidate_cache('get_repo_cached_%s' % repo_name)
240 242 h.flash(_('Repository %s updated successfully' % repo_name),
241 243 category='success')
242 244 changed_name = form_result['repo_name_full']
243 245 action_logger(self.rhodecode_user, 'admin_updated_repo',
244 246 changed_name, '', self.sa)
245 247
246 248 except formencode.Invalid, errors:
247 249 defaults = self.__load_data(repo_name)
248 250 defaults.update(errors.value)
249 251 return htmlfill.render(
250 252 render('admin/repos/repo_edit.html'),
251 253 defaults=defaults,
252 254 errors=errors.error_dict or {},
253 255 prefix_error=False,
254 256 encoding="UTF-8")
255 257
256 258 except Exception:
257 259 log.error(traceback.format_exc())
258 260 h.flash(_('error occurred during update of repository %s') \
259 261 % repo_name, category='error')
260 262 return redirect(url('edit_repo', repo_name=changed_name))
261 263
262 264 @HasPermissionAllDecorator('hg.admin')
263 265 def delete(self, repo_name):
264 266 """
265 267 DELETE /repos/repo_name: Delete an existing item"""
266 268 # Forms posted to this method should contain a hidden field:
267 269 # <input type="hidden" name="_method" value="DELETE" />
268 270 # Or using helpers:
269 271 # h.form(url('repo', repo_name=ID),
270 272 # method='delete')
271 273 # url('repo', repo_name=ID)
272 274
273 275 repo_model = RepoModel()
274 276 repo = repo_model.get_by_repo_name(repo_name)
275 277 if not repo:
276 278 h.flash(_('%s repository is not mapped to db perhaps'
277 279 ' it was moved or renamed from the filesystem'
278 280 ' please run the application again'
279 281 ' in order to rescan repositories') % repo_name,
280 282 category='error')
281 283
282 284 return redirect(url('repos'))
283 285 try:
284 286 action_logger(self.rhodecode_user, 'admin_deleted_repo',
285 287 repo_name, '', self.sa)
286 288 repo_model.delete(repo)
287 289 invalidate_cache('get_repo_cached_%s' % repo_name)
288 290 h.flash(_('deleted repository %s') % repo_name, category='success')
289 291
292 except IntegrityError, e:
293 if e.message.find('repositories_fork_id_fkey'):
294 log.error(traceback.format_exc())
295 h.flash(_('Cannot delete %s it still contains attached '
296 'forks') % repo_name,
297 category='warning')
298 else:
299 log.error(traceback.format_exc())
300 h.flash(_('An error occurred during '
301 'deletion of %s') % repo_name,
302 category='error')
303
290 304 except Exception, e:
291 305 log.error(traceback.format_exc())
292 306 h.flash(_('An error occurred during deletion of %s') % repo_name,
293 307 category='error')
294 308
295 309 return redirect(url('repos'))
296 310
297 311 @HasPermissionAllDecorator('hg.admin')
298 312 def delete_perm_user(self, repo_name):
299 313 """
300 314 DELETE an existing repository permission user
301 315
302 316 :param repo_name:
303 317 """
304 318
305 319 try:
306 320 repo_model = RepoModel()
307 321 repo_model.delete_perm_user(request.POST, repo_name)
308 322 except Exception, e:
309 323 h.flash(_('An error occurred during deletion of repository user'),
310 324 category='error')
311 325 raise HTTPInternalServerError()
312 326
313 327 @HasPermissionAllDecorator('hg.admin')
314 328 def delete_perm_users_group(self, repo_name):
315 329 """
316 330 DELETE an existing repository permission users group
317 331
318 332 :param repo_name:
319 333 """
320 334 try:
321 335 repo_model = RepoModel()
322 336 repo_model.delete_perm_users_group(request.POST, repo_name)
323 337 except Exception, e:
324 338 h.flash(_('An error occurred during deletion of repository'
325 339 ' users groups'),
326 340 category='error')
327 341 raise HTTPInternalServerError()
328 342
329 343 @HasPermissionAllDecorator('hg.admin')
330 344 def repo_stats(self, repo_name):
331 345 """
332 346 DELETE an existing repository statistics
333 347
334 348 :param repo_name:
335 349 """
336 350
337 351 try:
338 352 repo_model = RepoModel()
339 353 repo_model.delete_stats(repo_name)
340 354 except Exception, e:
341 355 h.flash(_('An error occurred during deletion of repository stats'),
342 356 category='error')
343 357 return redirect(url('edit_repo', repo_name=repo_name))
344 358
345 359 @HasPermissionAllDecorator('hg.admin')
346 360 def repo_cache(self, repo_name):
347 361 """
348 362 INVALIDATE existing repository cache
349 363
350 364 :param repo_name:
351 365 """
352 366
353 367 try:
354 368 ScmModel().mark_for_invalidation(repo_name)
355 369 except Exception, e:
356 370 h.flash(_('An error occurred during cache invalidation'),
357 371 category='error')
358 372 return redirect(url('edit_repo', repo_name=repo_name))
359 373
360 374 @HasPermissionAllDecorator('hg.admin')
361 375 def repo_public_journal(self, repo_name):
362 376 """
363 377 Set's this repository to be visible in public journal,
364 378 in other words assing default user to follow this repo
365 379
366 380 :param repo_name:
367 381 """
368 382
369 383 cur_token = request.POST.get('auth_token')
370 384 token = get_token()
371 385 if cur_token == token:
372 386 try:
373 387 repo_id = Repository.by_repo_name(repo_name).repo_id
374 388 user_id = User.by_username('default').user_id
375 389 self.scm_model.toggle_following_repo(repo_id, user_id)
376 390 h.flash(_('Updated repository visibility in public journal'),
377 391 category='success')
378 392 except:
379 393 h.flash(_('An error occurred during setting this'
380 394 ' repository in public journal'),
381 395 category='error')
382 396
383 397 else:
384 398 h.flash(_('Token mismatch'), category='error')
385 399 return redirect(url('edit_repo', repo_name=repo_name))
386 400
387 401 @HasPermissionAllDecorator('hg.admin')
388 402 def repo_pull(self, repo_name):
389 403 """
390 404 Runs task to update given repository with remote changes,
391 405 ie. make pull on remote location
392 406
393 407 :param repo_name:
394 408 """
395 409 try:
396 410 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
397 411 h.flash(_('Pulled from remote location'), category='success')
398 412 except Exception, e:
399 413 h.flash(_('An error occurred during pull from remote location'),
400 414 category='error')
401 415
402 416 return redirect(url('edit_repo', repo_name=repo_name))
403 417
404 418 @HasPermissionAllDecorator('hg.admin')
405 419 def show(self, repo_name, format='html'):
406 420 """GET /repos/repo_name: Show a specific item"""
407 421 # url('repo', repo_name=ID)
408 422
409 423 @HasPermissionAllDecorator('hg.admin')
410 424 def edit(self, repo_name, format='html'):
411 425 """GET /repos/repo_name/edit: Form to edit an existing item"""
412 426 # url('edit_repo', repo_name=ID)
413 427 defaults = self.__load_data(repo_name)
414 428
415 429 return htmlfill.render(
416 430 render('admin/repos/repo_edit.html'),
417 431 defaults=defaults,
418 432 encoding="UTF-8",
419 433 force_defaults=False
420 434 )
@@ -1,606 +1,607 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.utils
4 4 ~~~~~~~~~~~~~~~~~~~
5 5
6 6 Utilities library for RhodeCode
7 7
8 8 :created_on: Apr 18, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 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 os
27 27 import logging
28 28 import datetime
29 29 import traceback
30 30 import paste
31 31 import beaker
32 32 from os.path import dirname as dn, join as jn
33 33
34 34 from paste.script.command import Command, BadCommand
35 35
36 36 from UserDict import DictMixin
37 37
38 38 from mercurial import ui, config, hg
39 39 from mercurial.error import RepoError
40 40
41 41 from webhelpers.text import collapse, remove_formatting, strip_tags
42 42
43 43 from vcs.backends.base import BaseChangeset
44 44 from vcs.utils.lazy import LazyProperty
45 45
46 46 from rhodecode.model import meta
47 47 from rhodecode.model.caching_query import FromCache
48 48 from rhodecode.model.db import Repository, User, RhodeCodeUi, UserLog, Group, \
49 49 RhodeCodeSettings
50 50 from rhodecode.model.repo import RepoModel
51 51 from rhodecode.model.user import UserModel
52 52
53 53 log = logging.getLogger(__name__)
54 54
55 55
56 56 def recursive_replace(str, replace=' '):
57 57 """Recursive replace of given sign to just one instance
58 58
59 59 :param str: given string
60 60 :param replace: char to find and replace multiple instances
61 61
62 62 Examples::
63 63 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
64 64 'Mighty-Mighty-Bo-sstones'
65 65 """
66 66
67 67 if str.find(replace * 2) == -1:
68 68 return str
69 69 else:
70 70 str = str.replace(replace * 2, replace)
71 71 return recursive_replace(str, replace)
72 72
73 73
74 74 def repo_name_slug(value):
75 75 """Return slug of name of repository
76 76 This function is called on each creation/modification
77 77 of repository to prevent bad names in repo
78 78 """
79 79
80 80 slug = remove_formatting(value)
81 81 slug = strip_tags(slug)
82 82
83 83 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
84 84 slug = slug.replace(c, '-')
85 85 slug = recursive_replace(slug, '-')
86 86 slug = collapse(slug, '-')
87 87 return slug
88 88
89 89
90 90 def get_repo_slug(request):
91 91 return request.environ['pylons.routes_dict'].get('repo_name')
92 92
93 93
94 94 def action_logger(user, action, repo, ipaddr='', sa=None):
95 95 """
96 96 Action logger for various actions made by users
97 97
98 98 :param user: user that made this action, can be a unique username string or
99 99 object containing user_id attribute
100 100 :param action: action to log, should be on of predefined unique actions for
101 101 easy translations
102 102 :param repo: string name of repository or object containing repo_id,
103 103 that action was made on
104 104 :param ipaddr: optional ip address from what the action was made
105 105 :param sa: optional sqlalchemy session
106 106
107 107 """
108 108
109 109 if not sa:
110 110 sa = meta.Session()
111 111
112 112 try:
113 113 um = UserModel()
114 114 if hasattr(user, 'user_id'):
115 115 user_obj = user
116 116 elif isinstance(user, basestring):
117 117 user_obj = um.get_by_username(user, cache=False)
118 118 else:
119 119 raise Exception('You have to provide user object or username')
120 120
121 121 rm = RepoModel()
122 122 if hasattr(repo, 'repo_id'):
123 123 repo_obj = rm.get(repo.repo_id, cache=False)
124 124 repo_name = repo_obj.repo_name
125 125 elif isinstance(repo, basestring):
126 126 repo_name = repo.lstrip('/')
127 127 repo_obj = rm.get_by_repo_name(repo_name, cache=False)
128 128 else:
129 129 raise Exception('You have to provide repository to action logger')
130 130
131 131 user_log = UserLog()
132 132 user_log.user_id = user_obj.user_id
133 133 user_log.action = action
134 134
135 135 user_log.repository_id = repo_obj.repo_id
136 136 user_log.repository_name = repo_name
137 137
138 138 user_log.action_date = datetime.datetime.now()
139 139 user_log.user_ip = ipaddr
140 140 sa.add(user_log)
141 141 sa.commit()
142 142
143 143 log.info('Adding user %s, action %s on %s', user_obj, action, repo)
144 144 except:
145 145 log.error(traceback.format_exc())
146 146 sa.rollback()
147 147
148 148
149 149 def get_repos(path, recursive=False):
150 150 """
151 151 Scans given path for repos and return (name,(type,path)) tuple
152 152
153 153 :param path: path to scann for repositories
154 154 :param recursive: recursive search and return names with subdirs in front
155 155 """
156 156 from vcs.utils.helpers import get_scm
157 157 from vcs.exceptions import VCSError
158 158
159 159 if path.endswith(os.sep):
160 160 #remove ending slash for better results
161 161 path = path[:-1]
162 162
163 163 def _get_repos(p):
164 164 if not os.access(p, os.W_OK):
165 165 return
166 166 for dirpath in os.listdir(p):
167 167 if os.path.isfile(os.path.join(p, dirpath)):
168 168 continue
169 169 cur_path = os.path.join(p, dirpath)
170 170 try:
171 171 scm_info = get_scm(cur_path)
172 172 yield scm_info[1].split(path)[-1].lstrip(os.sep), scm_info
173 173 except VCSError:
174 174 if not recursive:
175 175 continue
176 176 #check if this dir containts other repos for recursive scan
177 177 rec_path = os.path.join(p, dirpath)
178 178 if os.path.isdir(rec_path):
179 179 for inner_scm in _get_repos(rec_path):
180 180 yield inner_scm
181 181
182 182 return _get_repos(path)
183 183
184 184
185 185 def check_repo_fast(repo_name, base_path):
186 186 """
187 187 Check given path for existence of directory
188 188 :param repo_name:
189 189 :param base_path:
190 190
191 191 :return False: if this directory is present
192 192 """
193 193 if os.path.isdir(os.path.join(base_path, repo_name)):
194 194 return False
195 195 return True
196 196
197 197
198 198 def check_repo(repo_name, base_path, verify=True):
199 199
200 200 repo_path = os.path.join(base_path, repo_name)
201 201
202 202 try:
203 203 if not check_repo_fast(repo_name, base_path):
204 204 return False
205 205 r = hg.repository(ui.ui(), repo_path)
206 206 if verify:
207 207 hg.verify(r)
208 208 #here we hnow that repo exists it was verified
209 209 log.info('%s repo is already created', repo_name)
210 210 return False
211 211 except RepoError:
212 212 #it means that there is no valid repo there...
213 213 log.info('%s repo is free for creation', repo_name)
214 214 return True
215 215
216 216
217 217 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
218 218 while True:
219 219 ok = raw_input(prompt)
220 220 if ok in ('y', 'ye', 'yes'):
221 221 return True
222 222 if ok in ('n', 'no', 'nop', 'nope'):
223 223 return False
224 224 retries = retries - 1
225 225 if retries < 0:
226 226 raise IOError
227 227 print complaint
228 228
229 229 #propagated from mercurial documentation
230 230 ui_sections = ['alias', 'auth',
231 231 'decode/encode', 'defaults',
232 232 'diff', 'email',
233 233 'extensions', 'format',
234 234 'merge-patterns', 'merge-tools',
235 235 'hooks', 'http_proxy',
236 236 'smtp', 'patch',
237 237 'paths', 'profiling',
238 238 'server', 'trusted',
239 239 'ui', 'web', ]
240 240
241 241
242 242 def make_ui(read_from='file', path=None, checkpaths=True):
243 243 """A function that will read python rc files or database
244 244 and make an mercurial ui object from read options
245 245
246 246 :param path: path to mercurial config file
247 247 :param checkpaths: check the path
248 248 :param read_from: read from 'file' or 'db'
249 249 """
250 250
251 251 baseui = ui.ui()
252 252
253 253 #clean the baseui object
254 254 baseui._ocfg = config.config()
255 255 baseui._ucfg = config.config()
256 256 baseui._tcfg = config.config()
257 257
258 258 if read_from == 'file':
259 259 if not os.path.isfile(path):
260 260 log.warning('Unable to read config file %s' % path)
261 261 return False
262 262 log.debug('reading hgrc from %s', path)
263 263 cfg = config.config()
264 264 cfg.read(path)
265 265 for section in ui_sections:
266 266 for k, v in cfg.items(section):
267 267 log.debug('settings ui from file[%s]%s:%s', section, k, v)
268 268 baseui.setconfig(section, k, v)
269 269
270 270 elif read_from == 'db':
271 271 sa = meta.Session()
272 272 ret = sa.query(RhodeCodeUi)\
273 273 .options(FromCache("sql_cache_short",
274 274 "get_hg_ui_settings")).all()
275 275
276 276 hg_ui = ret
277 277 for ui_ in hg_ui:
278 278 if ui_.ui_active:
279 279 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
280 280 ui_.ui_key, ui_.ui_value)
281 281 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
282 282
283 283 meta.Session.remove()
284 284 return baseui
285 285
286 286
287 287 def set_rhodecode_config(config):
288 288 """Updates pylons config with new settings from database
289 289
290 290 :param config:
291 291 """
292 292 hgsettings = RhodeCodeSettings.get_app_settings()
293 293
294 294 for k, v in hgsettings.items():
295 295 config[k] = v
296 296
297 297
298 298 def invalidate_cache(cache_key, *args):
299 299 """Puts cache invalidation task into db for
300 300 further global cache invalidation
301 301 """
302 302
303 303 from rhodecode.model.scm import ScmModel
304 304
305 305 if cache_key.startswith('get_repo_cached_'):
306 306 name = cache_key.split('get_repo_cached_')[-1]
307 307 ScmModel().mark_for_invalidation(name)
308 308
309 309
310 310 class EmptyChangeset(BaseChangeset):
311 311 """
312 312 An dummy empty changeset. It's possible to pass hash when creating
313 313 an EmptyChangeset
314 314 """
315 315
316 316 def __init__(self, cs='0' * 40, repo=None):
317 317 self._empty_cs = cs
318 318 self.revision = -1
319 319 self.message = ''
320 320 self.author = ''
321 321 self.date = ''
322 322 self.repository = repo
323 323
324 324 @LazyProperty
325 325 def raw_id(self):
326 326 """Returns raw string identifying this changeset, useful for web
327 327 representation.
328 328 """
329 329
330 330 return self._empty_cs
331 331
332 332 @LazyProperty
333 333 def short_id(self):
334 334 return self.raw_id[:12]
335 335
336 336 def get_file_changeset(self, path):
337 337 return self
338 338
339 339 def get_file_content(self, path):
340 340 return u''
341 341
342 342 def get_file_size(self, path):
343 343 return 0
344 344
345 345
346 346 def map_groups(groups):
347 347 """Checks for groups existence, and creates groups structures.
348 348 It returns last group in structure
349 349
350 350 :param groups: list of groups structure
351 351 """
352 352 sa = meta.Session()
353 353
354 354 parent = None
355 355 group = None
356 356 for lvl, group_name in enumerate(groups[:-1]):
357 357 group = sa.query(Group).filter(Group.group_name == group_name).scalar()
358 358
359 359 if group is None:
360 360 group = Group(group_name, parent)
361 361 sa.add(group)
362 362 sa.commit()
363 363
364 364 parent = group
365 365
366 366 return group
367 367
368 368
369 369 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
370 370 """maps all repos given in initial_repo_list, non existing repositories
371 371 are created, if remove_obsolete is True it also check for db entries
372 372 that are not in initial_repo_list and removes them.
373 373
374 374 :param initial_repo_list: list of repositories found by scanning methods
375 375 :param remove_obsolete: check for obsolete entries in database
376 376 """
377 377
378 378 sa = meta.Session()
379 379 rm = RepoModel()
380 380 user = sa.query(User).filter(User.admin == True).first()
381 381 added = []
382 382 for name, repo in initial_repo_list.items():
383 383 group = map_groups(name.split('/'))
384 384 if not rm.get_by_repo_name(name, cache=False):
385 385 log.info('repository %s not found creating default', name)
386 386 added.append(name)
387 387 form_data = {
388 388 'repo_name': name,
389 'repo_name_full': name,
389 390 'repo_type': repo.alias,
390 391 'description': repo.description \
391 392 if repo.description != 'unknown' else \
392 393 '%s repository' % name,
393 394 'private': False,
394 395 'group_id': getattr(group, 'group_id', None)
395 396 }
396 397 rm.create(form_data, user, just_db=True)
397 398
398 399 removed = []
399 400 if remove_obsolete:
400 401 #remove from database those repositories that are not in the filesystem
401 402 for repo in sa.query(Repository).all():
402 403 if repo.repo_name not in initial_repo_list.keys():
403 404 removed.append(repo.repo_name)
404 405 sa.delete(repo)
405 406 sa.commit()
406 407
407 408 return added, removed
408 409
409 410 #set cache regions for beaker so celery can utilise it
410 411 def add_cache(settings):
411 412 cache_settings = {'regions': None}
412 413 for key in settings.keys():
413 414 for prefix in ['beaker.cache.', 'cache.']:
414 415 if key.startswith(prefix):
415 416 name = key.split(prefix)[1].strip()
416 417 cache_settings[name] = settings[key].strip()
417 418 if cache_settings['regions']:
418 419 for region in cache_settings['regions'].split(','):
419 420 region = region.strip()
420 421 region_settings = {}
421 422 for key, value in cache_settings.items():
422 423 if key.startswith(region):
423 424 region_settings[key.split('.')[1]] = value
424 425 region_settings['expire'] = int(region_settings.get('expire',
425 426 60))
426 427 region_settings.setdefault('lock_dir',
427 428 cache_settings.get('lock_dir'))
428 429 region_settings.setdefault('data_dir',
429 430 cache_settings.get('data_dir'))
430 431
431 432 if 'type' not in region_settings:
432 433 region_settings['type'] = cache_settings.get('type',
433 434 'memory')
434 435 beaker.cache.cache_regions[region] = region_settings
435 436
436 437
437 438 def get_current_revision():
438 439 """Returns tuple of (number, id) from repository containing this package
439 440 or None if repository could not be found.
440 441 """
441 442
442 443 try:
443 444 from vcs import get_repo
444 445 from vcs.utils.helpers import get_scm
445 446 from vcs.exceptions import RepositoryError, VCSError
446 447 repopath = os.path.join(os.path.dirname(__file__), '..', '..')
447 448 scm = get_scm(repopath)[0]
448 449 repo = get_repo(path=repopath, alias=scm)
449 450 tip = repo.get_changeset()
450 451 return (tip.revision, tip.short_id)
451 452 except (ImportError, RepositoryError, VCSError), err:
452 453 logging.debug("Cannot retrieve rhodecode's revision. Original error "
453 454 "was: %s" % err)
454 455 return None
455 456
456 457
457 458 #==============================================================================
458 459 # TEST FUNCTIONS AND CREATORS
459 460 #==============================================================================
460 461 def create_test_index(repo_location, full_index):
461 462 """Makes default test index
462 463 :param repo_location:
463 464 :param full_index:
464 465 """
465 466 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
466 467 from rhodecode.lib.pidlock import DaemonLock, LockHeld
467 468 import shutil
468 469
469 470 index_location = os.path.join(repo_location, 'index')
470 471 if os.path.exists(index_location):
471 472 shutil.rmtree(index_location)
472 473
473 474 try:
474 475 l = DaemonLock(file=jn(dn(dn(index_location)), 'make_index.lock'))
475 476 WhooshIndexingDaemon(index_location=index_location,
476 477 repo_location=repo_location)\
477 478 .run(full_index=full_index)
478 479 l.release()
479 480 except LockHeld:
480 481 pass
481 482
482 483
483 484 def create_test_env(repos_test_path, config):
484 485 """Makes a fresh database and
485 486 install test repository into tmp dir
486 487 """
487 488 from rhodecode.lib.db_manage import DbManage
488 489 from rhodecode.tests import HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, \
489 490 HG_FORK, GIT_FORK, TESTS_TMP_PATH
490 491 import tarfile
491 492 import shutil
492 493 from os.path import dirname as dn, join as jn, abspath
493 494
494 495 log = logging.getLogger('TestEnvCreator')
495 496 # create logger
496 497 log.setLevel(logging.DEBUG)
497 498 log.propagate = True
498 499 # create console handler and set level to debug
499 500 ch = logging.StreamHandler()
500 501 ch.setLevel(logging.DEBUG)
501 502
502 503 # create formatter
503 504 formatter = logging.Formatter("%(asctime)s - %(name)s -"
504 505 " %(levelname)s - %(message)s")
505 506
506 507 # add formatter to ch
507 508 ch.setFormatter(formatter)
508 509
509 510 # add ch to logger
510 511 log.addHandler(ch)
511 512
512 513 #PART ONE create db
513 514 dbconf = config['sqlalchemy.db1.url']
514 515 log.debug('making test db %s', dbconf)
515 516
516 517 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
517 518 tests=True)
518 519 dbmanage.create_tables(override=True)
519 520 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
520 521 dbmanage.create_default_user()
521 522 dbmanage.admin_prompt()
522 523 dbmanage.create_permissions()
523 524 dbmanage.populate_default_permissions()
524 525
525 526 #PART TWO make test repo
526 527 log.debug('making test vcs repositories')
527 528
528 529 #remove old one from previos tests
529 530 for r in [HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, HG_FORK, GIT_FORK]:
530 531
531 532 if os.path.isdir(jn(TESTS_TMP_PATH, r)):
532 533 log.debug('removing %s', r)
533 534 shutil.rmtree(jn(TESTS_TMP_PATH, r))
534 535
535 536 #CREATE DEFAULT HG REPOSITORY
536 537 cur_dir = dn(dn(abspath(__file__)))
537 538 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
538 539 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
539 540 tar.close()
540 541
541 542
542 543 #==============================================================================
543 544 # PASTER COMMANDS
544 545 #==============================================================================
545 546 class BasePasterCommand(Command):
546 547 """
547 548 Abstract Base Class for paster commands.
548 549
549 550 The celery commands are somewhat aggressive about loading
550 551 celery.conf, and since our module sets the `CELERY_LOADER`
551 552 environment variable to our loader, we have to bootstrap a bit and
552 553 make sure we've had a chance to load the pylons config off of the
553 554 command line, otherwise everything fails.
554 555 """
555 556 min_args = 1
556 557 min_args_error = "Please provide a paster config file as an argument."
557 558 takes_config_file = 1
558 559 requires_config_file = True
559 560
560 561 def notify_msg(self, msg, log=False):
561 562 """Make a notification to user, additionally if logger is passed
562 563 it logs this action using given logger
563 564
564 565 :param msg: message that will be printed to user
565 566 :param log: logging instance, to use to additionally log this message
566 567
567 568 """
568 569 if log and isinstance(log, logging):
569 570 log(msg)
570 571
571 572 def run(self, args):
572 573 """
573 574 Overrides Command.run
574 575
575 576 Checks for a config file argument and loads it.
576 577 """
577 578 if len(args) < self.min_args:
578 579 raise BadCommand(
579 580 self.min_args_error % {'min_args': self.min_args,
580 581 'actual_args': len(args)})
581 582
582 583 # Decrement because we're going to lob off the first argument.
583 584 # @@ This is hacky
584 585 self.min_args -= 1
585 586 self.bootstrap_config(args[0])
586 587 self.update_parser()
587 588 return super(BasePasterCommand, self).run(args[1:])
588 589
589 590 def update_parser(self):
590 591 """
591 592 Abstract method. Allows for the class's parser to be updated
592 593 before the superclass's `run` method is called. Necessary to
593 594 allow options/arguments to be passed through to the underlying
594 595 celery command.
595 596 """
596 597 raise NotImplementedError("Abstract Method.")
597 598
598 599 def bootstrap_config(self, conf):
599 600 """
600 601 Loads the pylons configuration.
601 602 """
602 603 from pylons import config as pylonsconfig
603 604
604 605 path_to_ini_file = os.path.realpath(conf)
605 606 conf = paste.deploy.appconfig('config:' + path_to_ini_file)
606 607 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
@@ -1,657 +1,657 b''
1 1 """ this is forms validation classes
2 2 http://formencode.org/module-formencode.validators.html
3 3 for list off all availible validators
4 4
5 5 we can create our own validators
6 6
7 7 The table below outlines the options which can be used in a schema in addition to the validators themselves
8 8 pre_validators [] These validators will be applied before the schema
9 9 chained_validators [] These validators will be applied after the schema
10 10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
11 11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
12 12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
13 13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
14 14
15 15
16 16 <name> = formencode.validators.<name of validator>
17 17 <name> must equal form name
18 18 list=[1,2,3,4,5]
19 19 for SELECT use formencode.All(OneOf(list), Int())
20 20
21 21 """
22 22 import os
23 23 import re
24 24 import logging
25 25 import traceback
26 26
27 27 import formencode
28 28 from formencode import All
29 29 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
30 30 Email, Bool, StringBoolean, Set
31 31
32 32 from pylons.i18n.translation import _
33 33 from webhelpers.pylonslib.secure_form import authentication_token
34 34
35 35 from rhodecode.lib.utils import repo_name_slug
36 36 from rhodecode.lib.auth import authenticate, get_crypt_password
37 37 from rhodecode.lib.exceptions import LdapImportError
38 38 from rhodecode.model.user import UserModel
39 39 from rhodecode.model.repo import RepoModel
40 40 from rhodecode.model.db import User, UsersGroup, Group
41 41 from rhodecode import BACKENDS
42 42
43 43 log = logging.getLogger(__name__)
44 44
45 45 #this is needed to translate the messages using _() in validators
46 46 class State_obj(object):
47 47 _ = staticmethod(_)
48 48
49 49 #==============================================================================
50 50 # VALIDATORS
51 51 #==============================================================================
52 52 class ValidAuthToken(formencode.validators.FancyValidator):
53 53 messages = {'invalid_token':_('Token mismatch')}
54 54
55 55 def validate_python(self, value, state):
56 56
57 57 if value != authentication_token():
58 58 raise formencode.Invalid(self.message('invalid_token', state,
59 59 search_number=value), value, state)
60 60
61 61 def ValidUsername(edit, old_data):
62 62 class _ValidUsername(formencode.validators.FancyValidator):
63 63
64 64 def validate_python(self, value, state):
65 65 if value in ['default', 'new_user']:
66 66 raise formencode.Invalid(_('Invalid username'), value, state)
67 67 #check if user is unique
68 68 old_un = None
69 69 if edit:
70 70 old_un = UserModel().get(old_data.get('user_id')).username
71 71
72 72 if old_un != value or not edit:
73 73 if UserModel().get_by_username(value, cache=False,
74 74 case_insensitive=True):
75 75 raise formencode.Invalid(_('This username already '
76 76 'exists') , value, state)
77 77
78 78 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
79 79 raise formencode.Invalid(_('Username may only contain '
80 80 'alphanumeric characters '
81 81 'underscores, periods or dashes '
82 82 'and must begin with alphanumeric '
83 83 'character'), value, state)
84 84
85 85 return _ValidUsername
86 86
87 87
88 88 def ValidUsersGroup(edit, old_data):
89 89
90 90 class _ValidUsersGroup(formencode.validators.FancyValidator):
91 91
92 92 def validate_python(self, value, state):
93 93 if value in ['default']:
94 94 raise formencode.Invalid(_('Invalid group name'), value, state)
95 95 #check if group is unique
96 96 old_ugname = None
97 97 if edit:
98 98 old_ugname = UsersGroup.get(
99 99 old_data.get('users_group_id')).users_group_name
100 100
101 101 if old_ugname != value or not edit:
102 102 if UsersGroup.get_by_group_name(value, cache=False,
103 103 case_insensitive=True):
104 104 raise formencode.Invalid(_('This users group '
105 105 'already exists') , value,
106 106 state)
107 107
108 108
109 109 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
110 110 raise formencode.Invalid(_('Group name may only contain '
111 111 'alphanumeric characters '
112 112 'underscores, periods or dashes '
113 113 'and must begin with alphanumeric '
114 114 'character'), value, state)
115 115
116 116 return _ValidUsersGroup
117 117
118 118
119 119 def ValidReposGroup(edit, old_data):
120 120 class _ValidReposGroup(formencode.validators.FancyValidator):
121 121
122 122 def validate_python(self, value, state):
123 123 #TODO WRITE VALIDATIONS
124 124 group_name = value.get('group_name')
125 125 group_parent_id = int(value.get('group_parent_id') or - 1)
126 126
127 127 # slugify repo group just in case :)
128 128 slug = repo_name_slug(group_name)
129 129
130 130 # check for parent of self
131 131 if old_data['group_id'] == group_parent_id:
132 132 e_dict = {'group_parent_id':_('Cannot assign this group '
133 133 'as parent')}
134 134 raise formencode.Invalid('', value, state,
135 135 error_dict=e_dict)
136 136
137 137 old_gname = None
138 138 if edit:
139 139 old_gname = Group.get(
140 140 old_data.get('group_id')).group_name
141 141
142 142 if old_gname != group_name or not edit:
143 143 # check filesystem
144 144 gr = Group.query().filter(Group.group_name == slug)\
145 145 .filter(Group.group_parent_id == group_parent_id).scalar()
146 146
147 147 if gr:
148 148 e_dict = {'group_name':_('This group already exists')}
149 149 raise formencode.Invalid('', value, state,
150 150 error_dict=e_dict)
151 151
152 152 return _ValidReposGroup
153 153
154 154 class ValidPassword(formencode.validators.FancyValidator):
155 155
156 156 def to_python(self, value, state):
157 157
158 158 if value:
159 159
160 160 if value.get('password'):
161 161 try:
162 162 value['password'] = get_crypt_password(value['password'])
163 163 except UnicodeEncodeError:
164 164 e_dict = {'password':_('Invalid characters in password')}
165 165 raise formencode.Invalid('', value, state, error_dict=e_dict)
166 166
167 167 if value.get('password_confirmation'):
168 168 try:
169 169 value['password_confirmation'] = \
170 170 get_crypt_password(value['password_confirmation'])
171 171 except UnicodeEncodeError:
172 172 e_dict = {'password_confirmation':_('Invalid characters in password')}
173 173 raise formencode.Invalid('', value, state, error_dict=e_dict)
174 174
175 175 if value.get('new_password'):
176 176 try:
177 177 value['new_password'] = \
178 178 get_crypt_password(value['new_password'])
179 179 except UnicodeEncodeError:
180 180 e_dict = {'new_password':_('Invalid characters in password')}
181 181 raise formencode.Invalid('', value, state, error_dict=e_dict)
182 182
183 183 return value
184 184
185 185 class ValidPasswordsMatch(formencode.validators.FancyValidator):
186 186
187 187 def validate_python(self, value, state):
188 188
189 189 if value['password'] != value['password_confirmation']:
190 190 e_dict = {'password_confirmation':
191 191 _('Password do not match')}
192 192 raise formencode.Invalid('', value, state, error_dict=e_dict)
193 193
194 194 class ValidAuth(formencode.validators.FancyValidator):
195 195 messages = {
196 196 'invalid_password':_('invalid password'),
197 197 'invalid_login':_('invalid user name'),
198 198 'disabled_account':_('Your account is disabled')
199 199
200 200 }
201 201 #error mapping
202 202 e_dict = {'username':messages['invalid_login'],
203 203 'password':messages['invalid_password']}
204 204 e_dict_disable = {'username':messages['disabled_account']}
205 205
206 206 def validate_python(self, value, state):
207 207 password = value['password']
208 208 username = value['username']
209 209 user = UserModel().get_by_username(username)
210 210
211 211 if authenticate(username, password):
212 212 return value
213 213 else:
214 214 if user and user.active is False:
215 215 log.warning('user %s is disabled', username)
216 216 raise formencode.Invalid(self.message('disabled_account',
217 217 state=State_obj),
218 218 value, state,
219 219 error_dict=self.e_dict_disable)
220 220 else:
221 221 log.warning('user %s not authenticated', username)
222 222 raise formencode.Invalid(self.message('invalid_password',
223 223 state=State_obj), value, state,
224 224 error_dict=self.e_dict)
225 225
226 226 class ValidRepoUser(formencode.validators.FancyValidator):
227 227
228 228 def to_python(self, value, state):
229 229 try:
230 self.user_db = User.query()\
231 .filter(User.active == True)\
230 User.query().filter(User.active == True)\
232 231 .filter(User.username == value).one()
233 232 except Exception:
234 233 raise formencode.Invalid(_('This username is not valid'),
235 234 value, state)
236 235 return value
237 236
238 237 def ValidRepoName(edit, old_data):
239 238 class _ValidRepoName(formencode.validators.FancyValidator):
240 239 def to_python(self, value, state):
241 240
242 241 repo_name = value.get('repo_name')
243 242
244 243 slug = repo_name_slug(repo_name)
245 244 if slug in ['_admin', '']:
246 245 e_dict = {'repo_name': _('This repository name is disallowed')}
247 246 raise formencode.Invalid('', value, state, error_dict=e_dict)
248 247
249 248
250 249 if value.get('repo_group'):
251 250 gr = Group.get(value.get('repo_group'))
252 251 group_path = gr.full_path
253 252 # value needs to be aware of group name in order to check
254 # db key
253 # db key This is an actuall just the name to store in the
254 # database
255 255 repo_name_full = group_path + Group.url_sep() + repo_name
256 256 else:
257 257 group_path = ''
258 258 repo_name_full = repo_name
259 259
260 260
261 261 value['repo_name_full'] = repo_name_full
262 262 if old_data.get('repo_name') != repo_name_full or not edit:
263 263
264 264 if group_path != '':
265 265 if RepoModel().get_by_repo_name(repo_name_full,):
266 266 e_dict = {'repo_name':_('This repository already '
267 267 'exists in group "%s"') %
268 268 gr.group_name}
269 269 raise formencode.Invalid('', value, state,
270 270 error_dict=e_dict)
271 271
272 272 else:
273 273 if RepoModel().get_by_repo_name(repo_name_full):
274 274 e_dict = {'repo_name':_('This repository '
275 275 'already exists')}
276 276 raise formencode.Invalid('', value, state,
277 277 error_dict=e_dict)
278 278 return value
279 279
280 280
281 281 return _ValidRepoName
282 282
283 283 def SlugifyName():
284 284 class _SlugifyName(formencode.validators.FancyValidator):
285 285
286 286 def to_python(self, value, state):
287 287 return repo_name_slug(value)
288 288
289 289 return _SlugifyName
290 290
291 291 def ValidCloneUri():
292 292 from mercurial.httprepo import httprepository, httpsrepository
293 293 from rhodecode.lib.utils import make_ui
294 294
295 295 class _ValidCloneUri(formencode.validators.FancyValidator):
296 296
297 297 def to_python(self, value, state):
298 298 if not value:
299 299 pass
300 300 elif value.startswith('https'):
301 301 try:
302 302 httpsrepository(make_ui('db'), value).capabilities
303 303 except Exception, e:
304 304 log.error(traceback.format_exc())
305 305 raise formencode.Invalid(_('invalid clone url'), value,
306 306 state)
307 307 elif value.startswith('http'):
308 308 try:
309 309 httprepository(make_ui('db'), value).capabilities
310 310 except Exception, e:
311 311 log.error(traceback.format_exc())
312 312 raise formencode.Invalid(_('invalid clone url'), value,
313 313 state)
314 314 else:
315 315 raise formencode.Invalid(_('Invalid clone url, provide a '
316 316 'valid clone http\s url'), value,
317 317 state)
318 318 return value
319 319
320 320 return _ValidCloneUri
321 321
322 322 def ValidForkType(old_data):
323 323 class _ValidForkType(formencode.validators.FancyValidator):
324 324
325 325 def to_python(self, value, state):
326 326 if old_data['repo_type'] != value:
327 327 raise formencode.Invalid(_('Fork have to be the same '
328 328 'type as original'), value, state)
329 329 return value
330 330 return _ValidForkType
331 331
332 332 class ValidPerms(formencode.validators.FancyValidator):
333 333 messages = {'perm_new_member_name':_('This username or users group name'
334 334 ' is not valid')}
335 335
336 336 def to_python(self, value, state):
337 337 perms_update = []
338 338 perms_new = []
339 339 #build a list of permission to update and new permission to create
340 340 for k, v in value.items():
341 341 #means new added member to permissions
342 342 if k.startswith('perm_new_member'):
343 343 new_perm = value.get('perm_new_member', False)
344 344 new_member = value.get('perm_new_member_name', False)
345 345 new_type = value.get('perm_new_member_type')
346 346
347 347 if new_member and new_perm:
348 348 if (new_member, new_perm, new_type) not in perms_new:
349 349 perms_new.append((new_member, new_perm, new_type))
350 350 elif k.startswith('u_perm_') or k.startswith('g_perm_'):
351 351 member = k[7:]
352 352 t = {'u':'user',
353 353 'g':'users_group'}[k[0]]
354 354 if member == 'default':
355 355 if value['private']:
356 356 #set none for default when updating to private repo
357 357 v = 'repository.none'
358 358 perms_update.append((member, v, t))
359 359
360 360 value['perms_updates'] = perms_update
361 361 value['perms_new'] = perms_new
362 362
363 363 #update permissions
364 364 for k, v, t in perms_new:
365 365 try:
366 366 if t is 'user':
367 367 self.user_db = User.query()\
368 368 .filter(User.active == True)\
369 369 .filter(User.username == k).one()
370 370 if t is 'users_group':
371 371 self.user_db = UsersGroup.query()\
372 372 .filter(UsersGroup.users_group_active == True)\
373 373 .filter(UsersGroup.users_group_name == k).one()
374 374
375 375 except Exception:
376 376 msg = self.message('perm_new_member_name',
377 377 state=State_obj)
378 378 raise formencode.Invalid(msg, value, state,
379 379 error_dict={'perm_new_member_name':msg})
380 380 return value
381 381
382 382 class ValidSettings(formencode.validators.FancyValidator):
383 383
384 384 def to_python(self, value, state):
385 385 #settings form can't edit user
386 386 if value.has_key('user'):
387 387 del['value']['user']
388 388
389 389 return value
390 390
391 391 class ValidPath(formencode.validators.FancyValidator):
392 392 def to_python(self, value, state):
393 393
394 394 if not os.path.isdir(value):
395 395 msg = _('This is not a valid path')
396 396 raise formencode.Invalid(msg, value, state,
397 397 error_dict={'paths_root_path':msg})
398 398 return value
399 399
400 400 def UniqSystemEmail(old_data):
401 401 class _UniqSystemEmail(formencode.validators.FancyValidator):
402 402 def to_python(self, value, state):
403 403 value = value.lower()
404 404 if old_data.get('email') != value:
405 405 user = User.query().filter(User.email == value).scalar()
406 406 if user:
407 407 raise formencode.Invalid(
408 408 _("This e-mail address is already taken"),
409 409 value, state)
410 410 return value
411 411
412 412 return _UniqSystemEmail
413 413
414 414 class ValidSystemEmail(formencode.validators.FancyValidator):
415 415 def to_python(self, value, state):
416 416 value = value.lower()
417 417 user = User.query().filter(User.email == value).scalar()
418 418 if user is None:
419 419 raise formencode.Invalid(_("This e-mail address doesn't exist.") ,
420 420 value, state)
421 421
422 422 return value
423 423
424 424 class LdapLibValidator(formencode.validators.FancyValidator):
425 425
426 426 def to_python(self, value, state):
427 427
428 428 try:
429 429 import ldap
430 430 except ImportError:
431 431 raise LdapImportError
432 432 return value
433 433
434 434 class AttrLoginValidator(formencode.validators.FancyValidator):
435 435
436 436 def to_python(self, value, state):
437 437
438 438 if not value or not isinstance(value, (str, unicode)):
439 439 raise formencode.Invalid(_("The LDAP Login attribute of the CN "
440 440 "must be specified - this is the name "
441 441 "of the attribute that is equivalent "
442 442 "to 'username'"),
443 443 value, state)
444 444
445 445 return value
446 446
447 447 #===============================================================================
448 448 # FORMS
449 449 #===============================================================================
450 450 class LoginForm(formencode.Schema):
451 451 allow_extra_fields = True
452 452 filter_extra_fields = True
453 453 username = UnicodeString(
454 454 strip=True,
455 455 min=1,
456 456 not_empty=True,
457 457 messages={
458 458 'empty':_('Please enter a login'),
459 459 'tooShort':_('Enter a value %(min)i characters long or more')}
460 460 )
461 461
462 462 password = UnicodeString(
463 463 strip=True,
464 464 min=3,
465 465 not_empty=True,
466 466 messages={
467 467 'empty':_('Please enter a password'),
468 468 'tooShort':_('Enter %(min)i characters or more')}
469 469 )
470 470
471 471
472 472 #chained validators have access to all data
473 473 chained_validators = [ValidAuth]
474 474
475 475 def UserForm(edit=False, old_data={}):
476 476 class _UserForm(formencode.Schema):
477 477 allow_extra_fields = True
478 478 filter_extra_fields = True
479 479 username = All(UnicodeString(strip=True, min=1, not_empty=True),
480 480 ValidUsername(edit, old_data))
481 481 if edit:
482 482 new_password = All(UnicodeString(strip=True, min=6, not_empty=False))
483 483 admin = StringBoolean(if_missing=False)
484 484 else:
485 485 password = All(UnicodeString(strip=True, min=6, not_empty=True))
486 486 active = StringBoolean(if_missing=False)
487 487 name = UnicodeString(strip=True, min=1, not_empty=True)
488 488 lastname = UnicodeString(strip=True, min=1, not_empty=True)
489 489 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
490 490
491 491 chained_validators = [ValidPassword]
492 492
493 493 return _UserForm
494 494
495 495
496 496 def UsersGroupForm(edit=False, old_data={}, available_members=[]):
497 497 class _UsersGroupForm(formencode.Schema):
498 498 allow_extra_fields = True
499 499 filter_extra_fields = True
500 500
501 501 users_group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
502 502 ValidUsersGroup(edit, old_data))
503 503
504 504 users_group_active = StringBoolean(if_missing=False)
505 505
506 506 if edit:
507 507 users_group_members = OneOf(available_members, hideList=False,
508 508 testValueList=True,
509 509 if_missing=None, not_empty=False)
510 510
511 511 return _UsersGroupForm
512 512
513 513 def ReposGroupForm(edit=False, old_data={}, available_groups=[]):
514 514 class _ReposGroupForm(formencode.Schema):
515 515 allow_extra_fields = True
516 516 filter_extra_fields = True
517 517
518 518 group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
519 519 SlugifyName())
520 520 group_description = UnicodeString(strip=True, min=1,
521 521 not_empty=True)
522 522 group_parent_id = OneOf(available_groups, hideList=False,
523 523 testValueList=True,
524 524 if_missing=None, not_empty=False)
525 525
526 526 chained_validators = [ValidReposGroup(edit, old_data)]
527 527
528 528 return _ReposGroupForm
529 529
530 530 def RegisterForm(edit=False, old_data={}):
531 531 class _RegisterForm(formencode.Schema):
532 532 allow_extra_fields = True
533 533 filter_extra_fields = True
534 534 username = All(ValidUsername(edit, old_data),
535 535 UnicodeString(strip=True, min=1, not_empty=True))
536 536 password = All(UnicodeString(strip=True, min=6, not_empty=True))
537 537 password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=True))
538 538 active = StringBoolean(if_missing=False)
539 539 name = UnicodeString(strip=True, min=1, not_empty=True)
540 540 lastname = UnicodeString(strip=True, min=1, not_empty=True)
541 541 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
542 542
543 543 chained_validators = [ValidPasswordsMatch, ValidPassword]
544 544
545 545 return _RegisterForm
546 546
547 547 def PasswordResetForm():
548 548 class _PasswordResetForm(formencode.Schema):
549 549 allow_extra_fields = True
550 550 filter_extra_fields = True
551 551 email = All(ValidSystemEmail(), Email(not_empty=True))
552 552 return _PasswordResetForm
553 553
554 554 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
555 555 repo_groups=[]):
556 556 class _RepoForm(formencode.Schema):
557 557 allow_extra_fields = True
558 558 filter_extra_fields = False
559 559 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
560 560 SlugifyName())
561 561 clone_uri = All(UnicodeString(strip=True, min=1, not_empty=False),
562 562 ValidCloneUri()())
563 563 repo_group = OneOf(repo_groups, hideList=True)
564 564 repo_type = OneOf(supported_backends)
565 565 description = UnicodeString(strip=True, min=1, not_empty=True)
566 566 private = StringBoolean(if_missing=False)
567 567 enable_statistics = StringBoolean(if_missing=False)
568 568 enable_downloads = StringBoolean(if_missing=False)
569 569
570 570 if edit:
571 571 #this is repo owner
572 572 user = All(UnicodeString(not_empty=True), ValidRepoUser)
573 573
574 574 chained_validators = [ValidRepoName(edit, old_data), ValidPerms]
575 575 return _RepoForm
576 576
577 577 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
578 578 class _RepoForkForm(formencode.Schema):
579 579 allow_extra_fields = True
580 580 filter_extra_fields = False
581 581 fork_name = All(UnicodeString(strip=True, min=1, not_empty=True),
582 582 SlugifyName())
583 583 description = UnicodeString(strip=True, min=1, not_empty=True)
584 584 private = StringBoolean(if_missing=False)
585 585 repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
586 586 return _RepoForkForm
587 587
588 588 def RepoSettingsForm(edit=False, old_data={}):
589 589 class _RepoForm(formencode.Schema):
590 590 allow_extra_fields = True
591 591 filter_extra_fields = False
592 592 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
593 593 SlugifyName())
594 594 description = UnicodeString(strip=True, min=1, not_empty=True)
595 595 private = StringBoolean(if_missing=False)
596 596
597 597 chained_validators = [ValidRepoName(edit, old_data), ValidPerms, ValidSettings]
598 598 return _RepoForm
599 599
600 600
601 601 def ApplicationSettingsForm():
602 602 class _ApplicationSettingsForm(formencode.Schema):
603 603 allow_extra_fields = True
604 604 filter_extra_fields = False
605 605 rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
606 606 rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
607 607 rhodecode_ga_code = UnicodeString(strip=True, min=1, not_empty=False)
608 608
609 609 return _ApplicationSettingsForm
610 610
611 611 def ApplicationUiSettingsForm():
612 612 class _ApplicationUiSettingsForm(formencode.Schema):
613 613 allow_extra_fields = True
614 614 filter_extra_fields = False
615 615 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
616 616 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
617 617 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
618 618 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
619 619 hooks_pretxnchangegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
620 620 hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
621 621
622 622 return _ApplicationUiSettingsForm
623 623
624 624 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
625 625 class _DefaultPermissionsForm(formencode.Schema):
626 626 allow_extra_fields = True
627 627 filter_extra_fields = True
628 628 overwrite_default = StringBoolean(if_missing=False)
629 629 anonymous = OneOf(['True', 'False'], if_missing=False)
630 630 default_perm = OneOf(perms_choices)
631 631 default_register = OneOf(register_choices)
632 632 default_create = OneOf(create_choices)
633 633
634 634 return _DefaultPermissionsForm
635 635
636 636
637 637 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices, tls_kind_choices):
638 638 class _LdapSettingsForm(formencode.Schema):
639 639 allow_extra_fields = True
640 640 filter_extra_fields = True
641 641 pre_validators = [LdapLibValidator]
642 642 ldap_active = StringBoolean(if_missing=False)
643 643 ldap_host = UnicodeString(strip=True,)
644 644 ldap_port = Number(strip=True,)
645 645 ldap_tls_kind = OneOf(tls_kind_choices)
646 646 ldap_tls_reqcert = OneOf(tls_reqcert_choices)
647 647 ldap_dn_user = UnicodeString(strip=True,)
648 648 ldap_dn_pass = UnicodeString(strip=True,)
649 649 ldap_base_dn = UnicodeString(strip=True,)
650 650 ldap_filter = UnicodeString(strip=True,)
651 651 ldap_search_scope = OneOf(search_scope_choices)
652 652 ldap_attr_login = All(AttrLoginValidator, UnicodeString(strip=True,))
653 653 ldap_attr_firstname = UnicodeString(strip=True,)
654 654 ldap_attr_lastname = UnicodeString(strip=True,)
655 655 ldap_attr_email = UnicodeString(strip=True,)
656 656
657 657 return _LdapSettingsForm
@@ -1,371 +1,376 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) 2009-2011 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 import os
26 26 import shutil
27 27 import logging
28 28 import traceback
29 29 from datetime import datetime
30 30
31 31 from sqlalchemy.orm import joinedload, make_transient
32 32
33 33 from vcs.utils.lazy import LazyProperty
34 34 from vcs.backends import get_backend
35 35
36 36 from rhodecode.model import BaseModel
37 37 from rhodecode.model.caching_query import FromCache
38 38 from rhodecode.model.db import Repository, RepoToPerm, User, Permission, \
39 39 Statistics, UsersGroup, UsersGroupRepoToPerm, RhodeCodeUi, Group
40 40 from rhodecode.model.user import UserModel
41 41
42 42 log = logging.getLogger(__name__)
43 43
44 44
45 45 class RepoModel(BaseModel):
46 46
47 47 @LazyProperty
48 48 def repos_path(self):
49 49 """Get's the repositories root path from database
50 50 """
51 51
52 52 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
53 53 return q.ui_value
54 54
55 55 def get(self, repo_id, cache=False):
56 56 repo = self.sa.query(Repository)\
57 57 .filter(Repository.repo_id == repo_id)
58 58
59 59 if cache:
60 60 repo = repo.options(FromCache("sql_cache_short",
61 61 "get_repo_%s" % repo_id))
62 62 return repo.scalar()
63 63
64 64 def get_by_repo_name(self, repo_name, cache=False):
65 65 repo = self.sa.query(Repository)\
66 66 .filter(Repository.repo_name == repo_name)
67 67
68 68 if cache:
69 69 repo = repo.options(FromCache("sql_cache_short",
70 70 "get_repo_%s" % repo_name))
71 71 return repo.scalar()
72 72
73 73 def get_full(self, repo_name, cache=False, invalidate=False):
74 74 repo = self.sa.query(Repository)\
75 75 .options(joinedload(Repository.fork))\
76 76 .options(joinedload(Repository.user))\
77 77 .options(joinedload(Repository.group))\
78 78 .filter(Repository.repo_name == repo_name)\
79 79
80 80 if cache:
81 81 repo = repo.options(FromCache("sql_cache_long",
82 82 "get_repo_full_%s" % repo_name))
83 83 if invalidate and cache:
84 84 repo.invalidate()
85 85
86 86 ret = repo.scalar()
87 87
88 88 #make transient for sake of errors
89 89 make_transient(ret)
90 90 for k in ['fork', 'user', 'group']:
91 91 attr = getattr(ret, k, False)
92 92 if attr:
93 93 make_transient(attr)
94 94 return ret
95 95
96 96 def get_users_js(self):
97 97
98 98 users = self.sa.query(User).filter(User.active == True).all()
99 99 u_tmpl = '''{id:%s, fname:"%s", lname:"%s", nname:"%s"},'''
100 100 users_array = '[%s]' % '\n'.join([u_tmpl % (u.user_id, u.name,
101 101 u.lastname, u.username)
102 102 for u in users])
103 103 return users_array
104 104
105 105 def get_users_groups_js(self):
106 106 users_groups = self.sa.query(UsersGroup)\
107 107 .filter(UsersGroup.users_group_active == True).all()
108 108
109 109 g_tmpl = '''{id:%s, grname:"%s",grmembers:"%s"},'''
110 110
111 111 users_groups_array = '[%s]' % '\n'.join([g_tmpl % \
112 112 (gr.users_group_id, gr.users_group_name,
113 113 len(gr.members))
114 114 for gr in users_groups])
115 115 return users_groups_array
116 116
117 117 def update(self, repo_name, form_data):
118 118 try:
119 119 cur_repo = self.get_by_repo_name(repo_name, cache=False)
120 120 user_model = UserModel(self.sa)
121 121
122 122 #update permissions
123 123 for member, perm, member_type in form_data['perms_updates']:
124 124 if member_type == 'user':
125 125 r2p = self.sa.query(RepoToPerm)\
126 126 .filter(RepoToPerm.user == user_model.
127 127 get_by_username(member))\
128 128 .filter(RepoToPerm.repository == cur_repo)\
129 129 .one()
130 130
131 131 r2p.permission = self.sa.query(Permission)\
132 132 .filter(Permission.permission_name ==
133 133 perm).scalar()
134 134 self.sa.add(r2p)
135 135 else:
136 136 g2p = self.sa.query(UsersGroupRepoToPerm)\
137 137 .filter(UsersGroupRepoToPerm.users_group ==
138 138 UsersGroup.get_by_group_name(member))\
139 139 .filter(UsersGroupRepoToPerm.repository ==
140 140 cur_repo).one()
141 141
142 142 g2p.permission = self.sa.query(Permission)\
143 143 .filter(Permission.permission_name ==
144 144 perm).scalar()
145 145 self.sa.add(g2p)
146 146
147 147 #set new permissions
148 148 for member, perm, member_type in form_data['perms_new']:
149 149 if member_type == 'user':
150 150 r2p = RepoToPerm()
151 151 r2p.repository = cur_repo
152 152 r2p.user = user_model.get_by_username(member)
153 153
154 154 r2p.permission = self.sa.query(Permission)\
155 155 .filter(Permission.
156 156 permission_name == perm)\
157 157 .scalar()
158 158 self.sa.add(r2p)
159 159 else:
160 160 g2p = UsersGroupRepoToPerm()
161 161 g2p.repository = cur_repo
162 162 g2p.users_group = UsersGroup.get_by_group_name(member)
163 163
164 164 g2p.permission = self.sa.query(Permission)\
165 165 .filter(Permission.
166 166 permission_name == perm)\
167 167 .scalar()
168 168 self.sa.add(g2p)
169 169
170 170 #update current repo
171 171 for k, v in form_data.items():
172 172 if k == 'user':
173 173 cur_repo.user = user_model.get_by_username(v)
174 174 elif k == 'repo_name':
175 175 cur_repo.repo_name = form_data['repo_name_full']
176 176 elif k == 'repo_group' and v:
177 177 cur_repo.group_id = v
178 178
179 179 else:
180 180 setattr(cur_repo, k, v)
181 181
182 182 self.sa.add(cur_repo)
183 183
184 184 if repo_name != form_data['repo_name_full']:
185 185 # rename repository
186 186 self.__rename_repo(old=repo_name,
187 187 new=form_data['repo_name_full'])
188 188
189 189 self.sa.commit()
190 190 except:
191 191 log.error(traceback.format_exc())
192 192 self.sa.rollback()
193 193 raise
194 194
195 195 def create(self, form_data, cur_user, just_db=False, fork=False):
196 196 try:
197 197 if fork:
198 198 #force str since hg doesn't go with unicode
199 199 repo_name = str(form_data['fork_name'])
200 200 org_name = str(form_data['repo_name'])
201 org_full_name = str(form_data['repo_name_full'])
201 202
202 203 else:
203 204 org_name = repo_name = str(form_data['repo_name'])
205 repo_name_full = form_data['repo_name_full']
206
204 207 new_repo = Repository()
205 208 new_repo.enable_statistics = False
206 209 for k, v in form_data.items():
207 210 if k == 'repo_name':
208 v = repo_name
211 v = repo_name_full
209 212 if k == 'repo_group':
210 213 k = 'group_id'
211 214
212 215 setattr(new_repo, k, v)
213 216
214 217 if fork:
215 218 parent_repo = self.sa.query(Repository)\
216 .filter(Repository.repo_name == org_name).scalar()
219 .filter(Repository.repo_name == org_full_name).scalar()
217 220 new_repo.fork = parent_repo
218 221
219 222 new_repo.user_id = cur_user.user_id
220 223 self.sa.add(new_repo)
221 224
222 225 #create default permission
223 226 repo_to_perm = RepoToPerm()
224 227 default = 'repository.read'
225 228 for p in UserModel(self.sa).get_by_username('default',
226 229 cache=False).user_perms:
227 230 if p.permission.permission_name.startswith('repository.'):
228 231 default = p.permission.permission_name
229 232 break
230 233
231 234 default_perm = 'repository.none' if form_data['private'] else default
232 235
233 236 repo_to_perm.permission_id = self.sa.query(Permission)\
234 237 .filter(Permission.permission_name == default_perm)\
235 238 .one().permission_id
236 239
237 240 repo_to_perm.repository = new_repo
238 241 repo_to_perm.user_id = UserModel(self.sa)\
239 242 .get_by_username('default', cache=False).user_id
240 243
241 244 self.sa.add(repo_to_perm)
242 245
243 246 if not just_db:
244 247 self.__create_repo(repo_name, form_data['repo_type'],
245 248 form_data['repo_group'],
246 249 form_data['clone_uri'])
247 250
248 251 self.sa.commit()
249 252
250 253 #now automatically start following this repository as owner
251 254 from rhodecode.model.scm import ScmModel
252 255 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
253 256 cur_user.user_id)
254 257
255 258 except:
256 259 log.error(traceback.format_exc())
257 260 self.sa.rollback()
258 261 raise
259 262
260 263 def create_fork(self, form_data, cur_user):
261 264 from rhodecode.lib.celerylib import tasks, run_task
262 265 run_task(tasks.create_repo_fork, form_data, cur_user)
263 266
264 267 def delete(self, repo):
265 268 try:
266 269 self.sa.delete(repo)
267 270 self.__delete_repo(repo)
268 271 self.sa.commit()
269 272 except:
270 273 log.error(traceback.format_exc())
271 274 self.sa.rollback()
272 275 raise
273 276
274 277 def delete_perm_user(self, form_data, repo_name):
275 278 try:
276 279 self.sa.query(RepoToPerm)\
277 280 .filter(RepoToPerm.repository \
278 281 == self.get_by_repo_name(repo_name))\
279 282 .filter(RepoToPerm.user_id == form_data['user_id']).delete()
280 283 self.sa.commit()
281 284 except:
282 285 log.error(traceback.format_exc())
283 286 self.sa.rollback()
284 287 raise
285 288
286 289 def delete_perm_users_group(self, form_data, repo_name):
287 290 try:
288 291 self.sa.query(UsersGroupRepoToPerm)\
289 292 .filter(UsersGroupRepoToPerm.repository \
290 293 == self.get_by_repo_name(repo_name))\
291 294 .filter(UsersGroupRepoToPerm.users_group_id \
292 295 == form_data['users_group_id']).delete()
293 296 self.sa.commit()
294 297 except:
295 298 log.error(traceback.format_exc())
296 299 self.sa.rollback()
297 300 raise
298 301
299 302 def delete_stats(self, repo_name):
300 303 try:
301 304 self.sa.query(Statistics)\
302 305 .filter(Statistics.repository == \
303 306 self.get_by_repo_name(repo_name)).delete()
304 307 self.sa.commit()
305 308 except:
306 309 log.error(traceback.format_exc())
307 310 self.sa.rollback()
308 311 raise
309 312
310 313 def __create_repo(self, repo_name, alias, new_parent_id, clone_uri=False):
311 314 """
312 makes repository on filesystem it's group aware
315 makes repository on filesystem. It's group aware means it'll create
316 a repository within a group, and alter the paths accordingly of
317 group location
313 318
314 319 :param repo_name:
315 320 :param alias:
316 321 :param parent_id:
317 322 :param clone_uri:
318 323 """
319 324 from rhodecode.lib.utils import check_repo
320 325
321 326
322 327 if new_parent_id:
323 328 paths = Group.get(new_parent_id).full_path.split(Group.url_sep())
324 329 new_parent_path = os.sep.join(paths)
325 330 else:
326 331 new_parent_path = ''
327 332
328 333 repo_path = os.path.join(self.repos_path, new_parent_path, repo_name)
329 334
330 335 if check_repo(repo_name, self.repos_path):
331 336 log.info('creating repo %s in %s @ %s', repo_name, repo_path,
332 337 clone_uri)
333 338 backend = get_backend(alias)
334 339 backend(repo_path, create=True, src_url=clone_uri)
335 340
336 341 def __rename_repo(self, old, new):
337 342 """
338 343 renames repository on filesystem
339 344
340 345 :param old: old name
341 346 :param new: new name
342 347 """
343 348 log.info('renaming repo from %s to %s', old, new)
344 349
345 350 old_path = os.path.join(self.repos_path, old)
346 351 new_path = os.path.join(self.repos_path, new)
347 352 if os.path.isdir(new_path):
348 353 raise Exception('Was trying to rename to already existing dir %s',
349 354 new_path)
350 355 shutil.move(old_path, new_path)
351 356
352 357 def __delete_repo(self, repo):
353 358 """
354 359 removes repo from filesystem, the removal is acctually made by
355 360 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
356 361 repository is no longer valid for rhodecode, can be undeleted later on
357 362 by reverting the renames on this repository
358 363
359 364 :param repo: repo object
360 365 """
361 366 rm_path = os.path.join(self.repos_path, repo.repo_name)
362 367 log.info("Removing %s", rm_path)
363 368 #disable hg/git
364 369 alias = repo.repo_type
365 370 shutil.move(os.path.join(rm_path, '.%s' % alias),
366 371 os.path.join(rm_path, 'rm__.%s' % alias))
367 372 #disable repo
368 373 shutil.move(rm_path, os.path.join(self.repos_path, 'rm__%s__%s' \
369 374 % (datetime.today()\
370 375 .strftime('%Y%m%d_%H%M%S_%f'),
371 376 repo.repo_name)))
General Comments 0
You need to be logged in to leave comments. Login now