##// END OF EJS Templates
validating choices for landing_rev
marcink -
r2472:e70ebd6d beta
parent child Browse files
Show More
@@ -1,437 +1,442
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 Repositories controller for RhodeCode
6 Repositories 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) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 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 formencode import htmlfill
29 from formencode import htmlfill
30
30
31 from paste.httpexceptions import HTTPInternalServerError
31 from paste.httpexceptions import HTTPInternalServerError
32 from pylons import request, session, tmpl_context as c, url
32 from pylons import request, session, tmpl_context as c, url
33 from pylons.controllers.util import redirect
33 from pylons.controllers.util import redirect
34 from pylons.i18n.translation import _
34 from pylons.i18n.translation import _
35 from sqlalchemy.exc import IntegrityError
35 from sqlalchemy.exc import IntegrityError
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, HasRepoPermissionAllDecorator
39 HasPermissionAnyDecorator, HasRepoPermissionAllDecorator
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.meta import Session
43 from rhodecode.model.meta import Session
44 from rhodecode.model.db import User, Repository, UserFollowing, RepoGroup
44 from rhodecode.model.db import User, Repository, UserFollowing, RepoGroup
45 from rhodecode.model.forms import RepoForm
45 from rhodecode.model.forms import RepoForm
46 from rhodecode.model.scm import ScmModel
46 from rhodecode.model.scm import ScmModel
47 from rhodecode.model.repo import RepoModel
47 from rhodecode.model.repo import RepoModel
48
48
49 log = logging.getLogger(__name__)
49 log = logging.getLogger(__name__)
50
50
51
51
52 class ReposController(BaseController):
52 class ReposController(BaseController):
53 """
53 """
54 REST Controller styled on the Atom Publishing Protocol"""
54 REST Controller styled on the Atom Publishing Protocol"""
55 # To properly map this controller, ensure your config/routing.py
55 # To properly map this controller, ensure your config/routing.py
56 # file has a resource setup:
56 # file has a resource setup:
57 # map.resource('repo', 'repos')
57 # map.resource('repo', 'repos')
58
58
59 @LoginRequired()
59 @LoginRequired()
60 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
60 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
61 def __before__(self):
61 def __before__(self):
62 c.admin_user = session.get('admin_user')
62 c.admin_user = session.get('admin_user')
63 c.admin_username = session.get('admin_username')
63 c.admin_username = session.get('admin_username')
64 super(ReposController, self).__before__()
64 super(ReposController, self).__before__()
65
65
66 def __load_defaults(self):
66 def __load_defaults(self):
67 c.repo_groups = RepoGroup.groups_choices()
67 c.repo_groups = RepoGroup.groups_choices()
68 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
68 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
69
69
70 repo_model = RepoModel()
70 repo_model = RepoModel()
71 c.users_array = repo_model.get_users_js()
71 c.users_array = repo_model.get_users_js()
72 c.users_groups_array = repo_model.get_users_groups_js()
72 c.users_groups_array = repo_model.get_users_groups_js()
73 c.landing_revs = ScmModel().get_repo_landing_revs()
73 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
74 c.landing_revs_choices = choices
74
75
75 def __load_data(self, repo_name=None):
76 def __load_data(self, repo_name=None):
76 """
77 """
77 Load defaults settings for edit, and update
78 Load defaults settings for edit, and update
78
79
79 :param repo_name:
80 :param repo_name:
80 """
81 """
81 self.__load_defaults()
82 self.__load_defaults()
82
83
83 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
84 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
84 repo = db_repo.scm_instance
85 repo = db_repo.scm_instance
85
86
86 if c.repo_info is None:
87 if c.repo_info is None:
87 h.flash(_('%s repository is not mapped to db perhaps'
88 h.flash(_('%s repository is not mapped to db perhaps'
88 ' it was created or renamed from the filesystem'
89 ' it was created or renamed from the filesystem'
89 ' please run the application again'
90 ' please run the application again'
90 ' in order to rescan repositories') % repo_name,
91 ' in order to rescan repositories') % repo_name,
91 category='error')
92 category='error')
92
93
93 return redirect(url('repos'))
94 return redirect(url('repos'))
94
95
95 c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info)
96 choices, c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info)
97 c.landing_revs_choices = choices
98
96 c.default_user_id = User.get_by_username('default').user_id
99 c.default_user_id = User.get_by_username('default').user_id
97 c.in_public_journal = UserFollowing.query()\
100 c.in_public_journal = UserFollowing.query()\
98 .filter(UserFollowing.user_id == c.default_user_id)\
101 .filter(UserFollowing.user_id == c.default_user_id)\
99 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
102 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
100
103
101 if c.repo_info.stats:
104 if c.repo_info.stats:
102 # this is on what revision we ended up so we add +1 for count
105 # this is on what revision we ended up so we add +1 for count
103 last_rev = c.repo_info.stats.stat_on_revision + 1
106 last_rev = c.repo_info.stats.stat_on_revision + 1
104 else:
107 else:
105 last_rev = 0
108 last_rev = 0
106 c.stats_revision = last_rev
109 c.stats_revision = last_rev
107
110
108 c.repo_last_rev = repo.count() if repo.revisions else 0
111 c.repo_last_rev = repo.count() if repo.revisions else 0
109
112
110 if last_rev == 0 or c.repo_last_rev == 0:
113 if last_rev == 0 or c.repo_last_rev == 0:
111 c.stats_percentage = 0
114 c.stats_percentage = 0
112 else:
115 else:
113 c.stats_percentage = '%.2f' % ((float((last_rev)) /
116 c.stats_percentage = '%.2f' % ((float((last_rev)) /
114 c.repo_last_rev) * 100)
117 c.repo_last_rev) * 100)
115
118
116 defaults = RepoModel()._get_defaults(repo_name)
119 defaults = RepoModel()._get_defaults(repo_name)
117
120
118 c.repos_list = [('', _('--REMOVE FORK--'))]
121 c.repos_list = [('', _('--REMOVE FORK--'))]
119 c.repos_list += [(x.repo_id, x.repo_name) for x in
122 c.repos_list += [(x.repo_id, x.repo_name) for x in
120 Repository.query().order_by(Repository.repo_name).all()]
123 Repository.query().order_by(Repository.repo_name).all()]
121
124
122 return defaults
125 return defaults
123
126
124 @HasPermissionAllDecorator('hg.admin')
127 @HasPermissionAllDecorator('hg.admin')
125 def index(self, format='html'):
128 def index(self, format='html'):
126 """GET /repos: All items in the collection"""
129 """GET /repos: All items in the collection"""
127 # url('repos')
130 # url('repos')
128
131
129 c.repos_list = ScmModel().get_repos(Repository.query()
132 c.repos_list = ScmModel().get_repos(Repository.query()
130 .order_by(Repository.repo_name)
133 .order_by(Repository.repo_name)
131 .all(), sort_key='name_sort')
134 .all(), sort_key='name_sort')
132 return render('admin/repos/repos.html')
135 return render('admin/repos/repos.html')
133
136
134 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
137 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
135 def create(self):
138 def create(self):
136 """
139 """
137 POST /repos: Create a new item"""
140 POST /repos: Create a new item"""
138 # url('repos')
141 # url('repos')
139
142
140 self.__load_defaults()
143 self.__load_defaults()
141 form_result = {}
144 form_result = {}
142 try:
145 try:
143 form_result = RepoForm(repo_groups=c.repo_groups_choices)()\
146 form_result = RepoForm(repo_groups=c.repo_groups_choices,
147 landing_revs=c.landing_revs_choices)()\
144 .to_python(dict(request.POST))
148 .to_python(dict(request.POST))
145 RepoModel().create(form_result, self.rhodecode_user)
149 RepoModel().create(form_result, self.rhodecode_user)
146 if form_result['clone_uri']:
150 if form_result['clone_uri']:
147 h.flash(_('created repository %s from %s') \
151 h.flash(_('created repository %s from %s') \
148 % (form_result['repo_name'], form_result['clone_uri']),
152 % (form_result['repo_name'], form_result['clone_uri']),
149 category='success')
153 category='success')
150 else:
154 else:
151 h.flash(_('created repository %s') % form_result['repo_name'],
155 h.flash(_('created repository %s') % form_result['repo_name'],
152 category='success')
156 category='success')
153
157
154 if request.POST.get('user_created'):
158 if request.POST.get('user_created'):
155 # created by regular non admin user
159 # created by regular non admin user
156 action_logger(self.rhodecode_user, 'user_created_repo',
160 action_logger(self.rhodecode_user, 'user_created_repo',
157 form_result['repo_name_full'], self.ip_addr,
161 form_result['repo_name_full'], self.ip_addr,
158 self.sa)
162 self.sa)
159 else:
163 else:
160 action_logger(self.rhodecode_user, 'admin_created_repo',
164 action_logger(self.rhodecode_user, 'admin_created_repo',
161 form_result['repo_name_full'], self.ip_addr,
165 form_result['repo_name_full'], self.ip_addr,
162 self.sa)
166 self.sa)
163 Session.commit()
167 Session.commit()
164 except formencode.Invalid, errors:
168 except formencode.Invalid, errors:
165
169
166 c.new_repo = errors.value['repo_name']
170 c.new_repo = errors.value['repo_name']
167
171
168 if request.POST.get('user_created'):
172 if request.POST.get('user_created'):
169 r = render('admin/repos/repo_add_create_repository.html')
173 r = render('admin/repos/repo_add_create_repository.html')
170 else:
174 else:
171 r = render('admin/repos/repo_add.html')
175 r = render('admin/repos/repo_add.html')
172
176
173 return htmlfill.render(
177 return htmlfill.render(
174 r,
178 r,
175 defaults=errors.value,
179 defaults=errors.value,
176 errors=errors.error_dict or {},
180 errors=errors.error_dict or {},
177 prefix_error=False,
181 prefix_error=False,
178 encoding="UTF-8")
182 encoding="UTF-8")
179
183
180 except Exception:
184 except Exception:
181 log.error(traceback.format_exc())
185 log.error(traceback.format_exc())
182 msg = _('error occurred during creation of repository %s') \
186 msg = _('error occurred during creation of repository %s') \
183 % form_result.get('repo_name')
187 % form_result.get('repo_name')
184 h.flash(msg, category='error')
188 h.flash(msg, category='error')
185 if request.POST.get('user_created'):
189 if request.POST.get('user_created'):
186 return redirect(url('home'))
190 return redirect(url('home'))
187 return redirect(url('repos'))
191 return redirect(url('repos'))
188
192
189 @HasPermissionAllDecorator('hg.admin')
193 @HasPermissionAllDecorator('hg.admin')
190 def new(self, format='html'):
194 def new(self, format='html'):
191 """GET /repos/new: Form to create a new item"""
195 """GET /repos/new: Form to create a new item"""
192 new_repo = request.GET.get('repo', '')
196 new_repo = request.GET.get('repo', '')
193 c.new_repo = repo_name_slug(new_repo)
197 c.new_repo = repo_name_slug(new_repo)
194 self.__load_defaults()
198 self.__load_defaults()
195 return render('admin/repos/repo_add.html')
199 return render('admin/repos/repo_add.html')
196
200
197 @HasPermissionAllDecorator('hg.admin')
201 @HasPermissionAllDecorator('hg.admin')
198 def update(self, repo_name):
202 def update(self, repo_name):
199 """
203 """
200 PUT /repos/repo_name: Update an existing item"""
204 PUT /repos/repo_name: Update an existing item"""
201 # Forms posted to this method should contain a hidden field:
205 # Forms posted to this method should contain a hidden field:
202 # <input type="hidden" name="_method" value="PUT" />
206 # <input type="hidden" name="_method" value="PUT" />
203 # Or using helpers:
207 # Or using helpers:
204 # h.form(url('repo', repo_name=ID),
208 # h.form(url('repo', repo_name=ID),
205 # method='put')
209 # method='put')
206 # url('repo', repo_name=ID)
210 # url('repo', repo_name=ID)
207 self.__load_defaults()
211 self.__load_defaults()
208 repo_model = RepoModel()
212 repo_model = RepoModel()
209 changed_name = repo_name
213 changed_name = repo_name
210 _form = RepoForm(edit=True, old_data={'repo_name': repo_name},
214 _form = RepoForm(edit=True, old_data={'repo_name': repo_name},
211 repo_groups=c.repo_groups_choices)()
215 repo_groups=c.repo_groups_choices,
216 landing_revs=c.landing_revs_choices)()
212 try:
217 try:
213 form_result = _form.to_python(dict(request.POST))
218 form_result = _form.to_python(dict(request.POST))
214 repo = repo_model.update(repo_name, form_result)
219 repo = repo_model.update(repo_name, form_result)
215 invalidate_cache('get_repo_cached_%s' % repo_name)
220 invalidate_cache('get_repo_cached_%s' % repo_name)
216 h.flash(_('Repository %s updated successfully' % repo_name),
221 h.flash(_('Repository %s updated successfully' % repo_name),
217 category='success')
222 category='success')
218 changed_name = repo.repo_name
223 changed_name = repo.repo_name
219 action_logger(self.rhodecode_user, 'admin_updated_repo',
224 action_logger(self.rhodecode_user, 'admin_updated_repo',
220 changed_name, self.ip_addr, self.sa)
225 changed_name, self.ip_addr, self.sa)
221 Session.commit()
226 Session.commit()
222 except formencode.Invalid, errors:
227 except formencode.Invalid, errors:
223 defaults = self.__load_data(repo_name)
228 defaults = self.__load_data(repo_name)
224 defaults.update(errors.value)
229 defaults.update(errors.value)
225 return htmlfill.render(
230 return htmlfill.render(
226 render('admin/repos/repo_edit.html'),
231 render('admin/repos/repo_edit.html'),
227 defaults=defaults,
232 defaults=defaults,
228 errors=errors.error_dict or {},
233 errors=errors.error_dict or {},
229 prefix_error=False,
234 prefix_error=False,
230 encoding="UTF-8")
235 encoding="UTF-8")
231
236
232 except Exception:
237 except Exception:
233 log.error(traceback.format_exc())
238 log.error(traceback.format_exc())
234 h.flash(_('error occurred during update of repository %s') \
239 h.flash(_('error occurred during update of repository %s') \
235 % repo_name, category='error')
240 % repo_name, category='error')
236 return redirect(url('edit_repo', repo_name=changed_name))
241 return redirect(url('edit_repo', repo_name=changed_name))
237
242
238 @HasPermissionAllDecorator('hg.admin')
243 @HasPermissionAllDecorator('hg.admin')
239 def delete(self, repo_name):
244 def delete(self, repo_name):
240 """
245 """
241 DELETE /repos/repo_name: Delete an existing item"""
246 DELETE /repos/repo_name: Delete an existing item"""
242 # Forms posted to this method should contain a hidden field:
247 # Forms posted to this method should contain a hidden field:
243 # <input type="hidden" name="_method" value="DELETE" />
248 # <input type="hidden" name="_method" value="DELETE" />
244 # Or using helpers:
249 # Or using helpers:
245 # h.form(url('repo', repo_name=ID),
250 # h.form(url('repo', repo_name=ID),
246 # method='delete')
251 # method='delete')
247 # url('repo', repo_name=ID)
252 # url('repo', repo_name=ID)
248
253
249 repo_model = RepoModel()
254 repo_model = RepoModel()
250 repo = repo_model.get_by_repo_name(repo_name)
255 repo = repo_model.get_by_repo_name(repo_name)
251 if not repo:
256 if not repo:
252 h.flash(_('%s repository is not mapped to db perhaps'
257 h.flash(_('%s repository is not mapped to db perhaps'
253 ' it was moved or renamed from the filesystem'
258 ' it was moved or renamed from the filesystem'
254 ' please run the application again'
259 ' please run the application again'
255 ' in order to rescan repositories') % repo_name,
260 ' in order to rescan repositories') % repo_name,
256 category='error')
261 category='error')
257
262
258 return redirect(url('repos'))
263 return redirect(url('repos'))
259 try:
264 try:
260 action_logger(self.rhodecode_user, 'admin_deleted_repo',
265 action_logger(self.rhodecode_user, 'admin_deleted_repo',
261 repo_name, self.ip_addr, self.sa)
266 repo_name, self.ip_addr, self.sa)
262 repo_model.delete(repo)
267 repo_model.delete(repo)
263 invalidate_cache('get_repo_cached_%s' % repo_name)
268 invalidate_cache('get_repo_cached_%s' % repo_name)
264 h.flash(_('deleted repository %s') % repo_name, category='success')
269 h.flash(_('deleted repository %s') % repo_name, category='success')
265 Session.commit()
270 Session.commit()
266 except IntegrityError, e:
271 except IntegrityError, e:
267 if e.message.find('repositories_fork_id_fkey') != -1:
272 if e.message.find('repositories_fork_id_fkey') != -1:
268 log.error(traceback.format_exc())
273 log.error(traceback.format_exc())
269 h.flash(_('Cannot delete %s it still contains attached '
274 h.flash(_('Cannot delete %s it still contains attached '
270 'forks') % repo_name,
275 'forks') % repo_name,
271 category='warning')
276 category='warning')
272 else:
277 else:
273 log.error(traceback.format_exc())
278 log.error(traceback.format_exc())
274 h.flash(_('An error occurred during '
279 h.flash(_('An error occurred during '
275 'deletion of %s') % repo_name,
280 'deletion of %s') % repo_name,
276 category='error')
281 category='error')
277
282
278 except Exception, e:
283 except Exception, e:
279 log.error(traceback.format_exc())
284 log.error(traceback.format_exc())
280 h.flash(_('An error occurred during deletion of %s') % repo_name,
285 h.flash(_('An error occurred during deletion of %s') % repo_name,
281 category='error')
286 category='error')
282
287
283 return redirect(url('repos'))
288 return redirect(url('repos'))
284
289
285 @HasRepoPermissionAllDecorator('repository.admin')
290 @HasRepoPermissionAllDecorator('repository.admin')
286 def delete_perm_user(self, repo_name):
291 def delete_perm_user(self, repo_name):
287 """
292 """
288 DELETE an existing repository permission user
293 DELETE an existing repository permission user
289
294
290 :param repo_name:
295 :param repo_name:
291 """
296 """
292 try:
297 try:
293 RepoModel().revoke_user_permission(repo=repo_name,
298 RepoModel().revoke_user_permission(repo=repo_name,
294 user=request.POST['user_id'])
299 user=request.POST['user_id'])
295 Session.commit()
300 Session.commit()
296 except Exception:
301 except Exception:
297 log.error(traceback.format_exc())
302 log.error(traceback.format_exc())
298 h.flash(_('An error occurred during deletion of repository user'),
303 h.flash(_('An error occurred during deletion of repository user'),
299 category='error')
304 category='error')
300 raise HTTPInternalServerError()
305 raise HTTPInternalServerError()
301
306
302 @HasRepoPermissionAllDecorator('repository.admin')
307 @HasRepoPermissionAllDecorator('repository.admin')
303 def delete_perm_users_group(self, repo_name):
308 def delete_perm_users_group(self, repo_name):
304 """
309 """
305 DELETE an existing repository permission users group
310 DELETE an existing repository permission users group
306
311
307 :param repo_name:
312 :param repo_name:
308 """
313 """
309
314
310 try:
315 try:
311 RepoModel().revoke_users_group_permission(
316 RepoModel().revoke_users_group_permission(
312 repo=repo_name, group_name=request.POST['users_group_id']
317 repo=repo_name, group_name=request.POST['users_group_id']
313 )
318 )
314 Session.commit()
319 Session.commit()
315 except Exception:
320 except Exception:
316 log.error(traceback.format_exc())
321 log.error(traceback.format_exc())
317 h.flash(_('An error occurred during deletion of repository'
322 h.flash(_('An error occurred during deletion of repository'
318 ' users groups'),
323 ' users groups'),
319 category='error')
324 category='error')
320 raise HTTPInternalServerError()
325 raise HTTPInternalServerError()
321
326
322 @HasPermissionAllDecorator('hg.admin')
327 @HasPermissionAllDecorator('hg.admin')
323 def repo_stats(self, repo_name):
328 def repo_stats(self, repo_name):
324 """
329 """
325 DELETE an existing repository statistics
330 DELETE an existing repository statistics
326
331
327 :param repo_name:
332 :param repo_name:
328 """
333 """
329
334
330 try:
335 try:
331 RepoModel().delete_stats(repo_name)
336 RepoModel().delete_stats(repo_name)
332 Session.commit()
337 Session.commit()
333 except Exception, e:
338 except Exception, e:
334 h.flash(_('An error occurred during deletion of repository stats'),
339 h.flash(_('An error occurred during deletion of repository stats'),
335 category='error')
340 category='error')
336 return redirect(url('edit_repo', repo_name=repo_name))
341 return redirect(url('edit_repo', repo_name=repo_name))
337
342
338 @HasPermissionAllDecorator('hg.admin')
343 @HasPermissionAllDecorator('hg.admin')
339 def repo_cache(self, repo_name):
344 def repo_cache(self, repo_name):
340 """
345 """
341 INVALIDATE existing repository cache
346 INVALIDATE existing repository cache
342
347
343 :param repo_name:
348 :param repo_name:
344 """
349 """
345
350
346 try:
351 try:
347 ScmModel().mark_for_invalidation(repo_name)
352 ScmModel().mark_for_invalidation(repo_name)
348 Session.commit()
353 Session.commit()
349 except Exception, e:
354 except Exception, e:
350 h.flash(_('An error occurred during cache invalidation'),
355 h.flash(_('An error occurred during cache invalidation'),
351 category='error')
356 category='error')
352 return redirect(url('edit_repo', repo_name=repo_name))
357 return redirect(url('edit_repo', repo_name=repo_name))
353
358
354 @HasPermissionAllDecorator('hg.admin')
359 @HasPermissionAllDecorator('hg.admin')
355 def repo_public_journal(self, repo_name):
360 def repo_public_journal(self, repo_name):
356 """
361 """
357 Set's this repository to be visible in public journal,
362 Set's this repository to be visible in public journal,
358 in other words assing default user to follow this repo
363 in other words assing default user to follow this repo
359
364
360 :param repo_name:
365 :param repo_name:
361 """
366 """
362
367
363 cur_token = request.POST.get('auth_token')
368 cur_token = request.POST.get('auth_token')
364 token = get_token()
369 token = get_token()
365 if cur_token == token:
370 if cur_token == token:
366 try:
371 try:
367 repo_id = Repository.get_by_repo_name(repo_name).repo_id
372 repo_id = Repository.get_by_repo_name(repo_name).repo_id
368 user_id = User.get_by_username('default').user_id
373 user_id = User.get_by_username('default').user_id
369 self.scm_model.toggle_following_repo(repo_id, user_id)
374 self.scm_model.toggle_following_repo(repo_id, user_id)
370 h.flash(_('Updated repository visibility in public journal'),
375 h.flash(_('Updated repository visibility in public journal'),
371 category='success')
376 category='success')
372 Session.commit()
377 Session.commit()
373 except:
378 except:
374 h.flash(_('An error occurred during setting this'
379 h.flash(_('An error occurred during setting this'
375 ' repository in public journal'),
380 ' repository in public journal'),
376 category='error')
381 category='error')
377
382
378 else:
383 else:
379 h.flash(_('Token mismatch'), category='error')
384 h.flash(_('Token mismatch'), category='error')
380 return redirect(url('edit_repo', repo_name=repo_name))
385 return redirect(url('edit_repo', repo_name=repo_name))
381
386
382 @HasPermissionAllDecorator('hg.admin')
387 @HasPermissionAllDecorator('hg.admin')
383 def repo_pull(self, repo_name):
388 def repo_pull(self, repo_name):
384 """
389 """
385 Runs task to update given repository with remote changes,
390 Runs task to update given repository with remote changes,
386 ie. make pull on remote location
391 ie. make pull on remote location
387
392
388 :param repo_name:
393 :param repo_name:
389 """
394 """
390 try:
395 try:
391 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
396 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
392 h.flash(_('Pulled from remote location'), category='success')
397 h.flash(_('Pulled from remote location'), category='success')
393 except Exception, e:
398 except Exception, e:
394 h.flash(_('An error occurred during pull from remote location'),
399 h.flash(_('An error occurred during pull from remote location'),
395 category='error')
400 category='error')
396
401
397 return redirect(url('edit_repo', repo_name=repo_name))
402 return redirect(url('edit_repo', repo_name=repo_name))
398
403
399 @HasPermissionAllDecorator('hg.admin')
404 @HasPermissionAllDecorator('hg.admin')
400 def repo_as_fork(self, repo_name):
405 def repo_as_fork(self, repo_name):
401 """
406 """
402 Mark given repository as a fork of another
407 Mark given repository as a fork of another
403
408
404 :param repo_name:
409 :param repo_name:
405 """
410 """
406 try:
411 try:
407 fork_id = request.POST.get('id_fork_of')
412 fork_id = request.POST.get('id_fork_of')
408 repo = ScmModel().mark_as_fork(repo_name, fork_id,
413 repo = ScmModel().mark_as_fork(repo_name, fork_id,
409 self.rhodecode_user.username)
414 self.rhodecode_user.username)
410 fork = repo.fork.repo_name if repo.fork else _('Nothing')
415 fork = repo.fork.repo_name if repo.fork else _('Nothing')
411 Session.commit()
416 Session.commit()
412 h.flash(_('Marked repo %s as fork of %s' % (repo_name,fork)),
417 h.flash(_('Marked repo %s as fork of %s' % (repo_name,fork)),
413 category='success')
418 category='success')
414 except Exception, e:
419 except Exception, e:
415 raise
420 raise
416 h.flash(_('An error occurred during this operation'),
421 h.flash(_('An error occurred during this operation'),
417 category='error')
422 category='error')
418
423
419 return redirect(url('edit_repo', repo_name=repo_name))
424 return redirect(url('edit_repo', repo_name=repo_name))
420
425
421 @HasPermissionAllDecorator('hg.admin')
426 @HasPermissionAllDecorator('hg.admin')
422 def show(self, repo_name, format='html'):
427 def show(self, repo_name, format='html'):
423 """GET /repos/repo_name: Show a specific item"""
428 """GET /repos/repo_name: Show a specific item"""
424 # url('repo', repo_name=ID)
429 # url('repo', repo_name=ID)
425
430
426 @HasPermissionAllDecorator('hg.admin')
431 @HasPermissionAllDecorator('hg.admin')
427 def edit(self, repo_name, format='html'):
432 def edit(self, repo_name, format='html'):
428 """GET /repos/repo_name/edit: Form to edit an existing item"""
433 """GET /repos/repo_name/edit: Form to edit an existing item"""
429 # url('edit_repo', repo_name=ID)
434 # url('edit_repo', repo_name=ID)
430 defaults = self.__load_data(repo_name)
435 defaults = self.__load_data(repo_name)
431
436
432 return htmlfill.render(
437 return htmlfill.render(
433 render('admin/repos/repo_edit.html'),
438 render('admin/repos/repo_edit.html'),
434 defaults=defaults,
439 defaults=defaults,
435 encoding="UTF-8",
440 encoding="UTF-8",
436 force_defaults=False
441 force_defaults=False
437 )
442 )
@@ -1,158 +1,162
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.settings
3 rhodecode.controllers.settings
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Settings controller for rhodecode
6 Settings controller for rhodecode
7
7
8 :created_on: Jun 30, 2010
8 :created_on: Jun 30, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 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
29
30 from formencode import htmlfill
30 from formencode import htmlfill
31
31
32 from pylons import tmpl_context as c, request, url
32 from pylons import tmpl_context as c, request, url
33 from pylons.controllers.util import redirect
33 from pylons.controllers.util import redirect
34 from pylons.i18n.translation import _
34 from pylons.i18n.translation import _
35
35
36 import rhodecode.lib.helpers as h
36 import rhodecode.lib.helpers as h
37
37
38 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator
38 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator
39 from rhodecode.lib.base import BaseRepoController, render
39 from rhodecode.lib.base import BaseRepoController, render
40 from rhodecode.lib.utils import invalidate_cache, action_logger
40 from rhodecode.lib.utils import invalidate_cache, action_logger
41
41
42 from rhodecode.model.forms import RepoSettingsForm
42 from rhodecode.model.forms import RepoSettingsForm
43 from rhodecode.model.repo import RepoModel
43 from rhodecode.model.repo import RepoModel
44 from rhodecode.model.db import RepoGroup
44 from rhodecode.model.db import RepoGroup
45 from rhodecode.model.meta import Session
45 from rhodecode.model.meta import Session
46 from rhodecode.model.scm import ScmModel
46
47
47 log = logging.getLogger(__name__)
48 log = logging.getLogger(__name__)
48
49
49
50
50 class SettingsController(BaseRepoController):
51 class SettingsController(BaseRepoController):
51
52
52 @LoginRequired()
53 @LoginRequired()
53 def __before__(self):
54 def __before__(self):
54 super(SettingsController, self).__before__()
55 super(SettingsController, self).__before__()
55
56
56 def __load_defaults(self):
57 def __load_defaults(self):
57 c.repo_groups = RepoGroup.groups_choices()
58 c.repo_groups = RepoGroup.groups_choices()
58 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
59 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
59
60
60 repo_model = RepoModel()
61 repo_model = RepoModel()
61 c.users_array = repo_model.get_users_js()
62 c.users_array = repo_model.get_users_js()
62 c.users_groups_array = repo_model.get_users_groups_js()
63 c.users_groups_array = repo_model.get_users_groups_js()
64 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
65 c.landing_revs_choices = choices
63
66
64 @HasRepoPermissionAllDecorator('repository.admin')
67 @HasRepoPermissionAllDecorator('repository.admin')
65 def index(self, repo_name):
68 def index(self, repo_name):
66 repo_model = RepoModel()
69 repo_model = RepoModel()
67 c.repo_info = repo = repo_model.get_by_repo_name(repo_name)
70 c.repo_info = repo = repo_model.get_by_repo_name(repo_name)
68 if not repo:
71 if not repo:
69 h.flash(_('%s repository is not mapped to db perhaps'
72 h.flash(_('%s repository is not mapped to db perhaps'
70 ' it was created or renamed from the file system'
73 ' it was created or renamed from the file system'
71 ' please run the application again'
74 ' please run the application again'
72 ' in order to rescan repositories') % repo_name,
75 ' in order to rescan repositories') % repo_name,
73 category='error')
76 category='error')
74
77
75 return redirect(url('home'))
78 return redirect(url('home'))
76
79
77 self.__load_defaults()
80 self.__load_defaults()
78
81
79 defaults = RepoModel()._get_defaults(repo_name)
82 defaults = RepoModel()._get_defaults(repo_name)
80
83
81 return htmlfill.render(
84 return htmlfill.render(
82 render('settings/repo_settings.html'),
85 render('settings/repo_settings.html'),
83 defaults=defaults,
86 defaults=defaults,
84 encoding="UTF-8",
87 encoding="UTF-8",
85 force_defaults=False
88 force_defaults=False
86 )
89 )
87
90
88 @HasRepoPermissionAllDecorator('repository.admin')
91 @HasRepoPermissionAllDecorator('repository.admin')
89 def update(self, repo_name):
92 def update(self, repo_name):
90 repo_model = RepoModel()
93 repo_model = RepoModel()
91 changed_name = repo_name
94 changed_name = repo_name
92
95
93 self.__load_defaults()
96 self.__load_defaults()
94
97
95 _form = RepoSettingsForm(edit=True,
98 _form = RepoSettingsForm(edit=True,
96 old_data={'repo_name': repo_name},
99 old_data={'repo_name': repo_name},
97 repo_groups=c.repo_groups_choices)()
100 repo_groups=c.repo_groups_choices,
101 landing_revs=c.landing_revs_choices)()
98 try:
102 try:
99 form_result = _form.to_python(dict(request.POST))
103 form_result = _form.to_python(dict(request.POST))
100
104
101 repo_model.update(repo_name, form_result)
105 repo_model.update(repo_name, form_result)
102 invalidate_cache('get_repo_cached_%s' % repo_name)
106 invalidate_cache('get_repo_cached_%s' % repo_name)
103 h.flash(_('Repository %s updated successfully' % repo_name),
107 h.flash(_('Repository %s updated successfully' % repo_name),
104 category='success')
108 category='success')
105 changed_name = form_result['repo_name_full']
109 changed_name = form_result['repo_name_full']
106 action_logger(self.rhodecode_user, 'user_updated_repo',
110 action_logger(self.rhodecode_user, 'user_updated_repo',
107 changed_name, self.ip_addr, self.sa)
111 changed_name, self.ip_addr, self.sa)
108 Session.commit()
112 Session.commit()
109 except formencode.Invalid, errors:
113 except formencode.Invalid, errors:
110 c.repo_info = repo_model.get_by_repo_name(repo_name)
114 c.repo_info = repo_model.get_by_repo_name(repo_name)
111 c.users_array = repo_model.get_users_js()
115 c.users_array = repo_model.get_users_js()
112 errors.value.update({'user': c.repo_info.user.username})
116 errors.value.update({'user': c.repo_info.user.username})
113 return htmlfill.render(
117 return htmlfill.render(
114 render('settings/repo_settings.html'),
118 render('settings/repo_settings.html'),
115 defaults=errors.value,
119 defaults=errors.value,
116 errors=errors.error_dict or {},
120 errors=errors.error_dict or {},
117 prefix_error=False,
121 prefix_error=False,
118 encoding="UTF-8")
122 encoding="UTF-8")
119 except Exception:
123 except Exception:
120 log.error(traceback.format_exc())
124 log.error(traceback.format_exc())
121 h.flash(_('error occurred during update of repository %s') \
125 h.flash(_('error occurred during update of repository %s') \
122 % repo_name, category='error')
126 % repo_name, category='error')
123
127
124 return redirect(url('repo_settings_home', repo_name=changed_name))
128 return redirect(url('repo_settings_home', repo_name=changed_name))
125
129
126 @HasRepoPermissionAllDecorator('repository.admin')
130 @HasRepoPermissionAllDecorator('repository.admin')
127 def delete(self, repo_name):
131 def delete(self, repo_name):
128 """DELETE /repos/repo_name: Delete an existing item"""
132 """DELETE /repos/repo_name: Delete an existing item"""
129 # Forms posted to this method should contain a hidden field:
133 # Forms posted to this method should contain a hidden field:
130 # <input type="hidden" name="_method" value="DELETE" />
134 # <input type="hidden" name="_method" value="DELETE" />
131 # Or using helpers:
135 # Or using helpers:
132 # h.form(url('repo_settings_delete', repo_name=ID),
136 # h.form(url('repo_settings_delete', repo_name=ID),
133 # method='delete')
137 # method='delete')
134 # url('repo_settings_delete', repo_name=ID)
138 # url('repo_settings_delete', repo_name=ID)
135
139
136 repo_model = RepoModel()
140 repo_model = RepoModel()
137 repo = repo_model.get_by_repo_name(repo_name)
141 repo = repo_model.get_by_repo_name(repo_name)
138 if not repo:
142 if not repo:
139 h.flash(_('%s repository is not mapped to db perhaps'
143 h.flash(_('%s repository is not mapped to db perhaps'
140 ' it was moved or renamed from the filesystem'
144 ' it was moved or renamed from the filesystem'
141 ' please run the application again'
145 ' please run the application again'
142 ' in order to rescan repositories') % repo_name,
146 ' in order to rescan repositories') % repo_name,
143 category='error')
147 category='error')
144
148
145 return redirect(url('home'))
149 return redirect(url('home'))
146 try:
150 try:
147 action_logger(self.rhodecode_user, 'user_deleted_repo',
151 action_logger(self.rhodecode_user, 'user_deleted_repo',
148 repo_name, self.ip_addr, self.sa)
152 repo_name, self.ip_addr, self.sa)
149 repo_model.delete(repo)
153 repo_model.delete(repo)
150 invalidate_cache('get_repo_cached_%s' % repo_name)
154 invalidate_cache('get_repo_cached_%s' % repo_name)
151 h.flash(_('deleted repository %s') % repo_name, category='success')
155 h.flash(_('deleted repository %s') % repo_name, category='success')
152 Session.commit()
156 Session.commit()
153 except Exception:
157 except Exception:
154 log.error(traceback.format_exc())
158 log.error(traceback.format_exc())
155 h.flash(_('An error occurred during deletion of %s') % repo_name,
159 h.flash(_('An error occurred during deletion of %s') % repo_name,
156 category='error')
160 category='error')
157
161
158 return redirect(url('home'))
162 return redirect(url('home'))
@@ -1,774 +1,774
1 """ this is forms validation classes
1 """ this is forms validation classes
2 http://formencode.org/module-formencode.validators.html
2 http://formencode.org/module-formencode.validators.html
3 for list off all availible validators
3 for list off all availible validators
4
4
5 we can create our own validators
5 we can create our own validators
6
6
7 The table below outlines the options which can be used in a schema in addition to the validators themselves
7 The table below outlines the options which can be used in a schema in addition to the validators themselves
8 pre_validators [] These validators will be applied before the schema
8 pre_validators [] These validators will be applied before the schema
9 chained_validators [] These validators will be applied after the schema
9 chained_validators [] These validators will be applied after the schema
10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
14
14
15
15
16 <name> = formencode.validators.<name of validator>
16 <name> = formencode.validators.<name of validator>
17 <name> must equal form name
17 <name> must equal form name
18 list=[1,2,3,4,5]
18 list=[1,2,3,4,5]
19 for SELECT use formencode.All(OneOf(list), Int())
19 for SELECT use formencode.All(OneOf(list), Int())
20
20
21 """
21 """
22 import os
22 import os
23 import re
23 import re
24 import logging
24 import logging
25 import traceback
25 import traceback
26
26
27 import formencode
27 import formencode
28 from formencode import All
28 from formencode import All
29 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
29 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
30 Email, Bool, StringBoolean, Set
30 Email, Bool, StringBoolean, Set
31
31
32 from pylons.i18n.translation import _
32 from pylons.i18n.translation import _
33 from webhelpers.pylonslib.secure_form import authentication_token
33 from webhelpers.pylonslib.secure_form import authentication_token
34
34
35 from rhodecode.config.routing import ADMIN_PREFIX
35 from rhodecode.config.routing import ADMIN_PREFIX
36 from rhodecode.lib.utils import repo_name_slug
36 from rhodecode.lib.utils import repo_name_slug
37 from rhodecode.lib.auth import authenticate, get_crypt_password
37 from rhodecode.lib.auth import authenticate, get_crypt_password
38 from rhodecode.lib.exceptions import LdapImportError
38 from rhodecode.lib.exceptions import LdapImportError
39 from rhodecode.model.db import User, UsersGroup, RepoGroup, Repository
39 from rhodecode.model.db import User, UsersGroup, RepoGroup, Repository
40 from rhodecode import BACKENDS
40 from rhodecode import BACKENDS
41
41
42 log = logging.getLogger(__name__)
42 log = logging.getLogger(__name__)
43
43
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 #==============================================================================
50 #==============================================================================
51 # VALIDATORS
51 # VALIDATORS
52 #==============================================================================
52 #==============================================================================
53 class ValidAuthToken(formencode.validators.FancyValidator):
53 class ValidAuthToken(formencode.validators.FancyValidator):
54 messages = {'invalid_token': _('Token mismatch')}
54 messages = {'invalid_token': _('Token mismatch')}
55
55
56 def validate_python(self, value, state):
56 def validate_python(self, value, state):
57
57
58 if value != authentication_token():
58 if value != authentication_token():
59 raise formencode.Invalid(
59 raise formencode.Invalid(
60 self.message('invalid_token',
60 self.message('invalid_token',
61 state, search_number=value),
61 state, search_number=value),
62 value,
62 value,
63 state
63 state
64 )
64 )
65
65
66
66
67 def ValidUsername(edit, old_data):
67 def ValidUsername(edit, old_data):
68 class _ValidUsername(formencode.validators.FancyValidator):
68 class _ValidUsername(formencode.validators.FancyValidator):
69
69
70 def validate_python(self, value, state):
70 def validate_python(self, value, state):
71 if value in ['default', 'new_user']:
71 if value in ['default', 'new_user']:
72 raise formencode.Invalid(_('Invalid username'), value, state)
72 raise formencode.Invalid(_('Invalid username'), value, state)
73 #check if user is unique
73 #check if user is unique
74 old_un = None
74 old_un = None
75 if edit:
75 if edit:
76 old_un = User.get(old_data.get('user_id')).username
76 old_un = User.get(old_data.get('user_id')).username
77
77
78 if old_un != value or not edit:
78 if old_un != value or not edit:
79 if User.get_by_username(value, case_insensitive=True):
79 if User.get_by_username(value, case_insensitive=True):
80 raise formencode.Invalid(_('This username already '
80 raise formencode.Invalid(_('This username already '
81 'exists') , value, state)
81 'exists') , value, state)
82
82
83 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
83 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
84 raise formencode.Invalid(
84 raise formencode.Invalid(
85 _('Username may only contain alphanumeric characters '
85 _('Username may only contain alphanumeric characters '
86 'underscores, periods or dashes and must begin with '
86 'underscores, periods or dashes and must begin with '
87 'alphanumeric character'),
87 'alphanumeric character'),
88 value,
88 value,
89 state
89 state
90 )
90 )
91
91
92 return _ValidUsername
92 return _ValidUsername
93
93
94
94
95 def ValidUsersGroup(edit, old_data):
95 def ValidUsersGroup(edit, old_data):
96
96
97 class _ValidUsersGroup(formencode.validators.FancyValidator):
97 class _ValidUsersGroup(formencode.validators.FancyValidator):
98
98
99 def validate_python(self, value, state):
99 def validate_python(self, value, state):
100 if value in ['default']:
100 if value in ['default']:
101 raise formencode.Invalid(_('Invalid group name'), value, state)
101 raise formencode.Invalid(_('Invalid group name'), value, state)
102 #check if group is unique
102 #check if group is unique
103 old_ugname = None
103 old_ugname = None
104 if edit:
104 if edit:
105 old_ugname = UsersGroup.get(
105 old_ugname = UsersGroup.get(
106 old_data.get('users_group_id')).users_group_name
106 old_data.get('users_group_id')).users_group_name
107
107
108 if old_ugname != value or not edit:
108 if old_ugname != value or not edit:
109 if UsersGroup.get_by_group_name(value, cache=False,
109 if UsersGroup.get_by_group_name(value, cache=False,
110 case_insensitive=True):
110 case_insensitive=True):
111 raise formencode.Invalid(_('This users group '
111 raise formencode.Invalid(_('This users group '
112 'already exists'), value,
112 'already exists'), value,
113 state)
113 state)
114
114
115 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
115 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
116 raise formencode.Invalid(
116 raise formencode.Invalid(
117 _('RepoGroup name may only contain alphanumeric characters '
117 _('RepoGroup name may only contain alphanumeric characters '
118 'underscores, periods or dashes and must begin with '
118 'underscores, periods or dashes and must begin with '
119 'alphanumeric character'),
119 'alphanumeric character'),
120 value,
120 value,
121 state
121 state
122 )
122 )
123
123
124 return _ValidUsersGroup
124 return _ValidUsersGroup
125
125
126
126
127 def ValidReposGroup(edit, old_data):
127 def ValidReposGroup(edit, old_data):
128 class _ValidReposGroup(formencode.validators.FancyValidator):
128 class _ValidReposGroup(formencode.validators.FancyValidator):
129
129
130 def validate_python(self, value, state):
130 def validate_python(self, value, state):
131 # TODO WRITE VALIDATIONS
131 # TODO WRITE VALIDATIONS
132 group_name = value.get('group_name')
132 group_name = value.get('group_name')
133 group_parent_id = value.get('group_parent_id')
133 group_parent_id = value.get('group_parent_id')
134
134
135 # slugify repo group just in case :)
135 # slugify repo group just in case :)
136 slug = repo_name_slug(group_name)
136 slug = repo_name_slug(group_name)
137
137
138 # check for parent of self
138 # check for parent of self
139 parent_of_self = lambda: (
139 parent_of_self = lambda: (
140 old_data['group_id'] == int(group_parent_id)
140 old_data['group_id'] == int(group_parent_id)
141 if group_parent_id else False
141 if group_parent_id else False
142 )
142 )
143 if edit and parent_of_self():
143 if edit and parent_of_self():
144 e_dict = {
144 e_dict = {
145 'group_parent_id': _('Cannot assign this group as parent')
145 'group_parent_id': _('Cannot assign this group as parent')
146 }
146 }
147 raise formencode.Invalid('', value, state,
147 raise formencode.Invalid('', value, state,
148 error_dict=e_dict)
148 error_dict=e_dict)
149
149
150 old_gname = None
150 old_gname = None
151 if edit:
151 if edit:
152 old_gname = RepoGroup.get(old_data.get('group_id')).group_name
152 old_gname = RepoGroup.get(old_data.get('group_id')).group_name
153
153
154 if old_gname != group_name or not edit:
154 if old_gname != group_name or not edit:
155
155
156 # check group
156 # check group
157 gr = RepoGroup.query()\
157 gr = RepoGroup.query()\
158 .filter(RepoGroup.group_name == slug)\
158 .filter(RepoGroup.group_name == slug)\
159 .filter(RepoGroup.group_parent_id == group_parent_id)\
159 .filter(RepoGroup.group_parent_id == group_parent_id)\
160 .scalar()
160 .scalar()
161
161
162 if gr:
162 if gr:
163 e_dict = {
163 e_dict = {
164 'group_name': _('This group already exists')
164 'group_name': _('This group already exists')
165 }
165 }
166 raise formencode.Invalid('', value, state,
166 raise formencode.Invalid('', value, state,
167 error_dict=e_dict)
167 error_dict=e_dict)
168
168
169 # check for same repo
169 # check for same repo
170 repo = Repository.query()\
170 repo = Repository.query()\
171 .filter(Repository.repo_name == slug)\
171 .filter(Repository.repo_name == slug)\
172 .scalar()
172 .scalar()
173
173
174 if repo:
174 if repo:
175 e_dict = {
175 e_dict = {
176 'group_name': _('Repository with this name already exists')
176 'group_name': _('Repository with this name already exists')
177 }
177 }
178 raise formencode.Invalid('', value, state,
178 raise formencode.Invalid('', value, state,
179 error_dict=e_dict)
179 error_dict=e_dict)
180
180
181 return _ValidReposGroup
181 return _ValidReposGroup
182
182
183
183
184 class ValidPassword(formencode.validators.FancyValidator):
184 class ValidPassword(formencode.validators.FancyValidator):
185
185
186 def to_python(self, value, state):
186 def to_python(self, value, state):
187
187
188 if not value:
188 if not value:
189 return
189 return
190
190
191 if value.get('password'):
191 if value.get('password'):
192 try:
192 try:
193 value['password'] = get_crypt_password(value['password'])
193 value['password'] = get_crypt_password(value['password'])
194 except UnicodeEncodeError:
194 except UnicodeEncodeError:
195 e_dict = {'password': _('Invalid characters in password')}
195 e_dict = {'password': _('Invalid characters in password')}
196 raise formencode.Invalid('', value, state, error_dict=e_dict)
196 raise formencode.Invalid('', value, state, error_dict=e_dict)
197
197
198 if value.get('password_confirmation'):
198 if value.get('password_confirmation'):
199 try:
199 try:
200 value['password_confirmation'] = \
200 value['password_confirmation'] = \
201 get_crypt_password(value['password_confirmation'])
201 get_crypt_password(value['password_confirmation'])
202 except UnicodeEncodeError:
202 except UnicodeEncodeError:
203 e_dict = {
203 e_dict = {
204 'password_confirmation': _('Invalid characters in password')
204 'password_confirmation': _('Invalid characters in password')
205 }
205 }
206 raise formencode.Invalid('', value, state, error_dict=e_dict)
206 raise formencode.Invalid('', value, state, error_dict=e_dict)
207
207
208 if value.get('new_password'):
208 if value.get('new_password'):
209 try:
209 try:
210 value['new_password'] = \
210 value['new_password'] = \
211 get_crypt_password(value['new_password'])
211 get_crypt_password(value['new_password'])
212 except UnicodeEncodeError:
212 except UnicodeEncodeError:
213 e_dict = {'new_password': _('Invalid characters in password')}
213 e_dict = {'new_password': _('Invalid characters in password')}
214 raise formencode.Invalid('', value, state, error_dict=e_dict)
214 raise formencode.Invalid('', value, state, error_dict=e_dict)
215
215
216 return value
216 return value
217
217
218
218
219 class ValidPasswordsMatch(formencode.validators.FancyValidator):
219 class ValidPasswordsMatch(formencode.validators.FancyValidator):
220
220
221 def validate_python(self, value, state):
221 def validate_python(self, value, state):
222
222
223 pass_val = value.get('password') or value.get('new_password')
223 pass_val = value.get('password') or value.get('new_password')
224 if pass_val != value['password_confirmation']:
224 if pass_val != value['password_confirmation']:
225 e_dict = {'password_confirmation':
225 e_dict = {'password_confirmation':
226 _('Passwords do not match')}
226 _('Passwords do not match')}
227 raise formencode.Invalid('', value, state, error_dict=e_dict)
227 raise formencode.Invalid('', value, state, error_dict=e_dict)
228
228
229
229
230 class ValidAuth(formencode.validators.FancyValidator):
230 class ValidAuth(formencode.validators.FancyValidator):
231 messages = {
231 messages = {
232 'invalid_password':_('invalid password'),
232 'invalid_password':_('invalid password'),
233 'invalid_login':_('invalid user name'),
233 'invalid_login':_('invalid user name'),
234 'disabled_account':_('Your account is disabled')
234 'disabled_account':_('Your account is disabled')
235 }
235 }
236
236
237 # error mapping
237 # error mapping
238 e_dict = {'username': messages['invalid_login'],
238 e_dict = {'username': messages['invalid_login'],
239 'password': messages['invalid_password']}
239 'password': messages['invalid_password']}
240 e_dict_disable = {'username': messages['disabled_account']}
240 e_dict_disable = {'username': messages['disabled_account']}
241
241
242 def validate_python(self, value, state):
242 def validate_python(self, value, state):
243 password = value['password']
243 password = value['password']
244 username = value['username']
244 username = value['username']
245 user = User.get_by_username(username)
245 user = User.get_by_username(username)
246
246
247 if authenticate(username, password):
247 if authenticate(username, password):
248 return value
248 return value
249 else:
249 else:
250 if user and user.active is False:
250 if user and user.active is False:
251 log.warning('user %s is disabled' % username)
251 log.warning('user %s is disabled' % username)
252 raise formencode.Invalid(
252 raise formencode.Invalid(
253 self.message('disabled_account',
253 self.message('disabled_account',
254 state=State_obj),
254 state=State_obj),
255 value, state,
255 value, state,
256 error_dict=self.e_dict_disable
256 error_dict=self.e_dict_disable
257 )
257 )
258 else:
258 else:
259 log.warning('user %s failed to authenticate' % username)
259 log.warning('user %s failed to authenticate' % username)
260 raise formencode.Invalid(
260 raise formencode.Invalid(
261 self.message('invalid_password',
261 self.message('invalid_password',
262 state=State_obj), value, state,
262 state=State_obj), value, state,
263 error_dict=self.e_dict
263 error_dict=self.e_dict
264 )
264 )
265
265
266
266
267 class ValidRepoUser(formencode.validators.FancyValidator):
267 class ValidRepoUser(formencode.validators.FancyValidator):
268
268
269 def to_python(self, value, state):
269 def to_python(self, value, state):
270 try:
270 try:
271 User.query().filter(User.active == True)\
271 User.query().filter(User.active == True)\
272 .filter(User.username == value).one()
272 .filter(User.username == value).one()
273 except Exception:
273 except Exception:
274 raise formencode.Invalid(_('This username is not valid'),
274 raise formencode.Invalid(_('This username is not valid'),
275 value, state)
275 value, state)
276 return value
276 return value
277
277
278
278
279 def ValidRepoName(edit, old_data):
279 def ValidRepoName(edit, old_data):
280 class _ValidRepoName(formencode.validators.FancyValidator):
280 class _ValidRepoName(formencode.validators.FancyValidator):
281 def to_python(self, value, state):
281 def to_python(self, value, state):
282
282
283 repo_name = value.get('repo_name')
283 repo_name = value.get('repo_name')
284
284
285 slug = repo_name_slug(repo_name)
285 slug = repo_name_slug(repo_name)
286 if slug in [ADMIN_PREFIX, '']:
286 if slug in [ADMIN_PREFIX, '']:
287 e_dict = {'repo_name': _('This repository name is disallowed')}
287 e_dict = {'repo_name': _('This repository name is disallowed')}
288 raise formencode.Invalid('', value, state, error_dict=e_dict)
288 raise formencode.Invalid('', value, state, error_dict=e_dict)
289
289
290 if value.get('repo_group'):
290 if value.get('repo_group'):
291 gr = RepoGroup.get(value.get('repo_group'))
291 gr = RepoGroup.get(value.get('repo_group'))
292 group_path = gr.full_path
292 group_path = gr.full_path
293 # value needs to be aware of group name in order to check
293 # value needs to be aware of group name in order to check
294 # db key This is an actual just the name to store in the
294 # db key This is an actual just the name to store in the
295 # database
295 # database
296 repo_name_full = group_path + RepoGroup.url_sep() + repo_name
296 repo_name_full = group_path + RepoGroup.url_sep() + repo_name
297
297
298 else:
298 else:
299 group_path = ''
299 group_path = ''
300 repo_name_full = repo_name
300 repo_name_full = repo_name
301
301
302 value['repo_name_full'] = repo_name_full
302 value['repo_name_full'] = repo_name_full
303 rename = old_data.get('repo_name') != repo_name_full
303 rename = old_data.get('repo_name') != repo_name_full
304 create = not edit
304 create = not edit
305 if rename or create:
305 if rename or create:
306
306
307 if group_path != '':
307 if group_path != '':
308 if Repository.get_by_repo_name(repo_name_full):
308 if Repository.get_by_repo_name(repo_name_full):
309 e_dict = {
309 e_dict = {
310 'repo_name': _('This repository already exists in '
310 'repo_name': _('This repository already exists in '
311 'a group "%s"') % gr.group_name
311 'a group "%s"') % gr.group_name
312 }
312 }
313 raise formencode.Invalid('', value, state,
313 raise formencode.Invalid('', value, state,
314 error_dict=e_dict)
314 error_dict=e_dict)
315 elif RepoGroup.get_by_group_name(repo_name_full):
315 elif RepoGroup.get_by_group_name(repo_name_full):
316 e_dict = {
316 e_dict = {
317 'repo_name': _('There is a group with this name '
317 'repo_name': _('There is a group with this name '
318 'already "%s"') % repo_name_full
318 'already "%s"') % repo_name_full
319 }
319 }
320 raise formencode.Invalid('', value, state,
320 raise formencode.Invalid('', value, state,
321 error_dict=e_dict)
321 error_dict=e_dict)
322
322
323 elif Repository.get_by_repo_name(repo_name_full):
323 elif Repository.get_by_repo_name(repo_name_full):
324 e_dict = {'repo_name': _('This repository '
324 e_dict = {'repo_name': _('This repository '
325 'already exists')}
325 'already exists')}
326 raise formencode.Invalid('', value, state,
326 raise formencode.Invalid('', value, state,
327 error_dict=e_dict)
327 error_dict=e_dict)
328
328
329 return value
329 return value
330
330
331 return _ValidRepoName
331 return _ValidRepoName
332
332
333
333
334 def ValidForkName(*args, **kwargs):
334 def ValidForkName(*args, **kwargs):
335 return ValidRepoName(*args, **kwargs)
335 return ValidRepoName(*args, **kwargs)
336
336
337
337
338 def SlugifyName():
338 def SlugifyName():
339 class _SlugifyName(formencode.validators.FancyValidator):
339 class _SlugifyName(formencode.validators.FancyValidator):
340
340
341 def to_python(self, value, state):
341 def to_python(self, value, state):
342 return repo_name_slug(value)
342 return repo_name_slug(value)
343
343
344 return _SlugifyName
344 return _SlugifyName
345
345
346
346
347 def ValidCloneUri():
347 def ValidCloneUri():
348 from rhodecode.lib.utils import make_ui
348 from rhodecode.lib.utils import make_ui
349
349
350 def url_handler(repo_type, url, proto, ui=None):
350 def url_handler(repo_type, url, proto, ui=None):
351 if repo_type == 'hg':
351 if repo_type == 'hg':
352 from mercurial.httprepo import httprepository, httpsrepository
352 from mercurial.httprepo import httprepository, httpsrepository
353 if proto == 'https':
353 if proto == 'https':
354 httpsrepository(make_ui('db'), url).capabilities
354 httpsrepository(make_ui('db'), url).capabilities
355 elif proto == 'http':
355 elif proto == 'http':
356 httprepository(make_ui('db'), url).capabilities
356 httprepository(make_ui('db'), url).capabilities
357 elif repo_type == 'git':
357 elif repo_type == 'git':
358 #TODO: write a git url validator
358 #TODO: write a git url validator
359 pass
359 pass
360
360
361 class _ValidCloneUri(formencode.validators.FancyValidator):
361 class _ValidCloneUri(formencode.validators.FancyValidator):
362
362
363 def to_python(self, value, state):
363 def to_python(self, value, state):
364
364
365 repo_type = value.get('repo_type')
365 repo_type = value.get('repo_type')
366 url = value.get('clone_uri')
366 url = value.get('clone_uri')
367 e_dict = {'clone_uri': _('invalid clone url')}
367 e_dict = {'clone_uri': _('invalid clone url')}
368
368
369 if not url:
369 if not url:
370 pass
370 pass
371 elif url.startswith('https'):
371 elif url.startswith('https'):
372 try:
372 try:
373 url_handler(repo_type, url, 'https', make_ui('db'))
373 url_handler(repo_type, url, 'https', make_ui('db'))
374 except Exception:
374 except Exception:
375 log.error(traceback.format_exc())
375 log.error(traceback.format_exc())
376 raise formencode.Invalid('', value, state, error_dict=e_dict)
376 raise formencode.Invalid('', value, state, error_dict=e_dict)
377 elif url.startswith('http'):
377 elif url.startswith('http'):
378 try:
378 try:
379 url_handler(repo_type, url, 'http', make_ui('db'))
379 url_handler(repo_type, url, 'http', make_ui('db'))
380 except Exception:
380 except Exception:
381 log.error(traceback.format_exc())
381 log.error(traceback.format_exc())
382 raise formencode.Invalid('', value, state, error_dict=e_dict)
382 raise formencode.Invalid('', value, state, error_dict=e_dict)
383 else:
383 else:
384 e_dict = {'clone_uri': _('Invalid clone url, provide a '
384 e_dict = {'clone_uri': _('Invalid clone url, provide a '
385 'valid clone http\s url')}
385 'valid clone http\s url')}
386 raise formencode.Invalid('', value, state, error_dict=e_dict)
386 raise formencode.Invalid('', value, state, error_dict=e_dict)
387
387
388 return value
388 return value
389
389
390 return _ValidCloneUri
390 return _ValidCloneUri
391
391
392
392
393 def ValidForkType(old_data):
393 def ValidForkType(old_data):
394 class _ValidForkType(formencode.validators.FancyValidator):
394 class _ValidForkType(formencode.validators.FancyValidator):
395
395
396 def to_python(self, value, state):
396 def to_python(self, value, state):
397 if old_data['repo_type'] != value:
397 if old_data['repo_type'] != value:
398 raise formencode.Invalid(_('Fork have to be the same '
398 raise formencode.Invalid(_('Fork have to be the same '
399 'type as original'), value, state)
399 'type as original'), value, state)
400
400
401 return value
401 return value
402 return _ValidForkType
402 return _ValidForkType
403
403
404
404
405 def ValidPerms(type_='repo'):
405 def ValidPerms(type_='repo'):
406 if type_ == 'group':
406 if type_ == 'group':
407 EMPTY_PERM = 'group.none'
407 EMPTY_PERM = 'group.none'
408 elif type_ == 'repo':
408 elif type_ == 'repo':
409 EMPTY_PERM = 'repository.none'
409 EMPTY_PERM = 'repository.none'
410
410
411 class _ValidPerms(formencode.validators.FancyValidator):
411 class _ValidPerms(formencode.validators.FancyValidator):
412 messages = {
412 messages = {
413 'perm_new_member_name':
413 'perm_new_member_name':
414 _('This username or users group name is not valid')
414 _('This username or users group name is not valid')
415 }
415 }
416
416
417 def to_python(self, value, state):
417 def to_python(self, value, state):
418 perms_update = []
418 perms_update = []
419 perms_new = []
419 perms_new = []
420 # build a list of permission to update and new permission to create
420 # build a list of permission to update and new permission to create
421 for k, v in value.items():
421 for k, v in value.items():
422 # means new added member to permissions
422 # means new added member to permissions
423 if k.startswith('perm_new_member'):
423 if k.startswith('perm_new_member'):
424 new_perm = value.get('perm_new_member', False)
424 new_perm = value.get('perm_new_member', False)
425 new_member = value.get('perm_new_member_name', False)
425 new_member = value.get('perm_new_member_name', False)
426 new_type = value.get('perm_new_member_type')
426 new_type = value.get('perm_new_member_type')
427
427
428 if new_member and new_perm:
428 if new_member and new_perm:
429 if (new_member, new_perm, new_type) not in perms_new:
429 if (new_member, new_perm, new_type) not in perms_new:
430 perms_new.append((new_member, new_perm, new_type))
430 perms_new.append((new_member, new_perm, new_type))
431 elif k.startswith('u_perm_') or k.startswith('g_perm_'):
431 elif k.startswith('u_perm_') or k.startswith('g_perm_'):
432 member = k[7:]
432 member = k[7:]
433 t = {'u': 'user',
433 t = {'u': 'user',
434 'g': 'users_group'
434 'g': 'users_group'
435 }[k[0]]
435 }[k[0]]
436 if member == 'default':
436 if member == 'default':
437 if value.get('private'):
437 if value.get('private'):
438 # set none for default when updating to private repo
438 # set none for default when updating to private repo
439 v = EMPTY_PERM
439 v = EMPTY_PERM
440 perms_update.append((member, v, t))
440 perms_update.append((member, v, t))
441
441
442 value['perms_updates'] = perms_update
442 value['perms_updates'] = perms_update
443 value['perms_new'] = perms_new
443 value['perms_new'] = perms_new
444
444
445 # update permissions
445 # update permissions
446 for k, v, t in perms_new:
446 for k, v, t in perms_new:
447 try:
447 try:
448 if t is 'user':
448 if t is 'user':
449 self.user_db = User.query()\
449 self.user_db = User.query()\
450 .filter(User.active == True)\
450 .filter(User.active == True)\
451 .filter(User.username == k).one()
451 .filter(User.username == k).one()
452 if t is 'users_group':
452 if t is 'users_group':
453 self.user_db = UsersGroup.query()\
453 self.user_db = UsersGroup.query()\
454 .filter(UsersGroup.users_group_active == True)\
454 .filter(UsersGroup.users_group_active == True)\
455 .filter(UsersGroup.users_group_name == k).one()
455 .filter(UsersGroup.users_group_name == k).one()
456
456
457 except Exception:
457 except Exception:
458 msg = self.message('perm_new_member_name',
458 msg = self.message('perm_new_member_name',
459 state=State_obj)
459 state=State_obj)
460 raise formencode.Invalid(
460 raise formencode.Invalid(
461 msg, value, state, error_dict={'perm_new_member_name': msg}
461 msg, value, state, error_dict={'perm_new_member_name': msg}
462 )
462 )
463 return value
463 return value
464 return _ValidPerms
464 return _ValidPerms
465
465
466
466
467 class ValidSettings(formencode.validators.FancyValidator):
467 class ValidSettings(formencode.validators.FancyValidator):
468
468
469 def to_python(self, value, state):
469 def to_python(self, value, state):
470 # settings form can't edit user
470 # settings form can't edit user
471 if 'user' in value:
471 if 'user' in value:
472 del['value']['user']
472 del['value']['user']
473 return value
473 return value
474
474
475
475
476 class ValidPath(formencode.validators.FancyValidator):
476 class ValidPath(formencode.validators.FancyValidator):
477 def to_python(self, value, state):
477 def to_python(self, value, state):
478
478
479 if not os.path.isdir(value):
479 if not os.path.isdir(value):
480 msg = _('This is not a valid path')
480 msg = _('This is not a valid path')
481 raise formencode.Invalid(msg, value, state,
481 raise formencode.Invalid(msg, value, state,
482 error_dict={'paths_root_path': msg})
482 error_dict={'paths_root_path': msg})
483 return value
483 return value
484
484
485
485
486 def UniqSystemEmail(old_data):
486 def UniqSystemEmail(old_data):
487 class _UniqSystemEmail(formencode.validators.FancyValidator):
487 class _UniqSystemEmail(formencode.validators.FancyValidator):
488 def to_python(self, value, state):
488 def to_python(self, value, state):
489 value = value.lower()
489 value = value.lower()
490 if (old_data.get('email') or '').lower() != value:
490 if (old_data.get('email') or '').lower() != value:
491 user = User.get_by_email(value, case_insensitive=True)
491 user = User.get_by_email(value, case_insensitive=True)
492 if user:
492 if user:
493 raise formencode.Invalid(
493 raise formencode.Invalid(
494 _("This e-mail address is already taken"), value, state
494 _("This e-mail address is already taken"), value, state
495 )
495 )
496 return value
496 return value
497
497
498 return _UniqSystemEmail
498 return _UniqSystemEmail
499
499
500
500
501 class ValidSystemEmail(formencode.validators.FancyValidator):
501 class ValidSystemEmail(formencode.validators.FancyValidator):
502 def to_python(self, value, state):
502 def to_python(self, value, state):
503 value = value.lower()
503 value = value.lower()
504 user = User.get_by_email(value, case_insensitive=True)
504 user = User.get_by_email(value, case_insensitive=True)
505 if user is None:
505 if user is None:
506 raise formencode.Invalid(
506 raise formencode.Invalid(
507 _("This e-mail address doesn't exist."), value, state
507 _("This e-mail address doesn't exist."), value, state
508 )
508 )
509
509
510 return value
510 return value
511
511
512
512
513 class LdapLibValidator(formencode.validators.FancyValidator):
513 class LdapLibValidator(formencode.validators.FancyValidator):
514
514
515 def to_python(self, value, state):
515 def to_python(self, value, state):
516
516
517 try:
517 try:
518 import ldap
518 import ldap
519 except ImportError:
519 except ImportError:
520 raise LdapImportError
520 raise LdapImportError
521 return value
521 return value
522
522
523
523
524 class AttrLoginValidator(formencode.validators.FancyValidator):
524 class AttrLoginValidator(formencode.validators.FancyValidator):
525
525
526 def to_python(self, value, state):
526 def to_python(self, value, state):
527
527
528 if not value or not isinstance(value, (str, unicode)):
528 if not value or not isinstance(value, (str, unicode)):
529 raise formencode.Invalid(
529 raise formencode.Invalid(
530 _("The LDAP Login attribute of the CN must be specified - "
530 _("The LDAP Login attribute of the CN must be specified - "
531 "this is the name of the attribute that is equivalent "
531 "this is the name of the attribute that is equivalent "
532 "to 'username'"), value, state
532 "to 'username'"), value, state
533 )
533 )
534
534
535 return value
535 return value
536
536
537
537
538 #==============================================================================
538 #==============================================================================
539 # FORMS
539 # FORMS
540 #==============================================================================
540 #==============================================================================
541 class LoginForm(formencode.Schema):
541 class LoginForm(formencode.Schema):
542 allow_extra_fields = True
542 allow_extra_fields = True
543 filter_extra_fields = True
543 filter_extra_fields = True
544 username = UnicodeString(
544 username = UnicodeString(
545 strip=True,
545 strip=True,
546 min=1,
546 min=1,
547 not_empty=True,
547 not_empty=True,
548 messages={
548 messages={
549 'empty': _('Please enter a login'),
549 'empty': _('Please enter a login'),
550 'tooShort': _('Enter a value %(min)i characters long or more')}
550 'tooShort': _('Enter a value %(min)i characters long or more')}
551 )
551 )
552
552
553 password = UnicodeString(
553 password = UnicodeString(
554 strip=False,
554 strip=False,
555 min=3,
555 min=3,
556 not_empty=True,
556 not_empty=True,
557 messages={
557 messages={
558 'empty': _('Please enter a password'),
558 'empty': _('Please enter a password'),
559 'tooShort': _('Enter %(min)i characters or more')}
559 'tooShort': _('Enter %(min)i characters or more')}
560 )
560 )
561
561
562 remember = StringBoolean(if_missing=False)
562 remember = StringBoolean(if_missing=False)
563
563
564 chained_validators = [ValidAuth]
564 chained_validators = [ValidAuth]
565
565
566
566
567 def UserForm(edit=False, old_data={}):
567 def UserForm(edit=False, old_data={}):
568 class _UserForm(formencode.Schema):
568 class _UserForm(formencode.Schema):
569 allow_extra_fields = True
569 allow_extra_fields = True
570 filter_extra_fields = True
570 filter_extra_fields = True
571 username = All(UnicodeString(strip=True, min=1, not_empty=True),
571 username = All(UnicodeString(strip=True, min=1, not_empty=True),
572 ValidUsername(edit, old_data))
572 ValidUsername(edit, old_data))
573 if edit:
573 if edit:
574 new_password = All(UnicodeString(strip=False, min=6, not_empty=False))
574 new_password = All(UnicodeString(strip=False, min=6, not_empty=False))
575 password_confirmation = All(UnicodeString(strip=False, min=6,
575 password_confirmation = All(UnicodeString(strip=False, min=6,
576 not_empty=False))
576 not_empty=False))
577 admin = StringBoolean(if_missing=False)
577 admin = StringBoolean(if_missing=False)
578 else:
578 else:
579 password = All(UnicodeString(strip=False, min=6, not_empty=True))
579 password = All(UnicodeString(strip=False, min=6, not_empty=True))
580 password_confirmation = All(UnicodeString(strip=False, min=6,
580 password_confirmation = All(UnicodeString(strip=False, min=6,
581 not_empty=False))
581 not_empty=False))
582
582
583 active = StringBoolean(if_missing=False)
583 active = StringBoolean(if_missing=False)
584 name = UnicodeString(strip=True, min=1, not_empty=False)
584 name = UnicodeString(strip=True, min=1, not_empty=False)
585 lastname = UnicodeString(strip=True, min=1, not_empty=False)
585 lastname = UnicodeString(strip=True, min=1, not_empty=False)
586 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
586 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
587
587
588 chained_validators = [ValidPasswordsMatch, ValidPassword]
588 chained_validators = [ValidPasswordsMatch, ValidPassword]
589
589
590 return _UserForm
590 return _UserForm
591
591
592
592
593 def UsersGroupForm(edit=False, old_data={}, available_members=[]):
593 def UsersGroupForm(edit=False, old_data={}, available_members=[]):
594 class _UsersGroupForm(formencode.Schema):
594 class _UsersGroupForm(formencode.Schema):
595 allow_extra_fields = True
595 allow_extra_fields = True
596 filter_extra_fields = True
596 filter_extra_fields = True
597
597
598 users_group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
598 users_group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
599 ValidUsersGroup(edit, old_data))
599 ValidUsersGroup(edit, old_data))
600
600
601 users_group_active = StringBoolean(if_missing=False)
601 users_group_active = StringBoolean(if_missing=False)
602
602
603 if edit:
603 if edit:
604 users_group_members = OneOf(available_members, hideList=False,
604 users_group_members = OneOf(available_members, hideList=False,
605 testValueList=True,
605 testValueList=True,
606 if_missing=None, not_empty=False)
606 if_missing=None, not_empty=False)
607
607
608 return _UsersGroupForm
608 return _UsersGroupForm
609
609
610
610
611 def ReposGroupForm(edit=False, old_data={}, available_groups=[]):
611 def ReposGroupForm(edit=False, old_data={}, available_groups=[]):
612 class _ReposGroupForm(formencode.Schema):
612 class _ReposGroupForm(formencode.Schema):
613 allow_extra_fields = True
613 allow_extra_fields = True
614 filter_extra_fields = False
614 filter_extra_fields = False
615
615
616 group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
616 group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
617 SlugifyName())
617 SlugifyName())
618 group_description = UnicodeString(strip=True, min=1,
618 group_description = UnicodeString(strip=True, min=1,
619 not_empty=True)
619 not_empty=True)
620 group_parent_id = OneOf(available_groups, hideList=False,
620 group_parent_id = OneOf(available_groups, hideList=False,
621 testValueList=True,
621 testValueList=True,
622 if_missing=None, not_empty=False)
622 if_missing=None, not_empty=False)
623
623
624 chained_validators = [ValidReposGroup(edit, old_data), ValidPerms('group')]
624 chained_validators = [ValidReposGroup(edit, old_data), ValidPerms('group')]
625
625
626 return _ReposGroupForm
626 return _ReposGroupForm
627
627
628
628
629 def RegisterForm(edit=False, old_data={}):
629 def RegisterForm(edit=False, old_data={}):
630 class _RegisterForm(formencode.Schema):
630 class _RegisterForm(formencode.Schema):
631 allow_extra_fields = True
631 allow_extra_fields = True
632 filter_extra_fields = True
632 filter_extra_fields = True
633 username = All(ValidUsername(edit, old_data),
633 username = All(ValidUsername(edit, old_data),
634 UnicodeString(strip=True, min=1, not_empty=True))
634 UnicodeString(strip=True, min=1, not_empty=True))
635 password = All(UnicodeString(strip=False, min=6, not_empty=True))
635 password = All(UnicodeString(strip=False, min=6, not_empty=True))
636 password_confirmation = All(UnicodeString(strip=False, min=6, not_empty=True))
636 password_confirmation = All(UnicodeString(strip=False, min=6, not_empty=True))
637 active = StringBoolean(if_missing=False)
637 active = StringBoolean(if_missing=False)
638 name = UnicodeString(strip=True, min=1, not_empty=False)
638 name = UnicodeString(strip=True, min=1, not_empty=False)
639 lastname = UnicodeString(strip=True, min=1, not_empty=False)
639 lastname = UnicodeString(strip=True, min=1, not_empty=False)
640 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
640 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
641
641
642 chained_validators = [ValidPasswordsMatch, ValidPassword]
642 chained_validators = [ValidPasswordsMatch, ValidPassword]
643
643
644 return _RegisterForm
644 return _RegisterForm
645
645
646
646
647 def PasswordResetForm():
647 def PasswordResetForm():
648 class _PasswordResetForm(formencode.Schema):
648 class _PasswordResetForm(formencode.Schema):
649 allow_extra_fields = True
649 allow_extra_fields = True
650 filter_extra_fields = True
650 filter_extra_fields = True
651 email = All(ValidSystemEmail(), Email(not_empty=True))
651 email = All(ValidSystemEmail(), Email(not_empty=True))
652 return _PasswordResetForm
652 return _PasswordResetForm
653
653
654
654
655 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
655 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
656 repo_groups=[]):
656 repo_groups=[], landing_revs=[]):
657 class _RepoForm(formencode.Schema):
657 class _RepoForm(formencode.Schema):
658 allow_extra_fields = True
658 allow_extra_fields = True
659 filter_extra_fields = False
659 filter_extra_fields = False
660 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
660 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
661 SlugifyName())
661 SlugifyName())
662 clone_uri = All(UnicodeString(strip=True, min=1, not_empty=False))
662 clone_uri = All(UnicodeString(strip=True, min=1, not_empty=False))
663 repo_group = OneOf(repo_groups, hideList=True)
663 repo_group = OneOf(repo_groups, hideList=True)
664 repo_type = OneOf(supported_backends)
664 repo_type = OneOf(supported_backends)
665 description = UnicodeString(strip=True, min=1, not_empty=False)
665 description = UnicodeString(strip=True, min=1, not_empty=False)
666 private = StringBoolean(if_missing=False)
666 private = StringBoolean(if_missing=False)
667 enable_statistics = StringBoolean(if_missing=False)
667 enable_statistics = StringBoolean(if_missing=False)
668 enable_downloads = StringBoolean(if_missing=False)
668 enable_downloads = StringBoolean(if_missing=False)
669 landing_rev = UnicodeString(strip=True, min=1, not_empty=True)
669 landing_rev = OneOf(landing_revs, hideList=True)
670
670
671 if edit:
671 if edit:
672 #this is repo owner
672 #this is repo owner
673 user = All(UnicodeString(not_empty=True), ValidRepoUser)
673 user = All(UnicodeString(not_empty=True), ValidRepoUser)
674
674
675 chained_validators = [ValidCloneUri()(),
675 chained_validators = [ValidCloneUri()(),
676 ValidRepoName(edit, old_data),
676 ValidRepoName(edit, old_data),
677 ValidPerms()]
677 ValidPerms()]
678 return _RepoForm
678 return _RepoForm
679
679
680
680
681 def RepoForkForm(edit=False, old_data={},
681 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
682 supported_backends=BACKENDS.keys(), repo_groups=[]):
682 repo_groups=[]):
683 class _RepoForkForm(formencode.Schema):
683 class _RepoForkForm(formencode.Schema):
684 allow_extra_fields = True
684 allow_extra_fields = True
685 filter_extra_fields = False
685 filter_extra_fields = False
686 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
686 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
687 SlugifyName())
687 SlugifyName())
688 repo_group = OneOf(repo_groups, hideList=True)
688 repo_group = OneOf(repo_groups, hideList=True)
689 repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
689 repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
690 description = UnicodeString(strip=True, min=1, not_empty=True)
690 description = UnicodeString(strip=True, min=1, not_empty=True)
691 private = StringBoolean(if_missing=False)
691 private = StringBoolean(if_missing=False)
692 copy_permissions = StringBoolean(if_missing=False)
692 copy_permissions = StringBoolean(if_missing=False)
693 update_after_clone = StringBoolean(if_missing=False)
693 update_after_clone = StringBoolean(if_missing=False)
694 fork_parent_id = UnicodeString()
694 fork_parent_id = UnicodeString()
695 chained_validators = [ValidForkName(edit, old_data)]
695 chained_validators = [ValidForkName(edit, old_data)]
696
696
697 return _RepoForkForm
697 return _RepoForkForm
698
698
699
699
700 def RepoSettingsForm(edit=False, old_data={},
700 def RepoSettingsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
701 supported_backends=BACKENDS.keys(), repo_groups=[]):
701 repo_groups=[], landing_revs=[]):
702 class _RepoForm(formencode.Schema):
702 class _RepoForm(formencode.Schema):
703 allow_extra_fields = True
703 allow_extra_fields = True
704 filter_extra_fields = False
704 filter_extra_fields = False
705 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
705 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
706 SlugifyName())
706 SlugifyName())
707 description = UnicodeString(strip=True, min=1, not_empty=True)
707 description = UnicodeString(strip=True, min=1, not_empty=True)
708 repo_group = OneOf(repo_groups, hideList=True)
708 repo_group = OneOf(repo_groups, hideList=True)
709 private = StringBoolean(if_missing=False)
709 private = StringBoolean(if_missing=False)
710 landing_rev = UnicodeString(strip=True, min=1, not_empty=True)
710 landing_rev = OneOf(landing_revs, hideList=True)
711 chained_validators = [ValidRepoName(edit, old_data), ValidPerms(),
711 chained_validators = [ValidRepoName(edit, old_data), ValidPerms(),
712 ValidSettings]
712 ValidSettings]
713 return _RepoForm
713 return _RepoForm
714
714
715
715
716 def ApplicationSettingsForm():
716 def ApplicationSettingsForm():
717 class _ApplicationSettingsForm(formencode.Schema):
717 class _ApplicationSettingsForm(formencode.Schema):
718 allow_extra_fields = True
718 allow_extra_fields = True
719 filter_extra_fields = False
719 filter_extra_fields = False
720 rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
720 rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
721 rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
721 rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
722 rhodecode_ga_code = UnicodeString(strip=True, min=1, not_empty=False)
722 rhodecode_ga_code = UnicodeString(strip=True, min=1, not_empty=False)
723
723
724 return _ApplicationSettingsForm
724 return _ApplicationSettingsForm
725
725
726
726
727 def ApplicationUiSettingsForm():
727 def ApplicationUiSettingsForm():
728 class _ApplicationUiSettingsForm(formencode.Schema):
728 class _ApplicationUiSettingsForm(formencode.Schema):
729 allow_extra_fields = True
729 allow_extra_fields = True
730 filter_extra_fields = False
730 filter_extra_fields = False
731 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
731 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
732 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
732 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
733 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
733 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
734 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
734 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
735 hooks_changegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
735 hooks_changegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
736 hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
736 hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
737
737
738 return _ApplicationUiSettingsForm
738 return _ApplicationUiSettingsForm
739
739
740
740
741 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
741 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
742 class _DefaultPermissionsForm(formencode.Schema):
742 class _DefaultPermissionsForm(formencode.Schema):
743 allow_extra_fields = True
743 allow_extra_fields = True
744 filter_extra_fields = True
744 filter_extra_fields = True
745 overwrite_default = StringBoolean(if_missing=False)
745 overwrite_default = StringBoolean(if_missing=False)
746 anonymous = OneOf(['True', 'False'], if_missing=False)
746 anonymous = OneOf(['True', 'False'], if_missing=False)
747 default_perm = OneOf(perms_choices)
747 default_perm = OneOf(perms_choices)
748 default_register = OneOf(register_choices)
748 default_register = OneOf(register_choices)
749 default_create = OneOf(create_choices)
749 default_create = OneOf(create_choices)
750
750
751 return _DefaultPermissionsForm
751 return _DefaultPermissionsForm
752
752
753
753
754 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices, tls_kind_choices):
754 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices, tls_kind_choices):
755 class _LdapSettingsForm(formencode.Schema):
755 class _LdapSettingsForm(formencode.Schema):
756 allow_extra_fields = True
756 allow_extra_fields = True
757 filter_extra_fields = True
757 filter_extra_fields = True
758 #pre_validators = [LdapLibValidator]
758 #pre_validators = [LdapLibValidator]
759 ldap_active = StringBoolean(if_missing=False)
759 ldap_active = StringBoolean(if_missing=False)
760 ldap_host = UnicodeString(strip=True,)
760 ldap_host = UnicodeString(strip=True,)
761 ldap_port = Number(strip=True,)
761 ldap_port = Number(strip=True,)
762 ldap_tls_kind = OneOf(tls_kind_choices)
762 ldap_tls_kind = OneOf(tls_kind_choices)
763 ldap_tls_reqcert = OneOf(tls_reqcert_choices)
763 ldap_tls_reqcert = OneOf(tls_reqcert_choices)
764 ldap_dn_user = UnicodeString(strip=True,)
764 ldap_dn_user = UnicodeString(strip=True,)
765 ldap_dn_pass = UnicodeString(strip=True,)
765 ldap_dn_pass = UnicodeString(strip=True,)
766 ldap_base_dn = UnicodeString(strip=True,)
766 ldap_base_dn = UnicodeString(strip=True,)
767 ldap_filter = UnicodeString(strip=True,)
767 ldap_filter = UnicodeString(strip=True,)
768 ldap_search_scope = OneOf(search_scope_choices)
768 ldap_search_scope = OneOf(search_scope_choices)
769 ldap_attr_login = All(AttrLoginValidator, UnicodeString(strip=True,))
769 ldap_attr_login = All(AttrLoginValidator, UnicodeString(strip=True,))
770 ldap_attr_firstname = UnicodeString(strip=True,)
770 ldap_attr_firstname = UnicodeString(strip=True,)
771 ldap_attr_lastname = UnicodeString(strip=True,)
771 ldap_attr_lastname = UnicodeString(strip=True,)
772 ldap_attr_email = UnicodeString(strip=True,)
772 ldap_attr_email = UnicodeString(strip=True,)
773
773
774 return _LdapSettingsForm
774 return _LdapSettingsForm
@@ -1,505 +1,512
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.scm
3 rhodecode.model.scm
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Scm model for RhodeCode
6 Scm model for RhodeCode
7
7
8 :created_on: Apr 9, 2010
8 :created_on: Apr 9, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 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 time
26 import time
27 import traceback
27 import traceback
28 import logging
28 import logging
29 import cStringIO
29 import cStringIO
30
30
31 from sqlalchemy import func
31 from sqlalchemy import func
32 from pylons.i18n.translation import _
32 from pylons.i18n.translation import _
33
33
34 from rhodecode.lib.vcs import get_backend
34 from rhodecode.lib.vcs import get_backend
35 from rhodecode.lib.vcs.exceptions import RepositoryError
35 from rhodecode.lib.vcs.exceptions import RepositoryError
36 from rhodecode.lib.vcs.utils.lazy import LazyProperty
36 from rhodecode.lib.vcs.utils.lazy import LazyProperty
37 from rhodecode.lib.vcs.nodes import FileNode
37 from rhodecode.lib.vcs.nodes import FileNode
38
38
39 from rhodecode import BACKENDS
39 from rhodecode import BACKENDS
40 from rhodecode.lib import helpers as h
40 from rhodecode.lib import helpers as h
41 from rhodecode.lib.utils2 import safe_str, safe_unicode
41 from rhodecode.lib.utils2 import safe_str, safe_unicode
42 from rhodecode.lib.auth import HasRepoPermissionAny, HasReposGroupPermissionAny
42 from rhodecode.lib.auth import HasRepoPermissionAny, HasReposGroupPermissionAny
43 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
43 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
44 action_logger, EmptyChangeset, REMOVED_REPO_PAT
44 action_logger, EmptyChangeset, REMOVED_REPO_PAT
45 from rhodecode.model import BaseModel
45 from rhodecode.model import BaseModel
46 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
46 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
47 UserFollowing, UserLog, User, RepoGroup
47 UserFollowing, UserLog, User, RepoGroup
48
48
49 log = logging.getLogger(__name__)
49 log = logging.getLogger(__name__)
50
50
51
51
52 class UserTemp(object):
52 class UserTemp(object):
53 def __init__(self, user_id):
53 def __init__(self, user_id):
54 self.user_id = user_id
54 self.user_id = user_id
55
55
56 def __repr__(self):
56 def __repr__(self):
57 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
57 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
58
58
59
59
60 class RepoTemp(object):
60 class RepoTemp(object):
61 def __init__(self, repo_id):
61 def __init__(self, repo_id):
62 self.repo_id = repo_id
62 self.repo_id = repo_id
63
63
64 def __repr__(self):
64 def __repr__(self):
65 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
65 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
66
66
67
67
68 class CachedRepoList(object):
68 class CachedRepoList(object):
69
69
70 def __init__(self, db_repo_list, repos_path, order_by=None):
70 def __init__(self, db_repo_list, repos_path, order_by=None):
71 self.db_repo_list = db_repo_list
71 self.db_repo_list = db_repo_list
72 self.repos_path = repos_path
72 self.repos_path = repos_path
73 self.order_by = order_by
73 self.order_by = order_by
74 self.reversed = (order_by or '').startswith('-')
74 self.reversed = (order_by or '').startswith('-')
75
75
76 def __len__(self):
76 def __len__(self):
77 return len(self.db_repo_list)
77 return len(self.db_repo_list)
78
78
79 def __repr__(self):
79 def __repr__(self):
80 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
80 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
81
81
82 def __iter__(self):
82 def __iter__(self):
83 # pre-propagated cache_map to save executing select statements
83 # pre-propagated cache_map to save executing select statements
84 # for each repo
84 # for each repo
85 cache_map = CacheInvalidation.get_cache_map()
85 cache_map = CacheInvalidation.get_cache_map()
86
86
87 for dbr in self.db_repo_list:
87 for dbr in self.db_repo_list:
88 scmr = dbr.scm_instance_cached(cache_map)
88 scmr = dbr.scm_instance_cached(cache_map)
89 # check permission at this level
89 # check permission at this level
90 if not HasRepoPermissionAny(
90 if not HasRepoPermissionAny(
91 'repository.read', 'repository.write', 'repository.admin'
91 'repository.read', 'repository.write', 'repository.admin'
92 )(dbr.repo_name, 'get repo check'):
92 )(dbr.repo_name, 'get repo check'):
93 continue
93 continue
94
94
95 if scmr is None:
95 if scmr is None:
96 log.error(
96 log.error(
97 '%s this repository is present in database but it '
97 '%s this repository is present in database but it '
98 'cannot be created as an scm instance' % dbr.repo_name
98 'cannot be created as an scm instance' % dbr.repo_name
99 )
99 )
100 continue
100 continue
101
101
102 last_change = scmr.last_change
102 last_change = scmr.last_change
103 tip = h.get_changeset_safe(scmr, 'tip')
103 tip = h.get_changeset_safe(scmr, 'tip')
104
104
105 tmp_d = {}
105 tmp_d = {}
106 tmp_d['name'] = dbr.repo_name
106 tmp_d['name'] = dbr.repo_name
107 tmp_d['name_sort'] = tmp_d['name'].lower()
107 tmp_d['name_sort'] = tmp_d['name'].lower()
108 tmp_d['description'] = dbr.description
108 tmp_d['description'] = dbr.description
109 tmp_d['description_sort'] = tmp_d['description']
109 tmp_d['description_sort'] = tmp_d['description']
110 tmp_d['last_change'] = last_change
110 tmp_d['last_change'] = last_change
111 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
111 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
112 tmp_d['tip'] = tip.raw_id
112 tmp_d['tip'] = tip.raw_id
113 tmp_d['tip_sort'] = tip.revision
113 tmp_d['tip_sort'] = tip.revision
114 tmp_d['rev'] = tip.revision
114 tmp_d['rev'] = tip.revision
115 tmp_d['contact'] = dbr.user.full_contact
115 tmp_d['contact'] = dbr.user.full_contact
116 tmp_d['contact_sort'] = tmp_d['contact']
116 tmp_d['contact_sort'] = tmp_d['contact']
117 tmp_d['owner_sort'] = tmp_d['contact']
117 tmp_d['owner_sort'] = tmp_d['contact']
118 tmp_d['repo_archives'] = list(scmr._get_archives())
118 tmp_d['repo_archives'] = list(scmr._get_archives())
119 tmp_d['last_msg'] = tip.message
119 tmp_d['last_msg'] = tip.message
120 tmp_d['author'] = tip.author
120 tmp_d['author'] = tip.author
121 tmp_d['dbrepo'] = dbr.get_dict()
121 tmp_d['dbrepo'] = dbr.get_dict()
122 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork else {}
122 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork else {}
123 yield tmp_d
123 yield tmp_d
124
124
125
125
126 class GroupList(object):
126 class GroupList(object):
127
127
128 def __init__(self, db_repo_group_list):
128 def __init__(self, db_repo_group_list):
129 self.db_repo_group_list = db_repo_group_list
129 self.db_repo_group_list = db_repo_group_list
130
130
131 def __len__(self):
131 def __len__(self):
132 return len(self.db_repo_group_list)
132 return len(self.db_repo_group_list)
133
133
134 def __repr__(self):
134 def __repr__(self):
135 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
135 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
136
136
137 def __iter__(self):
137 def __iter__(self):
138 for dbgr in self.db_repo_group_list:
138 for dbgr in self.db_repo_group_list:
139 # check permission at this level
139 # check permission at this level
140 if not HasReposGroupPermissionAny(
140 if not HasReposGroupPermissionAny(
141 'group.read', 'group.write', 'group.admin'
141 'group.read', 'group.write', 'group.admin'
142 )(dbgr.group_name, 'get group repo check'):
142 )(dbgr.group_name, 'get group repo check'):
143 continue
143 continue
144
144
145 yield dbgr
145 yield dbgr
146
146
147
147
148 class ScmModel(BaseModel):
148 class ScmModel(BaseModel):
149 """
149 """
150 Generic Scm Model
150 Generic Scm Model
151 """
151 """
152
152
153 def __get_repo(self, instance):
153 def __get_repo(self, instance):
154 cls = Repository
154 cls = Repository
155 if isinstance(instance, cls):
155 if isinstance(instance, cls):
156 return instance
156 return instance
157 elif isinstance(instance, int) or str(instance).isdigit():
157 elif isinstance(instance, int) or str(instance).isdigit():
158 return cls.get(instance)
158 return cls.get(instance)
159 elif isinstance(instance, basestring):
159 elif isinstance(instance, basestring):
160 return cls.get_by_repo_name(instance)
160 return cls.get_by_repo_name(instance)
161 elif instance:
161 elif instance:
162 raise Exception('given object must be int, basestr or Instance'
162 raise Exception('given object must be int, basestr or Instance'
163 ' of %s got %s' % (type(cls), type(instance)))
163 ' of %s got %s' % (type(cls), type(instance)))
164
164
165 @LazyProperty
165 @LazyProperty
166 def repos_path(self):
166 def repos_path(self):
167 """
167 """
168 Get's the repositories root path from database
168 Get's the repositories root path from database
169 """
169 """
170
170
171 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
171 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
172
172
173 return q.ui_value
173 return q.ui_value
174
174
175 def repo_scan(self, repos_path=None):
175 def repo_scan(self, repos_path=None):
176 """
176 """
177 Listing of repositories in given path. This path should not be a
177 Listing of repositories in given path. This path should not be a
178 repository itself. Return a dictionary of repository objects
178 repository itself. Return a dictionary of repository objects
179
179
180 :param repos_path: path to directory containing repositories
180 :param repos_path: path to directory containing repositories
181 """
181 """
182
182
183 if repos_path is None:
183 if repos_path is None:
184 repos_path = self.repos_path
184 repos_path = self.repos_path
185
185
186 log.info('scanning for repositories in %s' % repos_path)
186 log.info('scanning for repositories in %s' % repos_path)
187
187
188 baseui = make_ui('db')
188 baseui = make_ui('db')
189 repos = {}
189 repos = {}
190
190
191 for name, path in get_filesystem_repos(repos_path, recursive=True):
191 for name, path in get_filesystem_repos(repos_path, recursive=True):
192 # skip removed repos
192 # skip removed repos
193 if REMOVED_REPO_PAT.match(name):
193 if REMOVED_REPO_PAT.match(name):
194 continue
194 continue
195
195
196 # name need to be decomposed and put back together using the /
196 # name need to be decomposed and put back together using the /
197 # since this is internal storage separator for rhodecode
197 # since this is internal storage separator for rhodecode
198 name = Repository.url_sep().join(name.split(os.sep))
198 name = Repository.url_sep().join(name.split(os.sep))
199
199
200 try:
200 try:
201 if name in repos:
201 if name in repos:
202 raise RepositoryError('Duplicate repository name %s '
202 raise RepositoryError('Duplicate repository name %s '
203 'found in %s' % (name, path))
203 'found in %s' % (name, path))
204 else:
204 else:
205
205
206 klass = get_backend(path[0])
206 klass = get_backend(path[0])
207
207
208 if path[0] == 'hg' and path[0] in BACKENDS.keys():
208 if path[0] == 'hg' and path[0] in BACKENDS.keys():
209 repos[name] = klass(safe_str(path[1]), baseui=baseui)
209 repos[name] = klass(safe_str(path[1]), baseui=baseui)
210
210
211 if path[0] == 'git' and path[0] in BACKENDS.keys():
211 if path[0] == 'git' and path[0] in BACKENDS.keys():
212 repos[name] = klass(path[1])
212 repos[name] = klass(path[1])
213 except OSError:
213 except OSError:
214 continue
214 continue
215
215
216 return repos
216 return repos
217
217
218 def get_repos(self, all_repos=None, sort_key=None):
218 def get_repos(self, all_repos=None, sort_key=None):
219 """
219 """
220 Get all repos from db and for each repo create it's
220 Get all repos from db and for each repo create it's
221 backend instance and fill that backed with information from database
221 backend instance and fill that backed with information from database
222
222
223 :param all_repos: list of repository names as strings
223 :param all_repos: list of repository names as strings
224 give specific repositories list, good for filtering
224 give specific repositories list, good for filtering
225 """
225 """
226 if all_repos is None:
226 if all_repos is None:
227 all_repos = self.sa.query(Repository)\
227 all_repos = self.sa.query(Repository)\
228 .filter(Repository.group_id == None)\
228 .filter(Repository.group_id == None)\
229 .order_by(func.lower(Repository.repo_name)).all()
229 .order_by(func.lower(Repository.repo_name)).all()
230
230
231 repo_iter = CachedRepoList(all_repos, repos_path=self.repos_path,
231 repo_iter = CachedRepoList(all_repos, repos_path=self.repos_path,
232 order_by=sort_key)
232 order_by=sort_key)
233
233
234 return repo_iter
234 return repo_iter
235
235
236 def get_repos_groups(self, all_groups=None):
236 def get_repos_groups(self, all_groups=None):
237 if all_groups is None:
237 if all_groups is None:
238 all_groups = RepoGroup.query()\
238 all_groups = RepoGroup.query()\
239 .filter(RepoGroup.group_parent_id == None).all()
239 .filter(RepoGroup.group_parent_id == None).all()
240 group_iter = GroupList(all_groups)
240 group_iter = GroupList(all_groups)
241
241
242 return group_iter
242 return group_iter
243
243
244 def mark_for_invalidation(self, repo_name):
244 def mark_for_invalidation(self, repo_name):
245 """
245 """
246 Puts cache invalidation task into db for
246 Puts cache invalidation task into db for
247 further global cache invalidation
247 further global cache invalidation
248
248
249 :param repo_name: this repo that should invalidation take place
249 :param repo_name: this repo that should invalidation take place
250 """
250 """
251 CacheInvalidation.set_invalidate(repo_name)
251 CacheInvalidation.set_invalidate(repo_name)
252
252
253 def toggle_following_repo(self, follow_repo_id, user_id):
253 def toggle_following_repo(self, follow_repo_id, user_id):
254
254
255 f = self.sa.query(UserFollowing)\
255 f = self.sa.query(UserFollowing)\
256 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
256 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
257 .filter(UserFollowing.user_id == user_id).scalar()
257 .filter(UserFollowing.user_id == user_id).scalar()
258
258
259 if f is not None:
259 if f is not None:
260 try:
260 try:
261 self.sa.delete(f)
261 self.sa.delete(f)
262 action_logger(UserTemp(user_id),
262 action_logger(UserTemp(user_id),
263 'stopped_following_repo',
263 'stopped_following_repo',
264 RepoTemp(follow_repo_id))
264 RepoTemp(follow_repo_id))
265 return
265 return
266 except:
266 except:
267 log.error(traceback.format_exc())
267 log.error(traceback.format_exc())
268 raise
268 raise
269
269
270 try:
270 try:
271 f = UserFollowing()
271 f = UserFollowing()
272 f.user_id = user_id
272 f.user_id = user_id
273 f.follows_repo_id = follow_repo_id
273 f.follows_repo_id = follow_repo_id
274 self.sa.add(f)
274 self.sa.add(f)
275
275
276 action_logger(UserTemp(user_id),
276 action_logger(UserTemp(user_id),
277 'started_following_repo',
277 'started_following_repo',
278 RepoTemp(follow_repo_id))
278 RepoTemp(follow_repo_id))
279 except:
279 except:
280 log.error(traceback.format_exc())
280 log.error(traceback.format_exc())
281 raise
281 raise
282
282
283 def toggle_following_user(self, follow_user_id, user_id):
283 def toggle_following_user(self, follow_user_id, user_id):
284 f = self.sa.query(UserFollowing)\
284 f = self.sa.query(UserFollowing)\
285 .filter(UserFollowing.follows_user_id == follow_user_id)\
285 .filter(UserFollowing.follows_user_id == follow_user_id)\
286 .filter(UserFollowing.user_id == user_id).scalar()
286 .filter(UserFollowing.user_id == user_id).scalar()
287
287
288 if f is not None:
288 if f is not None:
289 try:
289 try:
290 self.sa.delete(f)
290 self.sa.delete(f)
291 return
291 return
292 except:
292 except:
293 log.error(traceback.format_exc())
293 log.error(traceback.format_exc())
294 raise
294 raise
295
295
296 try:
296 try:
297 f = UserFollowing()
297 f = UserFollowing()
298 f.user_id = user_id
298 f.user_id = user_id
299 f.follows_user_id = follow_user_id
299 f.follows_user_id = follow_user_id
300 self.sa.add(f)
300 self.sa.add(f)
301 except:
301 except:
302 log.error(traceback.format_exc())
302 log.error(traceback.format_exc())
303 raise
303 raise
304
304
305 def is_following_repo(self, repo_name, user_id, cache=False):
305 def is_following_repo(self, repo_name, user_id, cache=False):
306 r = self.sa.query(Repository)\
306 r = self.sa.query(Repository)\
307 .filter(Repository.repo_name == repo_name).scalar()
307 .filter(Repository.repo_name == repo_name).scalar()
308
308
309 f = self.sa.query(UserFollowing)\
309 f = self.sa.query(UserFollowing)\
310 .filter(UserFollowing.follows_repository == r)\
310 .filter(UserFollowing.follows_repository == r)\
311 .filter(UserFollowing.user_id == user_id).scalar()
311 .filter(UserFollowing.user_id == user_id).scalar()
312
312
313 return f is not None
313 return f is not None
314
314
315 def is_following_user(self, username, user_id, cache=False):
315 def is_following_user(self, username, user_id, cache=False):
316 u = User.get_by_username(username)
316 u = User.get_by_username(username)
317
317
318 f = self.sa.query(UserFollowing)\
318 f = self.sa.query(UserFollowing)\
319 .filter(UserFollowing.follows_user == u)\
319 .filter(UserFollowing.follows_user == u)\
320 .filter(UserFollowing.user_id == user_id).scalar()
320 .filter(UserFollowing.user_id == user_id).scalar()
321
321
322 return f is not None
322 return f is not None
323
323
324 def get_followers(self, repo_id):
324 def get_followers(self, repo_id):
325 if not isinstance(repo_id, int):
325 if not isinstance(repo_id, int):
326 repo_id = getattr(Repository.get_by_repo_name(repo_id), 'repo_id')
326 repo_id = getattr(Repository.get_by_repo_name(repo_id), 'repo_id')
327
327
328 return self.sa.query(UserFollowing)\
328 return self.sa.query(UserFollowing)\
329 .filter(UserFollowing.follows_repo_id == repo_id).count()
329 .filter(UserFollowing.follows_repo_id == repo_id).count()
330
330
331 def get_forks(self, repo_id):
331 def get_forks(self, repo_id):
332 if not isinstance(repo_id, int):
332 if not isinstance(repo_id, int):
333 repo_id = getattr(Repository.get_by_repo_name(repo_id), 'repo_id')
333 repo_id = getattr(Repository.get_by_repo_name(repo_id), 'repo_id')
334
334
335 return self.sa.query(Repository)\
335 return self.sa.query(Repository)\
336 .filter(Repository.fork_id == repo_id).count()
336 .filter(Repository.fork_id == repo_id).count()
337
337
338 def mark_as_fork(self, repo, fork, user):
338 def mark_as_fork(self, repo, fork, user):
339 repo = self.__get_repo(repo)
339 repo = self.__get_repo(repo)
340 fork = self.__get_repo(fork)
340 fork = self.__get_repo(fork)
341 repo.fork = fork
341 repo.fork = fork
342 self.sa.add(repo)
342 self.sa.add(repo)
343 return repo
343 return repo
344
344
345 def pull_changes(self, repo_name, username):
345 def pull_changes(self, repo_name, username):
346 dbrepo = Repository.get_by_repo_name(repo_name)
346 dbrepo = Repository.get_by_repo_name(repo_name)
347 clone_uri = dbrepo.clone_uri
347 clone_uri = dbrepo.clone_uri
348 if not clone_uri:
348 if not clone_uri:
349 raise Exception("This repository doesn't have a clone uri")
349 raise Exception("This repository doesn't have a clone uri")
350
350
351 repo = dbrepo.scm_instance
351 repo = dbrepo.scm_instance
352 try:
352 try:
353 extras = {
353 extras = {
354 'ip': '',
354 'ip': '',
355 'username': username,
355 'username': username,
356 'action': 'push_remote',
356 'action': 'push_remote',
357 'repository': repo_name,
357 'repository': repo_name,
358 'scm': repo.alias,
358 'scm': repo.alias,
359 }
359 }
360
360
361 # inject ui extra param to log this action via push logger
361 # inject ui extra param to log this action via push logger
362 for k, v in extras.items():
362 for k, v in extras.items():
363 repo._repo.ui.setconfig('rhodecode_extras', k, v)
363 repo._repo.ui.setconfig('rhodecode_extras', k, v)
364 if repo.alias == 'git':
364 if repo.alias == 'git':
365 repo.fetch(clone_uri)
365 repo.fetch(clone_uri)
366 else:
366 else:
367 repo.pull(clone_uri)
367 repo.pull(clone_uri)
368 self.mark_for_invalidation(repo_name)
368 self.mark_for_invalidation(repo_name)
369 except:
369 except:
370 log.error(traceback.format_exc())
370 log.error(traceback.format_exc())
371 raise
371 raise
372
372
373 def commit_change(self, repo, repo_name, cs, user, author, message,
373 def commit_change(self, repo, repo_name, cs, user, author, message,
374 content, f_path):
374 content, f_path):
375
375
376 if repo.alias == 'hg':
376 if repo.alias == 'hg':
377 from rhodecode.lib.vcs.backends.hg import \
377 from rhodecode.lib.vcs.backends.hg import \
378 MercurialInMemoryChangeset as IMC
378 MercurialInMemoryChangeset as IMC
379 elif repo.alias == 'git':
379 elif repo.alias == 'git':
380 from rhodecode.lib.vcs.backends.git import \
380 from rhodecode.lib.vcs.backends.git import \
381 GitInMemoryChangeset as IMC
381 GitInMemoryChangeset as IMC
382
382
383 # decoding here will force that we have proper encoded values
383 # decoding here will force that we have proper encoded values
384 # in any other case this will throw exceptions and deny commit
384 # in any other case this will throw exceptions and deny commit
385 content = safe_str(content)
385 content = safe_str(content)
386 path = safe_str(f_path)
386 path = safe_str(f_path)
387 # message and author needs to be unicode
387 # message and author needs to be unicode
388 # proper backend should then translate that into required type
388 # proper backend should then translate that into required type
389 message = safe_unicode(message)
389 message = safe_unicode(message)
390 author = safe_unicode(author)
390 author = safe_unicode(author)
391 m = IMC(repo)
391 m = IMC(repo)
392 m.change(FileNode(path, content))
392 m.change(FileNode(path, content))
393 tip = m.commit(message=message,
393 tip = m.commit(message=message,
394 author=author,
394 author=author,
395 parents=[cs], branch=cs.branch)
395 parents=[cs], branch=cs.branch)
396
396
397 new_cs = tip.short_id
397 new_cs = tip.short_id
398 action = 'push_local:%s' % new_cs
398 action = 'push_local:%s' % new_cs
399
399
400 action_logger(user, action, repo_name)
400 action_logger(user, action, repo_name)
401
401
402 self.mark_for_invalidation(repo_name)
402 self.mark_for_invalidation(repo_name)
403
403
404 def create_node(self, repo, repo_name, cs, user, author, message, content,
404 def create_node(self, repo, repo_name, cs, user, author, message, content,
405 f_path):
405 f_path):
406 if repo.alias == 'hg':
406 if repo.alias == 'hg':
407 from rhodecode.lib.vcs.backends.hg import MercurialInMemoryChangeset as IMC
407 from rhodecode.lib.vcs.backends.hg import MercurialInMemoryChangeset as IMC
408 elif repo.alias == 'git':
408 elif repo.alias == 'git':
409 from rhodecode.lib.vcs.backends.git import GitInMemoryChangeset as IMC
409 from rhodecode.lib.vcs.backends.git import GitInMemoryChangeset as IMC
410 # decoding here will force that we have proper encoded values
410 # decoding here will force that we have proper encoded values
411 # in any other case this will throw exceptions and deny commit
411 # in any other case this will throw exceptions and deny commit
412
412
413 if isinstance(content, (basestring,)):
413 if isinstance(content, (basestring,)):
414 content = safe_str(content)
414 content = safe_str(content)
415 elif isinstance(content, (file, cStringIO.OutputType,)):
415 elif isinstance(content, (file, cStringIO.OutputType,)):
416 content = content.read()
416 content = content.read()
417 else:
417 else:
418 raise Exception('Content is of unrecognized type %s' % (
418 raise Exception('Content is of unrecognized type %s' % (
419 type(content)
419 type(content)
420 ))
420 ))
421
421
422 message = safe_unicode(message)
422 message = safe_unicode(message)
423 author = safe_unicode(author)
423 author = safe_unicode(author)
424 path = safe_str(f_path)
424 path = safe_str(f_path)
425 m = IMC(repo)
425 m = IMC(repo)
426
426
427 if isinstance(cs, EmptyChangeset):
427 if isinstance(cs, EmptyChangeset):
428 # EmptyChangeset means we we're editing empty repository
428 # EmptyChangeset means we we're editing empty repository
429 parents = None
429 parents = None
430 else:
430 else:
431 parents = [cs]
431 parents = [cs]
432
432
433 m.add(FileNode(path, content=content))
433 m.add(FileNode(path, content=content))
434 tip = m.commit(message=message,
434 tip = m.commit(message=message,
435 author=author,
435 author=author,
436 parents=parents, branch=cs.branch)
436 parents=parents, branch=cs.branch)
437 new_cs = tip.short_id
437 new_cs = tip.short_id
438 action = 'push_local:%s' % new_cs
438 action = 'push_local:%s' % new_cs
439
439
440 action_logger(user, action, repo_name)
440 action_logger(user, action, repo_name)
441
441
442 self.mark_for_invalidation(repo_name)
442 self.mark_for_invalidation(repo_name)
443
443
444 def get_nodes(self, repo_name, revision, root_path='/', flat=True):
444 def get_nodes(self, repo_name, revision, root_path='/', flat=True):
445 """
445 """
446 recursive walk in root dir and return a set of all path in that dir
446 recursive walk in root dir and return a set of all path in that dir
447 based on repository walk function
447 based on repository walk function
448
448
449 :param repo_name: name of repository
449 :param repo_name: name of repository
450 :param revision: revision for which to list nodes
450 :param revision: revision for which to list nodes
451 :param root_path: root path to list
451 :param root_path: root path to list
452 :param flat: return as a list, if False returns a dict with decription
452 :param flat: return as a list, if False returns a dict with decription
453
453
454 """
454 """
455 _files = list()
455 _files = list()
456 _dirs = list()
456 _dirs = list()
457 try:
457 try:
458 _repo = self.__get_repo(repo_name)
458 _repo = self.__get_repo(repo_name)
459 changeset = _repo.scm_instance.get_changeset(revision)
459 changeset = _repo.scm_instance.get_changeset(revision)
460 root_path = root_path.lstrip('/')
460 root_path = root_path.lstrip('/')
461 for topnode, dirs, files in changeset.walk(root_path):
461 for topnode, dirs, files in changeset.walk(root_path):
462 for f in files:
462 for f in files:
463 _files.append(f.path if flat else {"name": f.path,
463 _files.append(f.path if flat else {"name": f.path,
464 "type": "file"})
464 "type": "file"})
465 for d in dirs:
465 for d in dirs:
466 _dirs.append(d.path if flat else {"name": d.path,
466 _dirs.append(d.path if flat else {"name": d.path,
467 "type": "dir"})
467 "type": "dir"})
468 except RepositoryError:
468 except RepositoryError:
469 log.debug(traceback.format_exc())
469 log.debug(traceback.format_exc())
470 raise
470 raise
471
471
472 return _dirs, _files
472 return _dirs, _files
473
473
474 def get_unread_journal(self):
474 def get_unread_journal(self):
475 return self.sa.query(UserLog).count()
475 return self.sa.query(UserLog).count()
476
476
477 def get_repo_landing_revs(self, repo=None):
477 def get_repo_landing_revs(self, repo=None):
478 """
478 """
479 Generates select option with tags branches and bookmarks (for hg only)
479 Generates select option with tags branches and bookmarks (for hg only)
480 grouped by type
480 grouped by type
481
481
482 :param repo:
482 :param repo:
483 :type repo:
483 :type repo:
484 """
484 """
485
485 hist_l = []
486 hist_l = []
487 choices = []
486 repo = self.__get_repo(repo)
488 repo = self.__get_repo(repo)
487 hist_l.append(['tip', _('latest tip')])
489 hist_l.append(['tip', _('latest tip')])
490 choices.append('tip')
488 if not repo:
491 if not repo:
489 return hist_l
492 return choices, hist_l
490
493
491 repo = repo.scm_instance
494 repo = repo.scm_instance
495
492 branches_group = ([(k, k) for k, v in
496 branches_group = ([(k, k) for k, v in
493 repo.branches.iteritems()], _("Branches"))
497 repo.branches.iteritems()], _("Branches"))
494 hist_l.append(branches_group)
498 hist_l.append(branches_group)
499 choices.extend([x[0] for x in branches_group[0]])
495
500
496 if repo.alias == 'hg':
501 if repo.alias == 'hg':
497 bookmarks_group = ([(k, k) for k, v in
502 bookmarks_group = ([(k, k) for k, v in
498 repo.bookmarks.iteritems()], _("Bookmarks"))
503 repo.bookmarks.iteritems()], _("Bookmarks"))
499 hist_l.append(bookmarks_group)
504 hist_l.append(bookmarks_group)
505 choices.extend([x[0] for x in bookmarks_group[0]])
500
506
501 tags_group = ([(k, k) for k, v in
507 tags_group = ([(k, k) for k, v in
502 repo.tags.iteritems()], _("Tags"))
508 repo.tags.iteritems()], _("Tags"))
503 hist_l.append(tags_group)
509 hist_l.append(tags_group)
510 choices.extend([x[0] for x in tags_group[0]])
504
511
505 return hist_l
512 return choices, hist_l
General Comments 0
You need to be logged in to leave comments. Login now