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