##// END OF EJS Templates
Initial version of landing revisions ref #483...
marcink -
r2459:9492ab68 beta
parent child Browse files
Show More
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
@@ -1,434 +1,437 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.repos
3 rhodecode.controllers.admin.repos
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 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
74
74 def __load_data(self, repo_name=None):
75 def __load_data(self, repo_name=None):
75 """
76 """
76 Load defaults settings for edit, and update
77 Load defaults settings for edit, and update
77
78
78 :param repo_name:
79 :param repo_name:
79 """
80 """
80 self.__load_defaults()
81 self.__load_defaults()
81
82
82 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
83 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
83 repo = db_repo.scm_instance
84 repo = db_repo.scm_instance
84
85
85 if c.repo_info is None:
86 if c.repo_info is None:
86 h.flash(_('%s repository is not mapped to db perhaps'
87 h.flash(_('%s repository is not mapped to db perhaps'
87 ' it was created or renamed from the filesystem'
88 ' it was created or renamed from the filesystem'
88 ' please run the application again'
89 ' please run the application again'
89 ' in order to rescan repositories') % repo_name,
90 ' in order to rescan repositories') % repo_name,
90 category='error')
91 category='error')
91
92
92 return redirect(url('repos'))
93 return redirect(url('repos'))
93
94
95 c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info)
94 c.default_user_id = User.get_by_username('default').user_id
96 c.default_user_id = User.get_by_username('default').user_id
95 c.in_public_journal = UserFollowing.query()\
97 c.in_public_journal = UserFollowing.query()\
96 .filter(UserFollowing.user_id == c.default_user_id)\
98 .filter(UserFollowing.user_id == c.default_user_id)\
97 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
99 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
98
100
99 if c.repo_info.stats:
101 if c.repo_info.stats:
100 # this is on what revision we ended up so we add +1 for count
102 # this is on what revision we ended up so we add +1 for count
101 last_rev = c.repo_info.stats.stat_on_revision + 1
103 last_rev = c.repo_info.stats.stat_on_revision + 1
102 else:
104 else:
103 last_rev = 0
105 last_rev = 0
104 c.stats_revision = last_rev
106 c.stats_revision = last_rev
105
107
106 c.repo_last_rev = repo.count() if repo.revisions else 0
108 c.repo_last_rev = repo.count() if repo.revisions else 0
107
109
108 if last_rev == 0 or c.repo_last_rev == 0:
110 if last_rev == 0 or c.repo_last_rev == 0:
109 c.stats_percentage = 0
111 c.stats_percentage = 0
110 else:
112 else:
111 c.stats_percentage = '%.2f' % ((float((last_rev)) /
113 c.stats_percentage = '%.2f' % ((float((last_rev)) /
112 c.repo_last_rev) * 100)
114 c.repo_last_rev) * 100)
113
115
114 defaults = RepoModel()._get_defaults(repo_name)
116 defaults = RepoModel()._get_defaults(repo_name)
115
117
116 c.repos_list = [('', _('--REMOVE FORK--'))]
118 c.repos_list = [('', _('--REMOVE FORK--'))]
117 c.repos_list += [(x.repo_id, x.repo_name) for x in
119 c.repos_list += [(x.repo_id, x.repo_name) for x in
118 Repository.query().order_by(Repository.repo_name).all()]
120 Repository.query().order_by(Repository.repo_name).all()]
121
119 return defaults
122 return defaults
120
123
121 @HasPermissionAllDecorator('hg.admin')
124 @HasPermissionAllDecorator('hg.admin')
122 def index(self, format='html'):
125 def index(self, format='html'):
123 """GET /repos: All items in the collection"""
126 """GET /repos: All items in the collection"""
124 # url('repos')
127 # url('repos')
125
128
126 c.repos_list = ScmModel().get_repos(Repository.query()
129 c.repos_list = ScmModel().get_repos(Repository.query()
127 .order_by(Repository.repo_name)
130 .order_by(Repository.repo_name)
128 .all(), sort_key='name_sort')
131 .all(), sort_key='name_sort')
129 return render('admin/repos/repos.html')
132 return render('admin/repos/repos.html')
130
133
131 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
134 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
132 def create(self):
135 def create(self):
133 """
136 """
134 POST /repos: Create a new item"""
137 POST /repos: Create a new item"""
135 # url('repos')
138 # url('repos')
136
139
137 self.__load_defaults()
140 self.__load_defaults()
138 form_result = {}
141 form_result = {}
139 try:
142 try:
140 form_result = RepoForm(repo_groups=c.repo_groups_choices)()\
143 form_result = RepoForm(repo_groups=c.repo_groups_choices)()\
141 .to_python(dict(request.POST))
144 .to_python(dict(request.POST))
142 RepoModel().create(form_result, self.rhodecode_user)
145 RepoModel().create(form_result, self.rhodecode_user)
143 if form_result['clone_uri']:
146 if form_result['clone_uri']:
144 h.flash(_('created repository %s from %s') \
147 h.flash(_('created repository %s from %s') \
145 % (form_result['repo_name'], form_result['clone_uri']),
148 % (form_result['repo_name'], form_result['clone_uri']),
146 category='success')
149 category='success')
147 else:
150 else:
148 h.flash(_('created repository %s') % form_result['repo_name'],
151 h.flash(_('created repository %s') % form_result['repo_name'],
149 category='success')
152 category='success')
150
153
151 if request.POST.get('user_created'):
154 if request.POST.get('user_created'):
152 # created by regular non admin user
155 # created by regular non admin user
153 action_logger(self.rhodecode_user, 'user_created_repo',
156 action_logger(self.rhodecode_user, 'user_created_repo',
154 form_result['repo_name_full'], self.ip_addr,
157 form_result['repo_name_full'], self.ip_addr,
155 self.sa)
158 self.sa)
156 else:
159 else:
157 action_logger(self.rhodecode_user, 'admin_created_repo',
160 action_logger(self.rhodecode_user, 'admin_created_repo',
158 form_result['repo_name_full'], self.ip_addr,
161 form_result['repo_name_full'], self.ip_addr,
159 self.sa)
162 self.sa)
160 Session.commit()
163 Session.commit()
161 except formencode.Invalid, errors:
164 except formencode.Invalid, errors:
162
165
163 c.new_repo = errors.value['repo_name']
166 c.new_repo = errors.value['repo_name']
164
167
165 if request.POST.get('user_created'):
168 if request.POST.get('user_created'):
166 r = render('admin/repos/repo_add_create_repository.html')
169 r = render('admin/repos/repo_add_create_repository.html')
167 else:
170 else:
168 r = render('admin/repos/repo_add.html')
171 r = render('admin/repos/repo_add.html')
169
172
170 return htmlfill.render(
173 return htmlfill.render(
171 r,
174 r,
172 defaults=errors.value,
175 defaults=errors.value,
173 errors=errors.error_dict or {},
176 errors=errors.error_dict or {},
174 prefix_error=False,
177 prefix_error=False,
175 encoding="UTF-8")
178 encoding="UTF-8")
176
179
177 except Exception:
180 except Exception:
178 log.error(traceback.format_exc())
181 log.error(traceback.format_exc())
179 msg = _('error occurred during creation of repository %s') \
182 msg = _('error occurred during creation of repository %s') \
180 % form_result.get('repo_name')
183 % form_result.get('repo_name')
181 h.flash(msg, category='error')
184 h.flash(msg, category='error')
182 if request.POST.get('user_created'):
185 if request.POST.get('user_created'):
183 return redirect(url('home'))
186 return redirect(url('home'))
184 return redirect(url('repos'))
187 return redirect(url('repos'))
185
188
186 @HasPermissionAllDecorator('hg.admin')
189 @HasPermissionAllDecorator('hg.admin')
187 def new(self, format='html'):
190 def new(self, format='html'):
188 """GET /repos/new: Form to create a new item"""
191 """GET /repos/new: Form to create a new item"""
189 new_repo = request.GET.get('repo', '')
192 new_repo = request.GET.get('repo', '')
190 c.new_repo = repo_name_slug(new_repo)
193 c.new_repo = repo_name_slug(new_repo)
191 self.__load_defaults()
194 self.__load_defaults()
192 return render('admin/repos/repo_add.html')
195 return render('admin/repos/repo_add.html')
193
196
194 @HasPermissionAllDecorator('hg.admin')
197 @HasPermissionAllDecorator('hg.admin')
195 def update(self, repo_name):
198 def update(self, repo_name):
196 """
199 """
197 PUT /repos/repo_name: Update an existing item"""
200 PUT /repos/repo_name: Update an existing item"""
198 # Forms posted to this method should contain a hidden field:
201 # Forms posted to this method should contain a hidden field:
199 # <input type="hidden" name="_method" value="PUT" />
202 # <input type="hidden" name="_method" value="PUT" />
200 # Or using helpers:
203 # Or using helpers:
201 # h.form(url('repo', repo_name=ID),
204 # h.form(url('repo', repo_name=ID),
202 # method='put')
205 # method='put')
203 # url('repo', repo_name=ID)
206 # url('repo', repo_name=ID)
204 self.__load_defaults()
207 self.__load_defaults()
205 repo_model = RepoModel()
208 repo_model = RepoModel()
206 changed_name = repo_name
209 changed_name = repo_name
207 _form = RepoForm(edit=True, old_data={'repo_name': repo_name},
210 _form = RepoForm(edit=True, old_data={'repo_name': repo_name},
208 repo_groups=c.repo_groups_choices)()
211 repo_groups=c.repo_groups_choices)()
209 try:
212 try:
210 form_result = _form.to_python(dict(request.POST))
213 form_result = _form.to_python(dict(request.POST))
211 repo = repo_model.update(repo_name, form_result)
214 repo = repo_model.update(repo_name, form_result)
212 invalidate_cache('get_repo_cached_%s' % repo_name)
215 invalidate_cache('get_repo_cached_%s' % repo_name)
213 h.flash(_('Repository %s updated successfully' % repo_name),
216 h.flash(_('Repository %s updated successfully' % repo_name),
214 category='success')
217 category='success')
215 changed_name = repo.repo_name
218 changed_name = repo.repo_name
216 action_logger(self.rhodecode_user, 'admin_updated_repo',
219 action_logger(self.rhodecode_user, 'admin_updated_repo',
217 changed_name, self.ip_addr, self.sa)
220 changed_name, self.ip_addr, self.sa)
218 Session.commit()
221 Session.commit()
219 except formencode.Invalid, errors:
222 except formencode.Invalid, errors:
220 defaults = self.__load_data(repo_name)
223 defaults = self.__load_data(repo_name)
221 defaults.update(errors.value)
224 defaults.update(errors.value)
222 return htmlfill.render(
225 return htmlfill.render(
223 render('admin/repos/repo_edit.html'),
226 render('admin/repos/repo_edit.html'),
224 defaults=defaults,
227 defaults=defaults,
225 errors=errors.error_dict or {},
228 errors=errors.error_dict or {},
226 prefix_error=False,
229 prefix_error=False,
227 encoding="UTF-8")
230 encoding="UTF-8")
228
231
229 except Exception:
232 except Exception:
230 log.error(traceback.format_exc())
233 log.error(traceback.format_exc())
231 h.flash(_('error occurred during update of repository %s') \
234 h.flash(_('error occurred during update of repository %s') \
232 % repo_name, category='error')
235 % repo_name, category='error')
233 return redirect(url('edit_repo', repo_name=changed_name))
236 return redirect(url('edit_repo', repo_name=changed_name))
234
237
235 @HasPermissionAllDecorator('hg.admin')
238 @HasPermissionAllDecorator('hg.admin')
236 def delete(self, repo_name):
239 def delete(self, repo_name):
237 """
240 """
238 DELETE /repos/repo_name: Delete an existing item"""
241 DELETE /repos/repo_name: Delete an existing item"""
239 # Forms posted to this method should contain a hidden field:
242 # Forms posted to this method should contain a hidden field:
240 # <input type="hidden" name="_method" value="DELETE" />
243 # <input type="hidden" name="_method" value="DELETE" />
241 # Or using helpers:
244 # Or using helpers:
242 # h.form(url('repo', repo_name=ID),
245 # h.form(url('repo', repo_name=ID),
243 # method='delete')
246 # method='delete')
244 # url('repo', repo_name=ID)
247 # url('repo', repo_name=ID)
245
248
246 repo_model = RepoModel()
249 repo_model = RepoModel()
247 repo = repo_model.get_by_repo_name(repo_name)
250 repo = repo_model.get_by_repo_name(repo_name)
248 if not repo:
251 if not repo:
249 h.flash(_('%s repository is not mapped to db perhaps'
252 h.flash(_('%s repository is not mapped to db perhaps'
250 ' it was moved or renamed from the filesystem'
253 ' it was moved or renamed from the filesystem'
251 ' please run the application again'
254 ' please run the application again'
252 ' in order to rescan repositories') % repo_name,
255 ' in order to rescan repositories') % repo_name,
253 category='error')
256 category='error')
254
257
255 return redirect(url('repos'))
258 return redirect(url('repos'))
256 try:
259 try:
257 action_logger(self.rhodecode_user, 'admin_deleted_repo',
260 action_logger(self.rhodecode_user, 'admin_deleted_repo',
258 repo_name, self.ip_addr, self.sa)
261 repo_name, self.ip_addr, self.sa)
259 repo_model.delete(repo)
262 repo_model.delete(repo)
260 invalidate_cache('get_repo_cached_%s' % repo_name)
263 invalidate_cache('get_repo_cached_%s' % repo_name)
261 h.flash(_('deleted repository %s') % repo_name, category='success')
264 h.flash(_('deleted repository %s') % repo_name, category='success')
262 Session.commit()
265 Session.commit()
263 except IntegrityError, e:
266 except IntegrityError, e:
264 if e.message.find('repositories_fork_id_fkey') != -1:
267 if e.message.find('repositories_fork_id_fkey') != -1:
265 log.error(traceback.format_exc())
268 log.error(traceback.format_exc())
266 h.flash(_('Cannot delete %s it still contains attached '
269 h.flash(_('Cannot delete %s it still contains attached '
267 'forks') % repo_name,
270 'forks') % repo_name,
268 category='warning')
271 category='warning')
269 else:
272 else:
270 log.error(traceback.format_exc())
273 log.error(traceback.format_exc())
271 h.flash(_('An error occurred during '
274 h.flash(_('An error occurred during '
272 'deletion of %s') % repo_name,
275 'deletion of %s') % repo_name,
273 category='error')
276 category='error')
274
277
275 except Exception, e:
278 except Exception, e:
276 log.error(traceback.format_exc())
279 log.error(traceback.format_exc())
277 h.flash(_('An error occurred during deletion of %s') % repo_name,
280 h.flash(_('An error occurred during deletion of %s') % repo_name,
278 category='error')
281 category='error')
279
282
280 return redirect(url('repos'))
283 return redirect(url('repos'))
281
284
282 @HasRepoPermissionAllDecorator('repository.admin')
285 @HasRepoPermissionAllDecorator('repository.admin')
283 def delete_perm_user(self, repo_name):
286 def delete_perm_user(self, repo_name):
284 """
287 """
285 DELETE an existing repository permission user
288 DELETE an existing repository permission user
286
289
287 :param repo_name:
290 :param repo_name:
288 """
291 """
289 try:
292 try:
290 RepoModel().revoke_user_permission(repo=repo_name,
293 RepoModel().revoke_user_permission(repo=repo_name,
291 user=request.POST['user_id'])
294 user=request.POST['user_id'])
292 Session.commit()
295 Session.commit()
293 except Exception:
296 except Exception:
294 log.error(traceback.format_exc())
297 log.error(traceback.format_exc())
295 h.flash(_('An error occurred during deletion of repository user'),
298 h.flash(_('An error occurred during deletion of repository user'),
296 category='error')
299 category='error')
297 raise HTTPInternalServerError()
300 raise HTTPInternalServerError()
298
301
299 @HasRepoPermissionAllDecorator('repository.admin')
302 @HasRepoPermissionAllDecorator('repository.admin')
300 def delete_perm_users_group(self, repo_name):
303 def delete_perm_users_group(self, repo_name):
301 """
304 """
302 DELETE an existing repository permission users group
305 DELETE an existing repository permission users group
303
306
304 :param repo_name:
307 :param repo_name:
305 """
308 """
306
309
307 try:
310 try:
308 RepoModel().revoke_users_group_permission(
311 RepoModel().revoke_users_group_permission(
309 repo=repo_name, group_name=request.POST['users_group_id']
312 repo=repo_name, group_name=request.POST['users_group_id']
310 )
313 )
311 Session.commit()
314 Session.commit()
312 except Exception:
315 except Exception:
313 log.error(traceback.format_exc())
316 log.error(traceback.format_exc())
314 h.flash(_('An error occurred during deletion of repository'
317 h.flash(_('An error occurred during deletion of repository'
315 ' users groups'),
318 ' users groups'),
316 category='error')
319 category='error')
317 raise HTTPInternalServerError()
320 raise HTTPInternalServerError()
318
321
319 @HasPermissionAllDecorator('hg.admin')
322 @HasPermissionAllDecorator('hg.admin')
320 def repo_stats(self, repo_name):
323 def repo_stats(self, repo_name):
321 """
324 """
322 DELETE an existing repository statistics
325 DELETE an existing repository statistics
323
326
324 :param repo_name:
327 :param repo_name:
325 """
328 """
326
329
327 try:
330 try:
328 RepoModel().delete_stats(repo_name)
331 RepoModel().delete_stats(repo_name)
329 Session.commit()
332 Session.commit()
330 except Exception, e:
333 except Exception, e:
331 h.flash(_('An error occurred during deletion of repository stats'),
334 h.flash(_('An error occurred during deletion of repository stats'),
332 category='error')
335 category='error')
333 return redirect(url('edit_repo', repo_name=repo_name))
336 return redirect(url('edit_repo', repo_name=repo_name))
334
337
335 @HasPermissionAllDecorator('hg.admin')
338 @HasPermissionAllDecorator('hg.admin')
336 def repo_cache(self, repo_name):
339 def repo_cache(self, repo_name):
337 """
340 """
338 INVALIDATE existing repository cache
341 INVALIDATE existing repository cache
339
342
340 :param repo_name:
343 :param repo_name:
341 """
344 """
342
345
343 try:
346 try:
344 ScmModel().mark_for_invalidation(repo_name)
347 ScmModel().mark_for_invalidation(repo_name)
345 Session.commit()
348 Session.commit()
346 except Exception, e:
349 except Exception, e:
347 h.flash(_('An error occurred during cache invalidation'),
350 h.flash(_('An error occurred during cache invalidation'),
348 category='error')
351 category='error')
349 return redirect(url('edit_repo', repo_name=repo_name))
352 return redirect(url('edit_repo', repo_name=repo_name))
350
353
351 @HasPermissionAllDecorator('hg.admin')
354 @HasPermissionAllDecorator('hg.admin')
352 def repo_public_journal(self, repo_name):
355 def repo_public_journal(self, repo_name):
353 """
356 """
354 Set's this repository to be visible in public journal,
357 Set's this repository to be visible in public journal,
355 in other words assing default user to follow this repo
358 in other words assing default user to follow this repo
356
359
357 :param repo_name:
360 :param repo_name:
358 """
361 """
359
362
360 cur_token = request.POST.get('auth_token')
363 cur_token = request.POST.get('auth_token')
361 token = get_token()
364 token = get_token()
362 if cur_token == token:
365 if cur_token == token:
363 try:
366 try:
364 repo_id = Repository.get_by_repo_name(repo_name).repo_id
367 repo_id = Repository.get_by_repo_name(repo_name).repo_id
365 user_id = User.get_by_username('default').user_id
368 user_id = User.get_by_username('default').user_id
366 self.scm_model.toggle_following_repo(repo_id, user_id)
369 self.scm_model.toggle_following_repo(repo_id, user_id)
367 h.flash(_('Updated repository visibility in public journal'),
370 h.flash(_('Updated repository visibility in public journal'),
368 category='success')
371 category='success')
369 Session.commit()
372 Session.commit()
370 except:
373 except:
371 h.flash(_('An error occurred during setting this'
374 h.flash(_('An error occurred during setting this'
372 ' repository in public journal'),
375 ' repository in public journal'),
373 category='error')
376 category='error')
374
377
375 else:
378 else:
376 h.flash(_('Token mismatch'), category='error')
379 h.flash(_('Token mismatch'), category='error')
377 return redirect(url('edit_repo', repo_name=repo_name))
380 return redirect(url('edit_repo', repo_name=repo_name))
378
381
379 @HasPermissionAllDecorator('hg.admin')
382 @HasPermissionAllDecorator('hg.admin')
380 def repo_pull(self, repo_name):
383 def repo_pull(self, repo_name):
381 """
384 """
382 Runs task to update given repository with remote changes,
385 Runs task to update given repository with remote changes,
383 ie. make pull on remote location
386 ie. make pull on remote location
384
387
385 :param repo_name:
388 :param repo_name:
386 """
389 """
387 try:
390 try:
388 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
391 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
389 h.flash(_('Pulled from remote location'), category='success')
392 h.flash(_('Pulled from remote location'), category='success')
390 except Exception, e:
393 except Exception, e:
391 h.flash(_('An error occurred during pull from remote location'),
394 h.flash(_('An error occurred during pull from remote location'),
392 category='error')
395 category='error')
393
396
394 return redirect(url('edit_repo', repo_name=repo_name))
397 return redirect(url('edit_repo', repo_name=repo_name))
395
398
396 @HasPermissionAllDecorator('hg.admin')
399 @HasPermissionAllDecorator('hg.admin')
397 def repo_as_fork(self, repo_name):
400 def repo_as_fork(self, repo_name):
398 """
401 """
399 Mark given repository as a fork of another
402 Mark given repository as a fork of another
400
403
401 :param repo_name:
404 :param repo_name:
402 """
405 """
403 try:
406 try:
404 fork_id = request.POST.get('id_fork_of')
407 fork_id = request.POST.get('id_fork_of')
405 repo = ScmModel().mark_as_fork(repo_name, fork_id,
408 repo = ScmModel().mark_as_fork(repo_name, fork_id,
406 self.rhodecode_user.username)
409 self.rhodecode_user.username)
407 fork = repo.fork.repo_name if repo.fork else _('Nothing')
410 fork = repo.fork.repo_name if repo.fork else _('Nothing')
408 Session.commit()
411 Session.commit()
409 h.flash(_('Marked repo %s as fork of %s' % (repo_name,fork)),
412 h.flash(_('Marked repo %s as fork of %s' % (repo_name,fork)),
410 category='success')
413 category='success')
411 except Exception, e:
414 except Exception, e:
412 raise
415 raise
413 h.flash(_('An error occurred during this operation'),
416 h.flash(_('An error occurred during this operation'),
414 category='error')
417 category='error')
415
418
416 return redirect(url('edit_repo', repo_name=repo_name))
419 return redirect(url('edit_repo', repo_name=repo_name))
417
420
418 @HasPermissionAllDecorator('hg.admin')
421 @HasPermissionAllDecorator('hg.admin')
419 def show(self, repo_name, format='html'):
422 def show(self, repo_name, format='html'):
420 """GET /repos/repo_name: Show a specific item"""
423 """GET /repos/repo_name: Show a specific item"""
421 # url('repo', repo_name=ID)
424 # url('repo', repo_name=ID)
422
425
423 @HasPermissionAllDecorator('hg.admin')
426 @HasPermissionAllDecorator('hg.admin')
424 def edit(self, repo_name, format='html'):
427 def edit(self, repo_name, format='html'):
425 """GET /repos/repo_name/edit: Form to edit an existing item"""
428 """GET /repos/repo_name/edit: Form to edit an existing item"""
426 # url('edit_repo', repo_name=ID)
429 # url('edit_repo', repo_name=ID)
427 defaults = self.__load_data(repo_name)
430 defaults = self.__load_data(repo_name)
428
431
429 return htmlfill.render(
432 return htmlfill.render(
430 render('admin/repos/repo_edit.html'),
433 render('admin/repos/repo_edit.html'),
431 defaults=defaults,
434 defaults=defaults,
432 encoding="UTF-8",
435 encoding="UTF-8",
433 force_defaults=False
436 force_defaults=False
434 )
437 )
@@ -1,426 +1,427 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.settings
3 rhodecode.controllers.admin.settings
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 settings controller for rhodecode admin
6 settings controller for rhodecode admin
7
7
8 :created_on: Jul 14, 2010
8 :created_on: Jul 14, 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 import pkg_resources
29 import pkg_resources
30 import platform
30 import platform
31
31
32 from sqlalchemy import func
32 from sqlalchemy import func
33 from formencode import htmlfill
33 from formencode import htmlfill
34 from pylons import request, session, tmpl_context as c, url, config
34 from pylons import request, session, tmpl_context as c, url, config
35 from pylons.controllers.util import abort, redirect
35 from pylons.controllers.util import abort, redirect
36 from pylons.i18n.translation import _
36 from pylons.i18n.translation import _
37
37
38 from rhodecode.lib import helpers as h
38 from rhodecode.lib import helpers as h
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
40 HasPermissionAnyDecorator, NotAnonymous
40 HasPermissionAnyDecorator, NotAnonymous
41 from rhodecode.lib.base import BaseController, render
41 from rhodecode.lib.base import BaseController, render
42 from rhodecode.lib.celerylib import tasks, run_task
42 from rhodecode.lib.celerylib import tasks, run_task
43 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
43 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
44 set_rhodecode_config, repo_name_slug
44 set_rhodecode_config, repo_name_slug
45 from rhodecode.model.db import RhodeCodeUi, Repository, RepoGroup, \
45 from rhodecode.model.db import RhodeCodeUi, Repository, RepoGroup, \
46 RhodeCodeSetting
46 RhodeCodeSetting
47 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
47 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
48 ApplicationUiSettingsForm
48 ApplicationUiSettingsForm
49 from rhodecode.model.scm import ScmModel
49 from rhodecode.model.scm import ScmModel
50 from rhodecode.model.user import UserModel
50 from rhodecode.model.user import UserModel
51 from rhodecode.model.db import User
51 from rhodecode.model.db import User
52 from rhodecode.model.notification import EmailNotificationModel
52 from rhodecode.model.notification import EmailNotificationModel
53 from rhodecode.model.meta import Session
53 from rhodecode.model.meta import Session
54
54
55 log = logging.getLogger(__name__)
55 log = logging.getLogger(__name__)
56
56
57
57
58 class SettingsController(BaseController):
58 class SettingsController(BaseController):
59 """REST Controller styled on the Atom Publishing Protocol"""
59 """REST Controller styled on the Atom Publishing Protocol"""
60 # To properly map this controller, ensure your config/routing.py
60 # To properly map this controller, ensure your config/routing.py
61 # file has a resource setup:
61 # file has a resource setup:
62 # map.resource('setting', 'settings', controller='admin/settings',
62 # map.resource('setting', 'settings', controller='admin/settings',
63 # path_prefix='/admin', name_prefix='admin_')
63 # path_prefix='/admin', name_prefix='admin_')
64
64
65 @LoginRequired()
65 @LoginRequired()
66 def __before__(self):
66 def __before__(self):
67 c.admin_user = session.get('admin_user')
67 c.admin_user = session.get('admin_user')
68 c.admin_username = session.get('admin_username')
68 c.admin_username = session.get('admin_username')
69 c.modules = sorted([(p.project_name, p.version)
69 c.modules = sorted([(p.project_name, p.version)
70 for p in pkg_resources.working_set],
70 for p in pkg_resources.working_set],
71 key=lambda k: k[0].lower())
71 key=lambda k: k[0].lower())
72 c.py_version = platform.python_version()
72 c.py_version = platform.python_version()
73 c.platform = platform.platform()
73 c.platform = platform.platform()
74 super(SettingsController, self).__before__()
74 super(SettingsController, self).__before__()
75
75
76 @HasPermissionAllDecorator('hg.admin')
76 @HasPermissionAllDecorator('hg.admin')
77 def index(self, format='html'):
77 def index(self, format='html'):
78 """GET /admin/settings: All items in the collection"""
78 """GET /admin/settings: All items in the collection"""
79 # url('admin_settings')
79 # url('admin_settings')
80
80
81 defaults = RhodeCodeSetting.get_app_settings()
81 defaults = RhodeCodeSetting.get_app_settings()
82 defaults.update(self.get_hg_ui_settings())
82 defaults.update(self.get_hg_ui_settings())
83
83
84 return htmlfill.render(
84 return htmlfill.render(
85 render('admin/settings/settings.html'),
85 render('admin/settings/settings.html'),
86 defaults=defaults,
86 defaults=defaults,
87 encoding="UTF-8",
87 encoding="UTF-8",
88 force_defaults=False
88 force_defaults=False
89 )
89 )
90
90
91 @HasPermissionAllDecorator('hg.admin')
91 @HasPermissionAllDecorator('hg.admin')
92 def create(self):
92 def create(self):
93 """POST /admin/settings: Create a new item"""
93 """POST /admin/settings: Create a new item"""
94 # url('admin_settings')
94 # url('admin_settings')
95
95
96 @HasPermissionAllDecorator('hg.admin')
96 @HasPermissionAllDecorator('hg.admin')
97 def new(self, format='html'):
97 def new(self, format='html'):
98 """GET /admin/settings/new: Form to create a new item"""
98 """GET /admin/settings/new: Form to create a new item"""
99 # url('admin_new_setting')
99 # url('admin_new_setting')
100
100
101 @HasPermissionAllDecorator('hg.admin')
101 @HasPermissionAllDecorator('hg.admin')
102 def update(self, setting_id):
102 def update(self, setting_id):
103 """PUT /admin/settings/setting_id: Update an existing item"""
103 """PUT /admin/settings/setting_id: Update an existing item"""
104 # Forms posted to this method should contain a hidden field:
104 # Forms posted to this method should contain a hidden field:
105 # <input type="hidden" name="_method" value="PUT" />
105 # <input type="hidden" name="_method" value="PUT" />
106 # Or using helpers:
106 # Or using helpers:
107 # h.form(url('admin_setting', setting_id=ID),
107 # h.form(url('admin_setting', setting_id=ID),
108 # method='put')
108 # method='put')
109 # url('admin_setting', setting_id=ID)
109 # url('admin_setting', setting_id=ID)
110 if setting_id == 'mapping':
110 if setting_id == 'mapping':
111 rm_obsolete = request.POST.get('destroy', False)
111 rm_obsolete = request.POST.get('destroy', False)
112 log.debug('Rescanning directories with destroy=%s' % rm_obsolete)
112 log.debug('Rescanning directories with destroy=%s' % rm_obsolete)
113 initial = ScmModel().repo_scan()
113 initial = ScmModel().repo_scan()
114 log.debug('invalidating all repositories')
114 log.debug('invalidating all repositories')
115 for repo_name in initial.keys():
115 for repo_name in initial.keys():
116 invalidate_cache('get_repo_cached_%s' % repo_name)
116 invalidate_cache('get_repo_cached_%s' % repo_name)
117
117
118 added, removed = repo2db_mapper(initial, rm_obsolete)
118 added, removed = repo2db_mapper(initial, rm_obsolete)
119
119
120 h.flash(_('Repositories successfully'
120 h.flash(_('Repositories successfully'
121 ' rescanned added: %s,removed: %s') % (added, removed),
121 ' rescanned added: %s,removed: %s') % (added, removed),
122 category='success')
122 category='success')
123
123
124 if setting_id == 'whoosh':
124 if setting_id == 'whoosh':
125 repo_location = self.get_hg_ui_settings()['paths_root_path']
125 repo_location = self.get_hg_ui_settings()['paths_root_path']
126 full_index = request.POST.get('full_index', False)
126 full_index = request.POST.get('full_index', False)
127 run_task(tasks.whoosh_index, repo_location, full_index)
127 run_task(tasks.whoosh_index, repo_location, full_index)
128
128
129 h.flash(_('Whoosh reindex task scheduled'), category='success')
129 h.flash(_('Whoosh reindex task scheduled'), category='success')
130 if setting_id == 'global':
130 if setting_id == 'global':
131
131
132 application_form = ApplicationSettingsForm()()
132 application_form = ApplicationSettingsForm()()
133 try:
133 try:
134 form_result = application_form.to_python(dict(request.POST))
134 form_result = application_form.to_python(dict(request.POST))
135
135
136 try:
136 try:
137 hgsettings1 = RhodeCodeSetting.get_by_name('title')
137 hgsettings1 = RhodeCodeSetting.get_by_name('title')
138 hgsettings1.app_settings_value = \
138 hgsettings1.app_settings_value = \
139 form_result['rhodecode_title']
139 form_result['rhodecode_title']
140
140
141 hgsettings2 = RhodeCodeSetting.get_by_name('realm')
141 hgsettings2 = RhodeCodeSetting.get_by_name('realm')
142 hgsettings2.app_settings_value = \
142 hgsettings2.app_settings_value = \
143 form_result['rhodecode_realm']
143 form_result['rhodecode_realm']
144
144
145 hgsettings3 = RhodeCodeSetting.get_by_name('ga_code')
145 hgsettings3 = RhodeCodeSetting.get_by_name('ga_code')
146 hgsettings3.app_settings_value = \
146 hgsettings3.app_settings_value = \
147 form_result['rhodecode_ga_code']
147 form_result['rhodecode_ga_code']
148
148
149 self.sa.add(hgsettings1)
149 self.sa.add(hgsettings1)
150 self.sa.add(hgsettings2)
150 self.sa.add(hgsettings2)
151 self.sa.add(hgsettings3)
151 self.sa.add(hgsettings3)
152 self.sa.commit()
152 self.sa.commit()
153 set_rhodecode_config(config)
153 set_rhodecode_config(config)
154 h.flash(_('Updated application settings'),
154 h.flash(_('Updated application settings'),
155 category='success')
155 category='success')
156
156
157 except Exception:
157 except Exception:
158 log.error(traceback.format_exc())
158 log.error(traceback.format_exc())
159 h.flash(_('error occurred during updating '
159 h.flash(_('error occurred during updating '
160 'application settings'),
160 'application settings'),
161 category='error')
161 category='error')
162
162
163 self.sa.rollback()
163 self.sa.rollback()
164
164
165 except formencode.Invalid, errors:
165 except formencode.Invalid, errors:
166 return htmlfill.render(
166 return htmlfill.render(
167 render('admin/settings/settings.html'),
167 render('admin/settings/settings.html'),
168 defaults=errors.value,
168 defaults=errors.value,
169 errors=errors.error_dict or {},
169 errors=errors.error_dict or {},
170 prefix_error=False,
170 prefix_error=False,
171 encoding="UTF-8")
171 encoding="UTF-8")
172
172
173 if setting_id == 'mercurial':
173 if setting_id == 'mercurial':
174 application_form = ApplicationUiSettingsForm()()
174 application_form = ApplicationUiSettingsForm()()
175 try:
175 try:
176 form_result = application_form.to_python(dict(request.POST))
176 form_result = application_form.to_python(dict(request.POST))
177 # fix namespaces for hooks
177 # fix namespaces for hooks
178 _f = lambda s: s.replace('.', '_')
178 _f = lambda s: s.replace('.', '_')
179 try:
179 try:
180
180
181 hgsettings1 = self.sa.query(RhodeCodeUi)\
181 hgsettings1 = self.sa.query(RhodeCodeUi)\
182 .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
182 .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
183 hgsettings1.ui_value = form_result['web_push_ssl']
183 hgsettings1.ui_value = form_result['web_push_ssl']
184
184
185 hgsettings2 = self.sa.query(RhodeCodeUi)\
185 hgsettings2 = self.sa.query(RhodeCodeUi)\
186 .filter(RhodeCodeUi.ui_key == '/').one()
186 .filter(RhodeCodeUi.ui_key == '/').one()
187 hgsettings2.ui_value = form_result['paths_root_path']
187 hgsettings2.ui_value = form_result['paths_root_path']
188
188
189 #HOOKS
189 #HOOKS
190 hgsettings3 = self.sa.query(RhodeCodeUi)\
190 hgsettings3 = self.sa.query(RhodeCodeUi)\
191 .filter(RhodeCodeUi.ui_key == RhodeCodeUi.HOOK_UPDATE)\
191 .filter(RhodeCodeUi.ui_key == RhodeCodeUi.HOOK_UPDATE)\
192 .one()
192 .one()
193 hgsettings3.ui_active = bool(form_result[_f('hooks_%s' %
193 hgsettings3.ui_active = bool(form_result[_f('hooks_%s' %
194 RhodeCodeUi.HOOK_UPDATE)])
194 RhodeCodeUi.HOOK_UPDATE)])
195
195
196 hgsettings4 = self.sa.query(RhodeCodeUi)\
196 hgsettings4 = self.sa.query(RhodeCodeUi)\
197 .filter(RhodeCodeUi.ui_key == RhodeCodeUi.HOOK_REPO_SIZE)\
197 .filter(RhodeCodeUi.ui_key == RhodeCodeUi.HOOK_REPO_SIZE)\
198 .one()
198 .one()
199 hgsettings4.ui_active = bool(form_result[_f('hooks_%s' %
199 hgsettings4.ui_active = bool(form_result[_f('hooks_%s' %
200 RhodeCodeUi.HOOK_REPO_SIZE)])
200 RhodeCodeUi.HOOK_REPO_SIZE)])
201
201
202 hgsettings5 = self.sa.query(RhodeCodeUi)\
202 hgsettings5 = self.sa.query(RhodeCodeUi)\
203 .filter(RhodeCodeUi.ui_key == RhodeCodeUi.HOOK_PUSH)\
203 .filter(RhodeCodeUi.ui_key == RhodeCodeUi.HOOK_PUSH)\
204 .one()
204 .one()
205 hgsettings5.ui_active = bool(form_result[_f('hooks_%s' %
205 hgsettings5.ui_active = bool(form_result[_f('hooks_%s' %
206 RhodeCodeUi.HOOK_PUSH)])
206 RhodeCodeUi.HOOK_PUSH)])
207
207
208 hgsettings6 = self.sa.query(RhodeCodeUi)\
208 hgsettings6 = self.sa.query(RhodeCodeUi)\
209 .filter(RhodeCodeUi.ui_key == RhodeCodeUi.HOOK_PULL)\
209 .filter(RhodeCodeUi.ui_key == RhodeCodeUi.HOOK_PULL)\
210 .one()
210 .one()
211 hgsettings6.ui_active = bool(form_result[_f('hooks_%s' %
211 hgsettings6.ui_active = bool(form_result[_f('hooks_%s' %
212 RhodeCodeUi.HOOK_PULL)])
212 RhodeCodeUi.HOOK_PULL)])
213
213
214 self.sa.add(hgsettings1)
214 self.sa.add(hgsettings1)
215 self.sa.add(hgsettings2)
215 self.sa.add(hgsettings2)
216 self.sa.add(hgsettings3)
216 self.sa.add(hgsettings3)
217 self.sa.add(hgsettings4)
217 self.sa.add(hgsettings4)
218 self.sa.add(hgsettings5)
218 self.sa.add(hgsettings5)
219 self.sa.add(hgsettings6)
219 self.sa.add(hgsettings6)
220 self.sa.commit()
220 self.sa.commit()
221
221
222 h.flash(_('Updated mercurial settings'),
222 h.flash(_('Updated mercurial settings'),
223 category='success')
223 category='success')
224
224
225 except:
225 except:
226 log.error(traceback.format_exc())
226 log.error(traceback.format_exc())
227 h.flash(_('error occurred during updating '
227 h.flash(_('error occurred during updating '
228 'application settings'), category='error')
228 'application settings'), category='error')
229
229
230 self.sa.rollback()
230 self.sa.rollback()
231
231
232 except formencode.Invalid, errors:
232 except formencode.Invalid, errors:
233 return htmlfill.render(
233 return htmlfill.render(
234 render('admin/settings/settings.html'),
234 render('admin/settings/settings.html'),
235 defaults=errors.value,
235 defaults=errors.value,
236 errors=errors.error_dict or {},
236 errors=errors.error_dict or {},
237 prefix_error=False,
237 prefix_error=False,
238 encoding="UTF-8")
238 encoding="UTF-8")
239
239
240 if setting_id == 'hooks':
240 if setting_id == 'hooks':
241 ui_key = request.POST.get('new_hook_ui_key')
241 ui_key = request.POST.get('new_hook_ui_key')
242 ui_value = request.POST.get('new_hook_ui_value')
242 ui_value = request.POST.get('new_hook_ui_value')
243 try:
243 try:
244
244
245 if ui_value and ui_key:
245 if ui_value and ui_key:
246 RhodeCodeUi.create_or_update_hook(ui_key, ui_value)
246 RhodeCodeUi.create_or_update_hook(ui_key, ui_value)
247 h.flash(_('Added new hook'),
247 h.flash(_('Added new hook'),
248 category='success')
248 category='success')
249
249
250 # check for edits
250 # check for edits
251 update = False
251 update = False
252 _d = request.POST.dict_of_lists()
252 _d = request.POST.dict_of_lists()
253 for k, v in zip(_d.get('hook_ui_key', []),
253 for k, v in zip(_d.get('hook_ui_key', []),
254 _d.get('hook_ui_value_new', [])):
254 _d.get('hook_ui_value_new', [])):
255 RhodeCodeUi.create_or_update_hook(k, v)
255 RhodeCodeUi.create_or_update_hook(k, v)
256 update = True
256 update = True
257
257
258 if update:
258 if update:
259 h.flash(_('Updated hooks'), category='success')
259 h.flash(_('Updated hooks'), category='success')
260 self.sa.commit()
260 self.sa.commit()
261 except:
261 except:
262 log.error(traceback.format_exc())
262 log.error(traceback.format_exc())
263 h.flash(_('error occurred during hook creation'),
263 h.flash(_('error occurred during hook creation'),
264 category='error')
264 category='error')
265
265
266 return redirect(url('admin_edit_setting', setting_id='hooks'))
266 return redirect(url('admin_edit_setting', setting_id='hooks'))
267
267
268 if setting_id == 'email':
268 if setting_id == 'email':
269 test_email = request.POST.get('test_email')
269 test_email = request.POST.get('test_email')
270 test_email_subj = 'RhodeCode TestEmail'
270 test_email_subj = 'RhodeCode TestEmail'
271 test_email_body = 'RhodeCode Email test'
271 test_email_body = 'RhodeCode Email test'
272
272
273 test_email_html_body = EmailNotificationModel()\
273 test_email_html_body = EmailNotificationModel()\
274 .get_email_tmpl(EmailNotificationModel.TYPE_DEFAULT,
274 .get_email_tmpl(EmailNotificationModel.TYPE_DEFAULT,
275 body=test_email_body)
275 body=test_email_body)
276
276
277 recipients = [test_email] if [test_email] else None
277 recipients = [test_email] if [test_email] else None
278
278
279 run_task(tasks.send_email, recipients, test_email_subj,
279 run_task(tasks.send_email, recipients, test_email_subj,
280 test_email_body, test_email_html_body)
280 test_email_body, test_email_html_body)
281
281
282 h.flash(_('Email task created'), category='success')
282 h.flash(_('Email task created'), category='success')
283 return redirect(url('admin_settings'))
283 return redirect(url('admin_settings'))
284
284
285 @HasPermissionAllDecorator('hg.admin')
285 @HasPermissionAllDecorator('hg.admin')
286 def delete(self, setting_id):
286 def delete(self, setting_id):
287 """DELETE /admin/settings/setting_id: Delete an existing item"""
287 """DELETE /admin/settings/setting_id: Delete an existing item"""
288 # Forms posted to this method should contain a hidden field:
288 # Forms posted to this method should contain a hidden field:
289 # <input type="hidden" name="_method" value="DELETE" />
289 # <input type="hidden" name="_method" value="DELETE" />
290 # Or using helpers:
290 # Or using helpers:
291 # h.form(url('admin_setting', setting_id=ID),
291 # h.form(url('admin_setting', setting_id=ID),
292 # method='delete')
292 # method='delete')
293 # url('admin_setting', setting_id=ID)
293 # url('admin_setting', setting_id=ID)
294 if setting_id == 'hooks':
294 if setting_id == 'hooks':
295 hook_id = request.POST.get('hook_id')
295 hook_id = request.POST.get('hook_id')
296 RhodeCodeUi.delete(hook_id)
296 RhodeCodeUi.delete(hook_id)
297 self.sa.commit()
297 self.sa.commit()
298
298
299 @HasPermissionAllDecorator('hg.admin')
299 @HasPermissionAllDecorator('hg.admin')
300 def show(self, setting_id, format='html'):
300 def show(self, setting_id, format='html'):
301 """
301 """
302 GET /admin/settings/setting_id: Show a specific item"""
302 GET /admin/settings/setting_id: Show a specific item"""
303 # url('admin_setting', setting_id=ID)
303 # url('admin_setting', setting_id=ID)
304
304
305 @HasPermissionAllDecorator('hg.admin')
305 @HasPermissionAllDecorator('hg.admin')
306 def edit(self, setting_id, format='html'):
306 def edit(self, setting_id, format='html'):
307 """
307 """
308 GET /admin/settings/setting_id/edit: Form to
308 GET /admin/settings/setting_id/edit: Form to
309 edit an existing item"""
309 edit an existing item"""
310 # url('admin_edit_setting', setting_id=ID)
310 # url('admin_edit_setting', setting_id=ID)
311
311
312 c.hooks = RhodeCodeUi.get_builtin_hooks()
312 c.hooks = RhodeCodeUi.get_builtin_hooks()
313 c.custom_hooks = RhodeCodeUi.get_custom_hooks()
313 c.custom_hooks = RhodeCodeUi.get_custom_hooks()
314
314
315 return htmlfill.render(
315 return htmlfill.render(
316 render('admin/settings/hooks.html'),
316 render('admin/settings/hooks.html'),
317 defaults={},
317 defaults={},
318 encoding="UTF-8",
318 encoding="UTF-8",
319 force_defaults=False
319 force_defaults=False
320 )
320 )
321
321
322 @NotAnonymous()
322 @NotAnonymous()
323 def my_account(self):
323 def my_account(self):
324 """
324 """
325 GET /_admin/my_account Displays info about my account
325 GET /_admin/my_account Displays info about my account
326 """
326 """
327 # url('admin_settings_my_account')
327 # url('admin_settings_my_account')
328
328
329 c.user = User.get(self.rhodecode_user.user_id)
329 c.user = User.get(self.rhodecode_user.user_id)
330 all_repos = self.sa.query(Repository)\
330 all_repos = self.sa.query(Repository)\
331 .filter(Repository.user_id == c.user.user_id)\
331 .filter(Repository.user_id == c.user.user_id)\
332 .order_by(func.lower(Repository.repo_name)).all()
332 .order_by(func.lower(Repository.repo_name)).all()
333
333
334 c.user_repos = ScmModel().get_repos(all_repos)
334 c.user_repos = ScmModel().get_repos(all_repos)
335
335
336 if c.user.username == 'default':
336 if c.user.username == 'default':
337 h.flash(_("You can't edit this user since it's"
337 h.flash(_("You can't edit this user since it's"
338 " crucial for entire application"), category='warning')
338 " crucial for entire application"), category='warning')
339 return redirect(url('users'))
339 return redirect(url('users'))
340
340
341 defaults = c.user.get_dict()
341 defaults = c.user.get_dict()
342
342
343 c.form = htmlfill.render(
343 c.form = htmlfill.render(
344 render('admin/users/user_edit_my_account_form.html'),
344 render('admin/users/user_edit_my_account_form.html'),
345 defaults=defaults,
345 defaults=defaults,
346 encoding="UTF-8",
346 encoding="UTF-8",
347 force_defaults=False
347 force_defaults=False
348 )
348 )
349 return render('admin/users/user_edit_my_account.html')
349 return render('admin/users/user_edit_my_account.html')
350
350
351 def my_account_update(self):
351 def my_account_update(self):
352 """PUT /_admin/my_account_update: Update an existing item"""
352 """PUT /_admin/my_account_update: Update an existing item"""
353 # Forms posted to this method should contain a hidden field:
353 # Forms posted to this method should contain a hidden field:
354 # <input type="hidden" name="_method" value="PUT" />
354 # <input type="hidden" name="_method" value="PUT" />
355 # Or using helpers:
355 # Or using helpers:
356 # h.form(url('admin_settings_my_account_update'),
356 # h.form(url('admin_settings_my_account_update'),
357 # method='put')
357 # method='put')
358 # url('admin_settings_my_account_update', id=ID)
358 # url('admin_settings_my_account_update', id=ID)
359 user_model = UserModel()
359 user_model = UserModel()
360 uid = self.rhodecode_user.user_id
360 uid = self.rhodecode_user.user_id
361 _form = UserForm(edit=True,
361 _form = UserForm(edit=True,
362 old_data={'user_id': uid,
362 old_data={'user_id': uid,
363 'email': self.rhodecode_user.email})()
363 'email': self.rhodecode_user.email})()
364 form_result = {}
364 form_result = {}
365 try:
365 try:
366 form_result = _form.to_python(dict(request.POST))
366 form_result = _form.to_python(dict(request.POST))
367 user_model.update_my_account(uid, form_result)
367 user_model.update_my_account(uid, form_result)
368 h.flash(_('Your account was updated successfully'),
368 h.flash(_('Your account was updated successfully'),
369 category='success')
369 category='success')
370 Session.commit()
370 Session.commit()
371 except formencode.Invalid, errors:
371 except formencode.Invalid, errors:
372 c.user = User.get(self.rhodecode_user.user_id)
372 c.user = User.get(self.rhodecode_user.user_id)
373 all_repos = self.sa.query(Repository)\
373 all_repos = self.sa.query(Repository)\
374 .filter(Repository.user_id == c.user.user_id)\
374 .filter(Repository.user_id == c.user.user_id)\
375 .order_by(func.lower(Repository.repo_name))\
375 .order_by(func.lower(Repository.repo_name))\
376 .all()
376 .all()
377 c.user_repos = ScmModel().get_repos(all_repos)
377 c.user_repos = ScmModel().get_repos(all_repos)
378
378
379 c.form = htmlfill.render(
379 c.form = htmlfill.render(
380 render('admin/users/user_edit_my_account_form.html'),
380 render('admin/users/user_edit_my_account_form.html'),
381 defaults=errors.value,
381 defaults=errors.value,
382 errors=errors.error_dict or {},
382 errors=errors.error_dict or {},
383 prefix_error=False,
383 prefix_error=False,
384 encoding="UTF-8")
384 encoding="UTF-8")
385 return render('admin/users/user_edit_my_account.html')
385 return render('admin/users/user_edit_my_account.html')
386 except Exception:
386 except Exception:
387 log.error(traceback.format_exc())
387 log.error(traceback.format_exc())
388 h.flash(_('error occurred during update of user %s') \
388 h.flash(_('error occurred during update of user %s') \
389 % form_result.get('username'), category='error')
389 % form_result.get('username'), category='error')
390
390
391 return redirect(url('my_account'))
391 return redirect(url('my_account'))
392
392
393 @NotAnonymous()
393 @NotAnonymous()
394 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
394 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
395 def create_repository(self):
395 def create_repository(self):
396 """GET /_admin/create_repository: Form to create a new item"""
396 """GET /_admin/create_repository: Form to create a new item"""
397
397
398 c.repo_groups = RepoGroup.groups_choices()
398 c.repo_groups = RepoGroup.groups_choices()
399 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
399 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
400 c.landing_revs = ScmModel().get_repo_landing_revs()
400
401
401 new_repo = request.GET.get('repo', '')
402 new_repo = request.GET.get('repo', '')
402 c.new_repo = repo_name_slug(new_repo)
403 c.new_repo = repo_name_slug(new_repo)
403
404
404 return render('admin/repos/repo_add_create_repository.html')
405 return render('admin/repos/repo_add_create_repository.html')
405
406
406 def get_hg_ui_settings(self):
407 def get_hg_ui_settings(self):
407 ret = self.sa.query(RhodeCodeUi).all()
408 ret = self.sa.query(RhodeCodeUi).all()
408
409
409 if not ret:
410 if not ret:
410 raise Exception('Could not get application ui settings !')
411 raise Exception('Could not get application ui settings !')
411 settings = {}
412 settings = {}
412 for each in ret:
413 for each in ret:
413 k = each.ui_key
414 k = each.ui_key
414 v = each.ui_value
415 v = each.ui_value
415 if k == '/':
416 if k == '/':
416 k = 'root_path'
417 k = 'root_path'
417
418
418 if k.find('.') != -1:
419 if k.find('.') != -1:
419 k = k.replace('.', '_')
420 k = k.replace('.', '_')
420
421
421 if each.ui_section == 'hooks':
422 if each.ui_section == 'hooks':
422 v = each.ui_active
423 v = each.ui_active
423
424
424 settings[each.ui_section + '_' + k] = v
425 settings[each.ui_section + '_' + k] = v
425
426
426 return settings
427 return settings
@@ -1,671 +1,677 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.utils
3 rhodecode.lib.utils
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Utilities library for RhodeCode
6 Utilities library for RhodeCode
7
7
8 :created_on: Apr 18, 2010
8 :created_on: Apr 18, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 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 os
26 import os
27 import re
27 import re
28 import logging
28 import logging
29 import datetime
29 import datetime
30 import traceback
30 import traceback
31 import paste
31 import paste
32 import beaker
32 import beaker
33 import tarfile
33 import tarfile
34 import shutil
34 import shutil
35 from os.path import abspath
35 from os.path import abspath
36 from os.path import dirname as dn, join as jn
36 from os.path import dirname as dn, join as jn
37
37
38 from paste.script.command import Command, BadCommand
38 from paste.script.command import Command, BadCommand
39
39
40 from mercurial import ui, config
40 from mercurial import ui, config
41
41
42 from webhelpers.text import collapse, remove_formatting, strip_tags
42 from webhelpers.text import collapse, remove_formatting, strip_tags
43
43
44 from rhodecode.lib.vcs import get_backend
44 from rhodecode.lib.vcs import get_backend
45 from rhodecode.lib.vcs.backends.base import BaseChangeset
45 from rhodecode.lib.vcs.backends.base import BaseChangeset
46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
47 from rhodecode.lib.vcs.utils.helpers import get_scm
47 from rhodecode.lib.vcs.utils.helpers import get_scm
48 from rhodecode.lib.vcs.exceptions import VCSError
48 from rhodecode.lib.vcs.exceptions import VCSError
49
49
50 from rhodecode.lib.caching_query import FromCache
50 from rhodecode.lib.caching_query import FromCache
51
51
52 from rhodecode.model import meta
52 from rhodecode.model import meta
53 from rhodecode.model.db import Repository, User, RhodeCodeUi, \
53 from rhodecode.model.db import Repository, User, RhodeCodeUi, \
54 UserLog, RepoGroup, RhodeCodeSetting, UserRepoGroupToPerm,\
54 UserLog, RepoGroup, RhodeCodeSetting, UserRepoGroupToPerm,\
55 CacheInvalidation
55 CacheInvalidation
56 from rhodecode.model.meta import Session
56 from rhodecode.model.meta import Session
57 from rhodecode.model.repos_group import ReposGroupModel
57 from rhodecode.model.repos_group import ReposGroupModel
58 from rhodecode.lib.utils2 import safe_str, safe_unicode
58 from rhodecode.lib.utils2 import safe_str, safe_unicode
59 from rhodecode.lib.vcs.utils.fakemod import create_module
59 from rhodecode.lib.vcs.utils.fakemod import create_module
60
60
61 log = logging.getLogger(__name__)
61 log = logging.getLogger(__name__)
62
62
63 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
63 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
64
64
65
65
66 def recursive_replace(str_, replace=' '):
66 def recursive_replace(str_, replace=' '):
67 """
67 """
68 Recursive replace of given sign to just one instance
68 Recursive replace of given sign to just one instance
69
69
70 :param str_: given string
70 :param str_: given string
71 :param replace: char to find and replace multiple instances
71 :param replace: char to find and replace multiple instances
72
72
73 Examples::
73 Examples::
74 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
74 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
75 'Mighty-Mighty-Bo-sstones'
75 'Mighty-Mighty-Bo-sstones'
76 """
76 """
77
77
78 if str_.find(replace * 2) == -1:
78 if str_.find(replace * 2) == -1:
79 return str_
79 return str_
80 else:
80 else:
81 str_ = str_.replace(replace * 2, replace)
81 str_ = str_.replace(replace * 2, replace)
82 return recursive_replace(str_, replace)
82 return recursive_replace(str_, replace)
83
83
84
84
85 def repo_name_slug(value):
85 def repo_name_slug(value):
86 """
86 """
87 Return slug of name of repository
87 Return slug of name of repository
88 This function is called on each creation/modification
88 This function is called on each creation/modification
89 of repository to prevent bad names in repo
89 of repository to prevent bad names in repo
90 """
90 """
91
91
92 slug = remove_formatting(value)
92 slug = remove_formatting(value)
93 slug = strip_tags(slug)
93 slug = strip_tags(slug)
94
94
95 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
95 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
96 slug = slug.replace(c, '-')
96 slug = slug.replace(c, '-')
97 slug = recursive_replace(slug, '-')
97 slug = recursive_replace(slug, '-')
98 slug = collapse(slug, '-')
98 slug = collapse(slug, '-')
99 return slug
99 return slug
100
100
101
101
102 def get_repo_slug(request):
102 def get_repo_slug(request):
103 _repo = request.environ['pylons.routes_dict'].get('repo_name')
103 _repo = request.environ['pylons.routes_dict'].get('repo_name')
104 if _repo:
104 if _repo:
105 _repo = _repo.rstrip('/')
105 _repo = _repo.rstrip('/')
106 return _repo
106 return _repo
107
107
108
108
109 def get_repos_group_slug(request):
109 def get_repos_group_slug(request):
110 _group = request.environ['pylons.routes_dict'].get('group_name')
110 _group = request.environ['pylons.routes_dict'].get('group_name')
111 if _group:
111 if _group:
112 _group = _group.rstrip('/')
112 _group = _group.rstrip('/')
113 return _group
113 return _group
114
114
115
115
116 def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
116 def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
117 """
117 """
118 Action logger for various actions made by users
118 Action logger for various actions made by users
119
119
120 :param user: user that made this action, can be a unique username string or
120 :param user: user that made this action, can be a unique username string or
121 object containing user_id attribute
121 object containing user_id attribute
122 :param action: action to log, should be on of predefined unique actions for
122 :param action: action to log, should be on of predefined unique actions for
123 easy translations
123 easy translations
124 :param repo: string name of repository or object containing repo_id,
124 :param repo: string name of repository or object containing repo_id,
125 that action was made on
125 that action was made on
126 :param ipaddr: optional ip address from what the action was made
126 :param ipaddr: optional ip address from what the action was made
127 :param sa: optional sqlalchemy session
127 :param sa: optional sqlalchemy session
128
128
129 """
129 """
130
130
131 if not sa:
131 if not sa:
132 sa = meta.Session
132 sa = meta.Session
133
133
134 try:
134 try:
135 if hasattr(user, 'user_id'):
135 if hasattr(user, 'user_id'):
136 user_obj = user
136 user_obj = user
137 elif isinstance(user, basestring):
137 elif isinstance(user, basestring):
138 user_obj = User.get_by_username(user)
138 user_obj = User.get_by_username(user)
139 else:
139 else:
140 raise Exception('You have to provide user object or username')
140 raise Exception('You have to provide user object or username')
141
141
142 if hasattr(repo, 'repo_id'):
142 if hasattr(repo, 'repo_id'):
143 repo_obj = Repository.get(repo.repo_id)
143 repo_obj = Repository.get(repo.repo_id)
144 repo_name = repo_obj.repo_name
144 repo_name = repo_obj.repo_name
145 elif isinstance(repo, basestring):
145 elif isinstance(repo, basestring):
146 repo_name = repo.lstrip('/')
146 repo_name = repo.lstrip('/')
147 repo_obj = Repository.get_by_repo_name(repo_name)
147 repo_obj = Repository.get_by_repo_name(repo_name)
148 else:
148 else:
149 repo_obj = None
149 repo_obj = None
150 repo_name = ''
150 repo_name = ''
151
151
152 user_log = UserLog()
152 user_log = UserLog()
153 user_log.user_id = user_obj.user_id
153 user_log.user_id = user_obj.user_id
154 user_log.action = safe_unicode(action)
154 user_log.action = safe_unicode(action)
155
155
156 user_log.repository = repo_obj
156 user_log.repository = repo_obj
157 user_log.repository_name = repo_name
157 user_log.repository_name = repo_name
158
158
159 user_log.action_date = datetime.datetime.now()
159 user_log.action_date = datetime.datetime.now()
160 user_log.user_ip = ipaddr
160 user_log.user_ip = ipaddr
161 sa.add(user_log)
161 sa.add(user_log)
162
162
163 log.info(
163 log.info(
164 'Adding user %s, action %s on %s' % (user_obj, action,
164 'Adding user %s, action %s on %s' % (user_obj, action,
165 safe_unicode(repo))
165 safe_unicode(repo))
166 )
166 )
167 if commit:
167 if commit:
168 sa.commit()
168 sa.commit()
169 except:
169 except:
170 log.error(traceback.format_exc())
170 log.error(traceback.format_exc())
171 raise
171 raise
172
172
173
173
174 def get_repos(path, recursive=False):
174 def get_repos(path, recursive=False):
175 """
175 """
176 Scans given path for repos and return (name,(type,path)) tuple
176 Scans given path for repos and return (name,(type,path)) tuple
177
177
178 :param path: path to scan for repositories
178 :param path: path to scan for repositories
179 :param recursive: recursive search and return names with subdirs in front
179 :param recursive: recursive search and return names with subdirs in front
180 """
180 """
181
181
182 # remove ending slash for better results
182 # remove ending slash for better results
183 path = path.rstrip(os.sep)
183 path = path.rstrip(os.sep)
184
184
185 def _get_repos(p):
185 def _get_repos(p):
186 if not os.access(p, os.W_OK):
186 if not os.access(p, os.W_OK):
187 return
187 return
188 for dirpath in os.listdir(p):
188 for dirpath in os.listdir(p):
189 if os.path.isfile(os.path.join(p, dirpath)):
189 if os.path.isfile(os.path.join(p, dirpath)):
190 continue
190 continue
191 cur_path = os.path.join(p, dirpath)
191 cur_path = os.path.join(p, dirpath)
192 try:
192 try:
193 scm_info = get_scm(cur_path)
193 scm_info = get_scm(cur_path)
194 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
194 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
195 except VCSError:
195 except VCSError:
196 if not recursive:
196 if not recursive:
197 continue
197 continue
198 #check if this dir containts other repos for recursive scan
198 #check if this dir containts other repos for recursive scan
199 rec_path = os.path.join(p, dirpath)
199 rec_path = os.path.join(p, dirpath)
200 if os.path.isdir(rec_path):
200 if os.path.isdir(rec_path):
201 for inner_scm in _get_repos(rec_path):
201 for inner_scm in _get_repos(rec_path):
202 yield inner_scm
202 yield inner_scm
203
203
204 return _get_repos(path)
204 return _get_repos(path)
205
205
206
206
207 def is_valid_repo(repo_name, base_path):
207 def is_valid_repo(repo_name, base_path):
208 """
208 """
209 Returns True if given path is a valid repository False otherwise
209 Returns True if given path is a valid repository False otherwise
210
210
211 :param repo_name:
211 :param repo_name:
212 :param base_path:
212 :param base_path:
213
213
214 :return True: if given path is a valid repository
214 :return True: if given path is a valid repository
215 """
215 """
216 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
216 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
217
217
218 try:
218 try:
219 get_scm(full_path)
219 get_scm(full_path)
220 return True
220 return True
221 except VCSError:
221 except VCSError:
222 return False
222 return False
223
223
224
224
225 def is_valid_repos_group(repos_group_name, base_path):
225 def is_valid_repos_group(repos_group_name, base_path):
226 """
226 """
227 Returns True if given path is a repos group False otherwise
227 Returns True if given path is a repos group False otherwise
228
228
229 :param repo_name:
229 :param repo_name:
230 :param base_path:
230 :param base_path:
231 """
231 """
232 full_path = os.path.join(safe_str(base_path), safe_str(repos_group_name))
232 full_path = os.path.join(safe_str(base_path), safe_str(repos_group_name))
233
233
234 # check if it's not a repo
234 # check if it's not a repo
235 if is_valid_repo(repos_group_name, base_path):
235 if is_valid_repo(repos_group_name, base_path):
236 return False
236 return False
237
237
238 # check if it's a valid path
238 # check if it's a valid path
239 if os.path.isdir(full_path):
239 if os.path.isdir(full_path):
240 return True
240 return True
241
241
242 return False
242 return False
243
243
244
244
245 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
245 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
246 while True:
246 while True:
247 ok = raw_input(prompt)
247 ok = raw_input(prompt)
248 if ok in ('y', 'ye', 'yes'):
248 if ok in ('y', 'ye', 'yes'):
249 return True
249 return True
250 if ok in ('n', 'no', 'nop', 'nope'):
250 if ok in ('n', 'no', 'nop', 'nope'):
251 return False
251 return False
252 retries = retries - 1
252 retries = retries - 1
253 if retries < 0:
253 if retries < 0:
254 raise IOError
254 raise IOError
255 print complaint
255 print complaint
256
256
257 #propagated from mercurial documentation
257 #propagated from mercurial documentation
258 ui_sections = ['alias', 'auth',
258 ui_sections = ['alias', 'auth',
259 'decode/encode', 'defaults',
259 'decode/encode', 'defaults',
260 'diff', 'email',
260 'diff', 'email',
261 'extensions', 'format',
261 'extensions', 'format',
262 'merge-patterns', 'merge-tools',
262 'merge-patterns', 'merge-tools',
263 'hooks', 'http_proxy',
263 'hooks', 'http_proxy',
264 'smtp', 'patch',
264 'smtp', 'patch',
265 'paths', 'profiling',
265 'paths', 'profiling',
266 'server', 'trusted',
266 'server', 'trusted',
267 'ui', 'web', ]
267 'ui', 'web', ]
268
268
269
269
270 def make_ui(read_from='file', path=None, checkpaths=True):
270 def make_ui(read_from='file', path=None, checkpaths=True):
271 """
271 """
272 A function that will read python rc files or database
272 A function that will read python rc files or database
273 and make an mercurial ui object from read options
273 and make an mercurial ui object from read options
274
274
275 :param path: path to mercurial config file
275 :param path: path to mercurial config file
276 :param checkpaths: check the path
276 :param checkpaths: check the path
277 :param read_from: read from 'file' or 'db'
277 :param read_from: read from 'file' or 'db'
278 """
278 """
279
279
280 baseui = ui.ui()
280 baseui = ui.ui()
281
281
282 # clean the baseui object
282 # clean the baseui object
283 baseui._ocfg = config.config()
283 baseui._ocfg = config.config()
284 baseui._ucfg = config.config()
284 baseui._ucfg = config.config()
285 baseui._tcfg = config.config()
285 baseui._tcfg = config.config()
286
286
287 if read_from == 'file':
287 if read_from == 'file':
288 if not os.path.isfile(path):
288 if not os.path.isfile(path):
289 log.debug('hgrc file is not present at %s skipping...' % path)
289 log.debug('hgrc file is not present at %s skipping...' % path)
290 return False
290 return False
291 log.debug('reading hgrc from %s' % path)
291 log.debug('reading hgrc from %s' % path)
292 cfg = config.config()
292 cfg = config.config()
293 cfg.read(path)
293 cfg.read(path)
294 for section in ui_sections:
294 for section in ui_sections:
295 for k, v in cfg.items(section):
295 for k, v in cfg.items(section):
296 log.debug('settings ui from file[%s]%s:%s' % (section, k, v))
296 log.debug('settings ui from file[%s]%s:%s' % (section, k, v))
297 baseui.setconfig(section, k, v)
297 baseui.setconfig(section, k, v)
298
298
299 elif read_from == 'db':
299 elif read_from == 'db':
300 sa = meta.Session
300 sa = meta.Session
301 ret = sa.query(RhodeCodeUi)\
301 ret = sa.query(RhodeCodeUi)\
302 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
302 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
303 .all()
303 .all()
304
304
305 hg_ui = ret
305 hg_ui = ret
306 for ui_ in hg_ui:
306 for ui_ in hg_ui:
307 if ui_.ui_active:
307 if ui_.ui_active:
308 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
308 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
309 ui_.ui_key, ui_.ui_value)
309 ui_.ui_key, ui_.ui_value)
310 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
310 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
311
311
312 meta.Session.remove()
312 meta.Session.remove()
313 return baseui
313 return baseui
314
314
315
315
316 def set_rhodecode_config(config):
316 def set_rhodecode_config(config):
317 """
317 """
318 Updates pylons config with new settings from database
318 Updates pylons config with new settings from database
319
319
320 :param config:
320 :param config:
321 """
321 """
322 hgsettings = RhodeCodeSetting.get_app_settings()
322 hgsettings = RhodeCodeSetting.get_app_settings()
323
323
324 for k, v in hgsettings.items():
324 for k, v in hgsettings.items():
325 config[k] = v
325 config[k] = v
326
326
327
327
328 def invalidate_cache(cache_key, *args):
328 def invalidate_cache(cache_key, *args):
329 """
329 """
330 Puts cache invalidation task into db for
330 Puts cache invalidation task into db for
331 further global cache invalidation
331 further global cache invalidation
332 """
332 """
333
333
334 from rhodecode.model.scm import ScmModel
334 from rhodecode.model.scm import ScmModel
335
335
336 if cache_key.startswith('get_repo_cached_'):
336 if cache_key.startswith('get_repo_cached_'):
337 name = cache_key.split('get_repo_cached_')[-1]
337 name = cache_key.split('get_repo_cached_')[-1]
338 ScmModel().mark_for_invalidation(name)
338 ScmModel().mark_for_invalidation(name)
339
339
340
340
341 class EmptyChangeset(BaseChangeset):
341 class EmptyChangeset(BaseChangeset):
342 """
342 """
343 An dummy empty changeset. It's possible to pass hash when creating
343 An dummy empty changeset. It's possible to pass hash when creating
344 an EmptyChangeset
344 an EmptyChangeset
345 """
345 """
346
346
347 def __init__(self, cs='0' * 40, repo=None, requested_revision=None,
347 def __init__(self, cs='0' * 40, repo=None, requested_revision=None,
348 alias=None):
348 alias=None):
349 self._empty_cs = cs
349 self._empty_cs = cs
350 self.revision = -1
350 self.revision = -1
351 self.message = ''
351 self.message = ''
352 self.author = ''
352 self.author = ''
353 self.date = ''
353 self.date = ''
354 self.repository = repo
354 self.repository = repo
355 self.requested_revision = requested_revision
355 self.requested_revision = requested_revision
356 self.alias = alias
356 self.alias = alias
357
357
358 @LazyProperty
358 @LazyProperty
359 def raw_id(self):
359 def raw_id(self):
360 """
360 """
361 Returns raw string identifying this changeset, useful for web
361 Returns raw string identifying this changeset, useful for web
362 representation.
362 representation.
363 """
363 """
364
364
365 return self._empty_cs
365 return self._empty_cs
366
366
367 @LazyProperty
367 @LazyProperty
368 def branch(self):
368 def branch(self):
369 return get_backend(self.alias).DEFAULT_BRANCH_NAME
369 return get_backend(self.alias).DEFAULT_BRANCH_NAME
370
370
371 @LazyProperty
371 @LazyProperty
372 def short_id(self):
372 def short_id(self):
373 return self.raw_id[:12]
373 return self.raw_id[:12]
374
374
375 def get_file_changeset(self, path):
375 def get_file_changeset(self, path):
376 return self
376 return self
377
377
378 def get_file_content(self, path):
378 def get_file_content(self, path):
379 return u''
379 return u''
380
380
381 def get_file_size(self, path):
381 def get_file_size(self, path):
382 return 0
382 return 0
383
383
384
384
385 def map_groups(path):
385 def map_groups(path):
386 """
386 """
387 Given a full path to a repository, create all nested groups that this
387 Given a full path to a repository, create all nested groups that this
388 repo is inside. This function creates parent-child relationships between
388 repo is inside. This function creates parent-child relationships between
389 groups and creates default perms for all new groups.
389 groups and creates default perms for all new groups.
390
390
391 :param paths: full path to repository
391 :param paths: full path to repository
392 """
392 """
393 sa = meta.Session
393 sa = meta.Session
394 groups = path.split(Repository.url_sep())
394 groups = path.split(Repository.url_sep())
395 parent = None
395 parent = None
396 group = None
396 group = None
397
397
398 # last element is repo in nested groups structure
398 # last element is repo in nested groups structure
399 groups = groups[:-1]
399 groups = groups[:-1]
400 rgm = ReposGroupModel(sa)
400 rgm = ReposGroupModel(sa)
401 for lvl, group_name in enumerate(groups):
401 for lvl, group_name in enumerate(groups):
402 group_name = '/'.join(groups[:lvl] + [group_name])
402 group_name = '/'.join(groups[:lvl] + [group_name])
403 group = RepoGroup.get_by_group_name(group_name)
403 group = RepoGroup.get_by_group_name(group_name)
404 desc = '%s group' % group_name
404 desc = '%s group' % group_name
405
405
406 # skip folders that are now removed repos
406 # skip folders that are now removed repos
407 if REMOVED_REPO_PAT.match(group_name):
407 if REMOVED_REPO_PAT.match(group_name):
408 break
408 break
409
409
410 if group is None:
410 if group is None:
411 log.debug('creating group level: %s group_name: %s' % (lvl,
411 log.debug('creating group level: %s group_name: %s' % (lvl,
412 group_name))
412 group_name))
413 group = RepoGroup(group_name, parent)
413 group = RepoGroup(group_name, parent)
414 group.group_description = desc
414 group.group_description = desc
415 sa.add(group)
415 sa.add(group)
416 rgm._create_default_perms(group)
416 rgm._create_default_perms(group)
417 sa.flush()
417 sa.flush()
418 parent = group
418 parent = group
419 return group
419 return group
420
420
421
421
422 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
422 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
423 """
423 """
424 maps all repos given in initial_repo_list, non existing repositories
424 maps all repos given in initial_repo_list, non existing repositories
425 are created, if remove_obsolete is True it also check for db entries
425 are created, if remove_obsolete is True it also check for db entries
426 that are not in initial_repo_list and removes them.
426 that are not in initial_repo_list and removes them.
427
427
428 :param initial_repo_list: list of repositories found by scanning methods
428 :param initial_repo_list: list of repositories found by scanning methods
429 :param remove_obsolete: check for obsolete entries in database
429 :param remove_obsolete: check for obsolete entries in database
430 """
430 """
431 from rhodecode.model.repo import RepoModel
431 from rhodecode.model.repo import RepoModel
432 sa = meta.Session
432 sa = meta.Session
433 rm = RepoModel()
433 rm = RepoModel()
434 user = sa.query(User).filter(User.admin == True).first()
434 user = sa.query(User).filter(User.admin == True).first()
435 if user is None:
435 if user is None:
436 raise Exception('Missing administrative account !')
436 raise Exception('Missing administrative account !')
437 added = []
437 added = []
438
438
439 for name, repo in initial_repo_list.items():
439 for name, repo in initial_repo_list.items():
440 group = map_groups(name)
440 group = map_groups(name)
441 if not rm.get_by_repo_name(name, cache=False):
441 if not rm.get_by_repo_name(name, cache=False):
442 log.info('repository %s not found creating default' % name)
442 log.info('repository %s not found creating default' % name)
443 added.append(name)
443 added.append(name)
444 form_data = {
444 form_data = {
445 'repo_name': name,
445 'repo_name': name,
446 'repo_name_full': name,
446 'repo_name_full': name,
447 'repo_type': repo.alias,
447 'repo_type': repo.alias,
448 'description': repo.description \
448 'description': repo.description \
449 if repo.description != 'unknown' else '%s repository' % name,
449 if repo.description != 'unknown' else '%s repository' % name,
450 'private': False,
450 'private': False,
451 'group_id': getattr(group, 'group_id', None)
451 'group_id': getattr(group, 'group_id', None),
452 'landing_rev': repo.DEFAULT_BRANCH_NAME
452 }
453 }
453 rm.create(form_data, user, just_db=True)
454 rm.create(form_data, user, just_db=True)
454 sa.commit()
455 sa.commit()
455 removed = []
456 removed = []
456 if remove_obsolete:
457 if remove_obsolete:
457 # remove from database those repositories that are not in the filesystem
458 # remove from database those repositories that are not in the filesystem
458 for repo in sa.query(Repository).all():
459 for repo in sa.query(Repository).all():
459 if repo.repo_name not in initial_repo_list.keys():
460 if repo.repo_name not in initial_repo_list.keys():
460 log.debug("Removing non existing repository found in db %s" %
461 log.debug("Removing non existing repository found in db %s" %
461 repo.repo_name)
462 repo.repo_name)
462 removed.append(repo.repo_name)
463 removed.append(repo.repo_name)
463 sa.delete(repo)
464 sa.delete(repo)
464 sa.commit()
465 sa.commit()
465
466
466 # clear cache keys
467 # clear cache keys
467 log.debug("Clearing cache keys now...")
468 log.debug("Clearing cache keys now...")
468 CacheInvalidation.clear_cache()
469 CacheInvalidation.clear_cache()
469 sa.commit()
470 sa.commit()
470 return added, removed
471 return added, removed
471
472
472
473
473 # set cache regions for beaker so celery can utilise it
474 # set cache regions for beaker so celery can utilise it
474 def add_cache(settings):
475 def add_cache(settings):
475 cache_settings = {'regions': None}
476 cache_settings = {'regions': None}
476 for key in settings.keys():
477 for key in settings.keys():
477 for prefix in ['beaker.cache.', 'cache.']:
478 for prefix in ['beaker.cache.', 'cache.']:
478 if key.startswith(prefix):
479 if key.startswith(prefix):
479 name = key.split(prefix)[1].strip()
480 name = key.split(prefix)[1].strip()
480 cache_settings[name] = settings[key].strip()
481 cache_settings[name] = settings[key].strip()
481 if cache_settings['regions']:
482 if cache_settings['regions']:
482 for region in cache_settings['regions'].split(','):
483 for region in cache_settings['regions'].split(','):
483 region = region.strip()
484 region = region.strip()
484 region_settings = {}
485 region_settings = {}
485 for key, value in cache_settings.items():
486 for key, value in cache_settings.items():
486 if key.startswith(region):
487 if key.startswith(region):
487 region_settings[key.split('.')[1]] = value
488 region_settings[key.split('.')[1]] = value
488 region_settings['expire'] = int(region_settings.get('expire',
489 region_settings['expire'] = int(region_settings.get('expire',
489 60))
490 60))
490 region_settings.setdefault('lock_dir',
491 region_settings.setdefault('lock_dir',
491 cache_settings.get('lock_dir'))
492 cache_settings.get('lock_dir'))
492 region_settings.setdefault('data_dir',
493 region_settings.setdefault('data_dir',
493 cache_settings.get('data_dir'))
494 cache_settings.get('data_dir'))
494
495
495 if 'type' not in region_settings:
496 if 'type' not in region_settings:
496 region_settings['type'] = cache_settings.get('type',
497 region_settings['type'] = cache_settings.get('type',
497 'memory')
498 'memory')
498 beaker.cache.cache_regions[region] = region_settings
499 beaker.cache.cache_regions[region] = region_settings
499
500
500
501
501 def load_rcextensions(root_path):
502 def load_rcextensions(root_path):
502 import rhodecode
503 import rhodecode
503 from rhodecode.config import conf
504 from rhodecode.config import conf
504
505
505 path = os.path.join(root_path, 'rcextensions', '__init__.py')
506 path = os.path.join(root_path, 'rcextensions', '__init__.py')
506 if os.path.isfile(path):
507 if os.path.isfile(path):
507 rcext = create_module('rc', path)
508 rcext = create_module('rc', path)
508 EXT = rhodecode.EXTENSIONS = rcext
509 EXT = rhodecode.EXTENSIONS = rcext
509 log.debug('Found rcextensions now loading %s...' % rcext)
510 log.debug('Found rcextensions now loading %s...' % rcext)
510
511
511 # Additional mappings that are not present in the pygments lexers
512 # Additional mappings that are not present in the pygments lexers
512 conf.LANGUAGES_EXTENSIONS_MAP.update(getattr(EXT, 'EXTRA_MAPPINGS', {}))
513 conf.LANGUAGES_EXTENSIONS_MAP.update(getattr(EXT, 'EXTRA_MAPPINGS', {}))
513
514
514 #OVERRIDE OUR EXTENSIONS FROM RC-EXTENSIONS (if present)
515 #OVERRIDE OUR EXTENSIONS FROM RC-EXTENSIONS (if present)
515
516
516 if getattr(EXT, 'INDEX_EXTENSIONS', []) != []:
517 if getattr(EXT, 'INDEX_EXTENSIONS', []) != []:
517 log.debug('settings custom INDEX_EXTENSIONS')
518 log.debug('settings custom INDEX_EXTENSIONS')
518 conf.INDEX_EXTENSIONS = getattr(EXT, 'INDEX_EXTENSIONS', [])
519 conf.INDEX_EXTENSIONS = getattr(EXT, 'INDEX_EXTENSIONS', [])
519
520
520 #ADDITIONAL MAPPINGS
521 #ADDITIONAL MAPPINGS
521 log.debug('adding extra into INDEX_EXTENSIONS')
522 log.debug('adding extra into INDEX_EXTENSIONS')
522 conf.INDEX_EXTENSIONS.extend(getattr(EXT, 'EXTRA_INDEX_EXTENSIONS', []))
523 conf.INDEX_EXTENSIONS.extend(getattr(EXT, 'EXTRA_INDEX_EXTENSIONS', []))
523
524
524
525
525 #==============================================================================
526 #==============================================================================
526 # TEST FUNCTIONS AND CREATORS
527 # TEST FUNCTIONS AND CREATORS
527 #==============================================================================
528 #==============================================================================
528 def create_test_index(repo_location, config, full_index):
529 def create_test_index(repo_location, config, full_index):
529 """
530 """
530 Makes default test index
531 Makes default test index
531
532
532 :param config: test config
533 :param config: test config
533 :param full_index:
534 :param full_index:
534 """
535 """
535
536
536 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
537 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
537 from rhodecode.lib.pidlock import DaemonLock, LockHeld
538 from rhodecode.lib.pidlock import DaemonLock, LockHeld
538
539
539 repo_location = repo_location
540 repo_location = repo_location
540
541
541 index_location = os.path.join(config['app_conf']['index_dir'])
542 index_location = os.path.join(config['app_conf']['index_dir'])
542 if not os.path.exists(index_location):
543 if not os.path.exists(index_location):
543 os.makedirs(index_location)
544 os.makedirs(index_location)
544
545
545 try:
546 try:
546 l = DaemonLock(file_=jn(dn(index_location), 'make_index.lock'))
547 l = DaemonLock(file_=jn(dn(index_location), 'make_index.lock'))
547 WhooshIndexingDaemon(index_location=index_location,
548 WhooshIndexingDaemon(index_location=index_location,
548 repo_location=repo_location)\
549 repo_location=repo_location)\
549 .run(full_index=full_index)
550 .run(full_index=full_index)
550 l.release()
551 l.release()
551 except LockHeld:
552 except LockHeld:
552 pass
553 pass
553
554
554
555
555 def create_test_env(repos_test_path, config):
556 def create_test_env(repos_test_path, config):
556 """
557 """
557 Makes a fresh database and
558 Makes a fresh database and
558 install test repository into tmp dir
559 install test repository into tmp dir
559 """
560 """
560 from rhodecode.lib.db_manage import DbManage
561 from rhodecode.lib.db_manage import DbManage
561 from rhodecode.tests import HG_REPO, TESTS_TMP_PATH
562 from rhodecode.tests import HG_REPO, GIT_REPO, TESTS_TMP_PATH
562
563
563 # PART ONE create db
564 # PART ONE create db
564 dbconf = config['sqlalchemy.db1.url']
565 dbconf = config['sqlalchemy.db1.url']
565 log.debug('making test db %s' % dbconf)
566 log.debug('making test db %s' % dbconf)
566
567
567 # create test dir if it doesn't exist
568 # create test dir if it doesn't exist
568 if not os.path.isdir(repos_test_path):
569 if not os.path.isdir(repos_test_path):
569 log.debug('Creating testdir %s' % repos_test_path)
570 log.debug('Creating testdir %s' % repos_test_path)
570 os.makedirs(repos_test_path)
571 os.makedirs(repos_test_path)
571
572
572 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
573 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
573 tests=True)
574 tests=True)
574 dbmanage.create_tables(override=True)
575 dbmanage.create_tables(override=True)
575 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
576 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
576 dbmanage.create_default_user()
577 dbmanage.create_default_user()
577 dbmanage.admin_prompt()
578 dbmanage.admin_prompt()
578 dbmanage.create_permissions()
579 dbmanage.create_permissions()
579 dbmanage.populate_default_permissions()
580 dbmanage.populate_default_permissions()
580 Session.commit()
581 Session.commit()
581 # PART TWO make test repo
582 # PART TWO make test repo
582 log.debug('making test vcs repositories')
583 log.debug('making test vcs repositories')
583
584
584 idx_path = config['app_conf']['index_dir']
585 idx_path = config['app_conf']['index_dir']
585 data_path = config['app_conf']['cache_dir']
586 data_path = config['app_conf']['cache_dir']
586
587
587 #clean index and data
588 #clean index and data
588 if idx_path and os.path.exists(idx_path):
589 if idx_path and os.path.exists(idx_path):
589 log.debug('remove %s' % idx_path)
590 log.debug('remove %s' % idx_path)
590 shutil.rmtree(idx_path)
591 shutil.rmtree(idx_path)
591
592
592 if data_path and os.path.exists(data_path):
593 if data_path and os.path.exists(data_path):
593 log.debug('remove %s' % data_path)
594 log.debug('remove %s' % data_path)
594 shutil.rmtree(data_path)
595 shutil.rmtree(data_path)
595
596
596 #CREATE DEFAULT HG REPOSITORY
597 #CREATE DEFAULT TEST REPOS
597 cur_dir = dn(dn(abspath(__file__)))
598 cur_dir = dn(dn(abspath(__file__)))
598 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
599 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
599 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
600 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
600 tar.close()
601 tar.close()
601
602
603 cur_dir = dn(dn(abspath(__file__)))
604 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_git.tar.gz"))
605 tar.extractall(jn(TESTS_TMP_PATH, GIT_REPO))
606 tar.close()
607
602 #LOAD VCS test stuff
608 #LOAD VCS test stuff
603 from rhodecode.tests.vcs import setup_package
609 from rhodecode.tests.vcs import setup_package
604 setup_package()
610 setup_package()
605
611
606
612
607 #==============================================================================
613 #==============================================================================
608 # PASTER COMMANDS
614 # PASTER COMMANDS
609 #==============================================================================
615 #==============================================================================
610 class BasePasterCommand(Command):
616 class BasePasterCommand(Command):
611 """
617 """
612 Abstract Base Class for paster commands.
618 Abstract Base Class for paster commands.
613
619
614 The celery commands are somewhat aggressive about loading
620 The celery commands are somewhat aggressive about loading
615 celery.conf, and since our module sets the `CELERY_LOADER`
621 celery.conf, and since our module sets the `CELERY_LOADER`
616 environment variable to our loader, we have to bootstrap a bit and
622 environment variable to our loader, we have to bootstrap a bit and
617 make sure we've had a chance to load the pylons config off of the
623 make sure we've had a chance to load the pylons config off of the
618 command line, otherwise everything fails.
624 command line, otherwise everything fails.
619 """
625 """
620 min_args = 1
626 min_args = 1
621 min_args_error = "Please provide a paster config file as an argument."
627 min_args_error = "Please provide a paster config file as an argument."
622 takes_config_file = 1
628 takes_config_file = 1
623 requires_config_file = True
629 requires_config_file = True
624
630
625 def notify_msg(self, msg, log=False):
631 def notify_msg(self, msg, log=False):
626 """Make a notification to user, additionally if logger is passed
632 """Make a notification to user, additionally if logger is passed
627 it logs this action using given logger
633 it logs this action using given logger
628
634
629 :param msg: message that will be printed to user
635 :param msg: message that will be printed to user
630 :param log: logging instance, to use to additionally log this message
636 :param log: logging instance, to use to additionally log this message
631
637
632 """
638 """
633 if log and isinstance(log, logging):
639 if log and isinstance(log, logging):
634 log(msg)
640 log(msg)
635
641
636 def run(self, args):
642 def run(self, args):
637 """
643 """
638 Overrides Command.run
644 Overrides Command.run
639
645
640 Checks for a config file argument and loads it.
646 Checks for a config file argument and loads it.
641 """
647 """
642 if len(args) < self.min_args:
648 if len(args) < self.min_args:
643 raise BadCommand(
649 raise BadCommand(
644 self.min_args_error % {'min_args': self.min_args,
650 self.min_args_error % {'min_args': self.min_args,
645 'actual_args': len(args)})
651 'actual_args': len(args)})
646
652
647 # Decrement because we're going to lob off the first argument.
653 # Decrement because we're going to lob off the first argument.
648 # @@ This is hacky
654 # @@ This is hacky
649 self.min_args -= 1
655 self.min_args -= 1
650 self.bootstrap_config(args[0])
656 self.bootstrap_config(args[0])
651 self.update_parser()
657 self.update_parser()
652 return super(BasePasterCommand, self).run(args[1:])
658 return super(BasePasterCommand, self).run(args[1:])
653
659
654 def update_parser(self):
660 def update_parser(self):
655 """
661 """
656 Abstract method. Allows for the class's parser to be updated
662 Abstract method. Allows for the class's parser to be updated
657 before the superclass's `run` method is called. Necessary to
663 before the superclass's `run` method is called. Necessary to
658 allow options/arguments to be passed through to the underlying
664 allow options/arguments to be passed through to the underlying
659 celery command.
665 celery command.
660 """
666 """
661 raise NotImplementedError("Abstract Method.")
667 raise NotImplementedError("Abstract Method.")
662
668
663 def bootstrap_config(self, conf):
669 def bootstrap_config(self, conf):
664 """
670 """
665 Loads the pylons configuration.
671 Loads the pylons configuration.
666 """
672 """
667 from pylons import config as pylonsconfig
673 from pylons import config as pylonsconfig
668
674
669 self.path_to_ini_file = os.path.realpath(conf)
675 self.path_to_ini_file = os.path.realpath(conf)
670 conf = paste.deploy.appconfig('config:' + self.path_to_ini_file)
676 conf = paste.deploy.appconfig('config:' + self.path_to_ini_file)
671 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
677 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
@@ -1,1348 +1,1349 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.db
3 rhodecode.model.db
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 Database Models for RhodeCode
6 Database Models for RhodeCode
7
7
8 :created_on: Apr 08, 2010
8 :created_on: Apr 08, 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 os
26 import os
27 import logging
27 import logging
28 import datetime
28 import datetime
29 import traceback
29 import traceback
30 import hashlib
30 import hashlib
31 from collections import defaultdict
31 from collections import defaultdict
32
32
33 from sqlalchemy import *
33 from sqlalchemy import *
34 from sqlalchemy.ext.hybrid import hybrid_property
34 from sqlalchemy.ext.hybrid import hybrid_property
35 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
35 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
36 from beaker.cache import cache_region, region_invalidate
36 from beaker.cache import cache_region, region_invalidate
37
37
38 from rhodecode.lib.vcs import get_backend
38 from rhodecode.lib.vcs import get_backend
39 from rhodecode.lib.vcs.utils.helpers import get_scm
39 from rhodecode.lib.vcs.utils.helpers import get_scm
40 from rhodecode.lib.vcs.exceptions import VCSError
40 from rhodecode.lib.vcs.exceptions import VCSError
41 from rhodecode.lib.vcs.utils.lazy import LazyProperty
41 from rhodecode.lib.vcs.utils.lazy import LazyProperty
42
42
43 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
43 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
44 safe_unicode
44 safe_unicode
45 from rhodecode.lib.compat import json
45 from rhodecode.lib.compat import json
46 from rhodecode.lib.caching_query import FromCache
46 from rhodecode.lib.caching_query import FromCache
47 from rhodecode.model.meta import Base, Session
47 from rhodecode.model.meta import Base, Session
48
48
49
49
50 URL_SEP = '/'
50 URL_SEP = '/'
51 log = logging.getLogger(__name__)
51 log = logging.getLogger(__name__)
52
52
53 #==============================================================================
53 #==============================================================================
54 # BASE CLASSES
54 # BASE CLASSES
55 #==============================================================================
55 #==============================================================================
56
56
57 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
57 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
58
58
59
59
60 class ModelSerializer(json.JSONEncoder):
60 class ModelSerializer(json.JSONEncoder):
61 """
61 """
62 Simple Serializer for JSON,
62 Simple Serializer for JSON,
63
63
64 usage::
64 usage::
65
65
66 to make object customized for serialization implement a __json__
66 to make object customized for serialization implement a __json__
67 method that will return a dict for serialization into json
67 method that will return a dict for serialization into json
68
68
69 example::
69 example::
70
70
71 class Task(object):
71 class Task(object):
72
72
73 def __init__(self, name, value):
73 def __init__(self, name, value):
74 self.name = name
74 self.name = name
75 self.value = value
75 self.value = value
76
76
77 def __json__(self):
77 def __json__(self):
78 return dict(name=self.name,
78 return dict(name=self.name,
79 value=self.value)
79 value=self.value)
80
80
81 """
81 """
82
82
83 def default(self, obj):
83 def default(self, obj):
84
84
85 if hasattr(obj, '__json__'):
85 if hasattr(obj, '__json__'):
86 return obj.__json__()
86 return obj.__json__()
87 else:
87 else:
88 return json.JSONEncoder.default(self, obj)
88 return json.JSONEncoder.default(self, obj)
89
89
90
90
91 class BaseModel(object):
91 class BaseModel(object):
92 """
92 """
93 Base Model for all classess
93 Base Model for all classess
94 """
94 """
95
95
96 @classmethod
96 @classmethod
97 def _get_keys(cls):
97 def _get_keys(cls):
98 """return column names for this model """
98 """return column names for this model """
99 return class_mapper(cls).c.keys()
99 return class_mapper(cls).c.keys()
100
100
101 def get_dict(self):
101 def get_dict(self):
102 """
102 """
103 return dict with keys and values corresponding
103 return dict with keys and values corresponding
104 to this model data """
104 to this model data """
105
105
106 d = {}
106 d = {}
107 for k in self._get_keys():
107 for k in self._get_keys():
108 d[k] = getattr(self, k)
108 d[k] = getattr(self, k)
109
109
110 # also use __json__() if present to get additional fields
110 # also use __json__() if present to get additional fields
111 for k, val in getattr(self, '__json__', lambda: {})().iteritems():
111 for k, val in getattr(self, '__json__', lambda: {})().iteritems():
112 d[k] = val
112 d[k] = val
113 return d
113 return d
114
114
115 def get_appstruct(self):
115 def get_appstruct(self):
116 """return list with keys and values tupples corresponding
116 """return list with keys and values tupples corresponding
117 to this model data """
117 to this model data """
118
118
119 l = []
119 l = []
120 for k in self._get_keys():
120 for k in self._get_keys():
121 l.append((k, getattr(self, k),))
121 l.append((k, getattr(self, k),))
122 return l
122 return l
123
123
124 def populate_obj(self, populate_dict):
124 def populate_obj(self, populate_dict):
125 """populate model with data from given populate_dict"""
125 """populate model with data from given populate_dict"""
126
126
127 for k in self._get_keys():
127 for k in self._get_keys():
128 if k in populate_dict:
128 if k in populate_dict:
129 setattr(self, k, populate_dict[k])
129 setattr(self, k, populate_dict[k])
130
130
131 @classmethod
131 @classmethod
132 def query(cls):
132 def query(cls):
133 return Session.query(cls)
133 return Session.query(cls)
134
134
135 @classmethod
135 @classmethod
136 def get(cls, id_):
136 def get(cls, id_):
137 if id_:
137 if id_:
138 return cls.query().get(id_)
138 return cls.query().get(id_)
139
139
140 @classmethod
140 @classmethod
141 def getAll(cls):
141 def getAll(cls):
142 return cls.query().all()
142 return cls.query().all()
143
143
144 @classmethod
144 @classmethod
145 def delete(cls, id_):
145 def delete(cls, id_):
146 obj = cls.query().get(id_)
146 obj = cls.query().get(id_)
147 Session.delete(obj)
147 Session.delete(obj)
148
148
149 def __repr__(self):
149 def __repr__(self):
150 if hasattr(self, '__unicode__'):
150 if hasattr(self, '__unicode__'):
151 # python repr needs to return str
151 # python repr needs to return str
152 return safe_str(self.__unicode__())
152 return safe_str(self.__unicode__())
153 return '<DB:%s>' % (self.__class__.__name__)
153 return '<DB:%s>' % (self.__class__.__name__)
154
154
155
155
156 class RhodeCodeSetting(Base, BaseModel):
156 class RhodeCodeSetting(Base, BaseModel):
157 __tablename__ = 'rhodecode_settings'
157 __tablename__ = 'rhodecode_settings'
158 __table_args__ = (
158 __table_args__ = (
159 UniqueConstraint('app_settings_name'),
159 UniqueConstraint('app_settings_name'),
160 {'extend_existing': True, 'mysql_engine': 'InnoDB',
160 {'extend_existing': True, 'mysql_engine': 'InnoDB',
161 'mysql_charset': 'utf8'}
161 'mysql_charset': 'utf8'}
162 )
162 )
163 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
163 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
164 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
164 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
165 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
165 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
166
166
167 def __init__(self, k='', v=''):
167 def __init__(self, k='', v=''):
168 self.app_settings_name = k
168 self.app_settings_name = k
169 self.app_settings_value = v
169 self.app_settings_value = v
170
170
171 @validates('_app_settings_value')
171 @validates('_app_settings_value')
172 def validate_settings_value(self, key, val):
172 def validate_settings_value(self, key, val):
173 assert type(val) == unicode
173 assert type(val) == unicode
174 return val
174 return val
175
175
176 @hybrid_property
176 @hybrid_property
177 def app_settings_value(self):
177 def app_settings_value(self):
178 v = self._app_settings_value
178 v = self._app_settings_value
179 if self.app_settings_name == 'ldap_active':
179 if self.app_settings_name == 'ldap_active':
180 v = str2bool(v)
180 v = str2bool(v)
181 return v
181 return v
182
182
183 @app_settings_value.setter
183 @app_settings_value.setter
184 def app_settings_value(self, val):
184 def app_settings_value(self, val):
185 """
185 """
186 Setter that will always make sure we use unicode in app_settings_value
186 Setter that will always make sure we use unicode in app_settings_value
187
187
188 :param val:
188 :param val:
189 """
189 """
190 self._app_settings_value = safe_unicode(val)
190 self._app_settings_value = safe_unicode(val)
191
191
192 def __unicode__(self):
192 def __unicode__(self):
193 return u"<%s('%s:%s')>" % (
193 return u"<%s('%s:%s')>" % (
194 self.__class__.__name__,
194 self.__class__.__name__,
195 self.app_settings_name, self.app_settings_value
195 self.app_settings_name, self.app_settings_value
196 )
196 )
197
197
198 @classmethod
198 @classmethod
199 def get_by_name(cls, ldap_key):
199 def get_by_name(cls, ldap_key):
200 return cls.query()\
200 return cls.query()\
201 .filter(cls.app_settings_name == ldap_key).scalar()
201 .filter(cls.app_settings_name == ldap_key).scalar()
202
202
203 @classmethod
203 @classmethod
204 def get_app_settings(cls, cache=False):
204 def get_app_settings(cls, cache=False):
205
205
206 ret = cls.query()
206 ret = cls.query()
207
207
208 if cache:
208 if cache:
209 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
209 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
210
210
211 if not ret:
211 if not ret:
212 raise Exception('Could not get application settings !')
212 raise Exception('Could not get application settings !')
213 settings = {}
213 settings = {}
214 for each in ret:
214 for each in ret:
215 settings['rhodecode_' + each.app_settings_name] = \
215 settings['rhodecode_' + each.app_settings_name] = \
216 each.app_settings_value
216 each.app_settings_value
217
217
218 return settings
218 return settings
219
219
220 @classmethod
220 @classmethod
221 def get_ldap_settings(cls, cache=False):
221 def get_ldap_settings(cls, cache=False):
222 ret = cls.query()\
222 ret = cls.query()\
223 .filter(cls.app_settings_name.startswith('ldap_')).all()
223 .filter(cls.app_settings_name.startswith('ldap_')).all()
224 fd = {}
224 fd = {}
225 for row in ret:
225 for row in ret:
226 fd.update({row.app_settings_name: row.app_settings_value})
226 fd.update({row.app_settings_name: row.app_settings_value})
227
227
228 return fd
228 return fd
229
229
230
230
231 class RhodeCodeUi(Base, BaseModel):
231 class RhodeCodeUi(Base, BaseModel):
232 __tablename__ = 'rhodecode_ui'
232 __tablename__ = 'rhodecode_ui'
233 __table_args__ = (
233 __table_args__ = (
234 UniqueConstraint('ui_key'),
234 UniqueConstraint('ui_key'),
235 {'extend_existing': True, 'mysql_engine': 'InnoDB',
235 {'extend_existing': True, 'mysql_engine': 'InnoDB',
236 'mysql_charset': 'utf8'}
236 'mysql_charset': 'utf8'}
237 )
237 )
238
238
239 HOOK_UPDATE = 'changegroup.update'
239 HOOK_UPDATE = 'changegroup.update'
240 HOOK_REPO_SIZE = 'changegroup.repo_size'
240 HOOK_REPO_SIZE = 'changegroup.repo_size'
241 HOOK_PUSH = 'changegroup.push_logger'
241 HOOK_PUSH = 'changegroup.push_logger'
242 HOOK_PULL = 'preoutgoing.pull_logger'
242 HOOK_PULL = 'preoutgoing.pull_logger'
243
243
244 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
244 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
245 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
245 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
246 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
246 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
247 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
247 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
248 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
248 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
249
249
250 @classmethod
250 @classmethod
251 def get_by_key(cls, key):
251 def get_by_key(cls, key):
252 return cls.query().filter(cls.ui_key == key)
252 return cls.query().filter(cls.ui_key == key)
253
253
254 @classmethod
254 @classmethod
255 def get_builtin_hooks(cls):
255 def get_builtin_hooks(cls):
256 q = cls.query()
256 q = cls.query()
257 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
257 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
258 cls.HOOK_REPO_SIZE,
258 cls.HOOK_REPO_SIZE,
259 cls.HOOK_PUSH, cls.HOOK_PULL]))
259 cls.HOOK_PUSH, cls.HOOK_PULL]))
260 return q.all()
260 return q.all()
261
261
262 @classmethod
262 @classmethod
263 def get_custom_hooks(cls):
263 def get_custom_hooks(cls):
264 q = cls.query()
264 q = cls.query()
265 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
265 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
266 cls.HOOK_REPO_SIZE,
266 cls.HOOK_REPO_SIZE,
267 cls.HOOK_PUSH, cls.HOOK_PULL]))
267 cls.HOOK_PUSH, cls.HOOK_PULL]))
268 q = q.filter(cls.ui_section == 'hooks')
268 q = q.filter(cls.ui_section == 'hooks')
269 return q.all()
269 return q.all()
270
270
271 @classmethod
271 @classmethod
272 def get_repos_location(cls):
272 def get_repos_location(cls):
273 return cls.get_by_key('/').one().ui_value
273 return cls.get_by_key('/').one().ui_value
274
274
275 @classmethod
275 @classmethod
276 def create_or_update_hook(cls, key, val):
276 def create_or_update_hook(cls, key, val):
277 new_ui = cls.get_by_key(key).scalar() or cls()
277 new_ui = cls.get_by_key(key).scalar() or cls()
278 new_ui.ui_section = 'hooks'
278 new_ui.ui_section = 'hooks'
279 new_ui.ui_active = True
279 new_ui.ui_active = True
280 new_ui.ui_key = key
280 new_ui.ui_key = key
281 new_ui.ui_value = val
281 new_ui.ui_value = val
282
282
283 Session.add(new_ui)
283 Session.add(new_ui)
284
284
285
285
286 class User(Base, BaseModel):
286 class User(Base, BaseModel):
287 __tablename__ = 'users'
287 __tablename__ = 'users'
288 __table_args__ = (
288 __table_args__ = (
289 UniqueConstraint('username'), UniqueConstraint('email'),
289 UniqueConstraint('username'), UniqueConstraint('email'),
290 {'extend_existing': True, 'mysql_engine': 'InnoDB',
290 {'extend_existing': True, 'mysql_engine': 'InnoDB',
291 'mysql_charset': 'utf8'}
291 'mysql_charset': 'utf8'}
292 )
292 )
293 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
293 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
294 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
294 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
295 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
295 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
296 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
296 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
297 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
297 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
298 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
298 name = Column("firstname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
299 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
299 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
300 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
300 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
301 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
301 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
302 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
302 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
303 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
303 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
304
304
305 user_log = relationship('UserLog', cascade='all')
305 user_log = relationship('UserLog', cascade='all')
306 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
306 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
307
307
308 repositories = relationship('Repository')
308 repositories = relationship('Repository')
309 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
309 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
310 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
310 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
311 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
311 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
312
312
313 group_member = relationship('UsersGroupMember', cascade='all')
313 group_member = relationship('UsersGroupMember', cascade='all')
314
314
315 notifications = relationship('UserNotification', cascade='all')
315 notifications = relationship('UserNotification', cascade='all')
316 # notifications assigned to this user
316 # notifications assigned to this user
317 user_created_notifications = relationship('Notification', cascade='all')
317 user_created_notifications = relationship('Notification', cascade='all')
318 # comments created by this user
318 # comments created by this user
319 user_comments = relationship('ChangesetComment', cascade='all')
319 user_comments = relationship('ChangesetComment', cascade='all')
320
320
321 @hybrid_property
321 @hybrid_property
322 def email(self):
322 def email(self):
323 return self._email
323 return self._email
324
324
325 @email.setter
325 @email.setter
326 def email(self, val):
326 def email(self, val):
327 self._email = val.lower() if val else None
327 self._email = val.lower() if val else None
328
328
329 @property
329 @property
330 def full_name(self):
330 def full_name(self):
331 return '%s %s' % (self.name, self.lastname)
331 return '%s %s' % (self.name, self.lastname)
332
332
333 @property
333 @property
334 def full_name_or_username(self):
334 def full_name_or_username(self):
335 return ('%s %s' % (self.name, self.lastname)
335 return ('%s %s' % (self.name, self.lastname)
336 if (self.name and self.lastname) else self.username)
336 if (self.name and self.lastname) else self.username)
337
337
338 @property
338 @property
339 def full_contact(self):
339 def full_contact(self):
340 return '%s %s <%s>' % (self.name, self.lastname, self.email)
340 return '%s %s <%s>' % (self.name, self.lastname, self.email)
341
341
342 @property
342 @property
343 def short_contact(self):
343 def short_contact(self):
344 return '%s %s' % (self.name, self.lastname)
344 return '%s %s' % (self.name, self.lastname)
345
345
346 @property
346 @property
347 def is_admin(self):
347 def is_admin(self):
348 return self.admin
348 return self.admin
349
349
350 def __unicode__(self):
350 def __unicode__(self):
351 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
351 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
352 self.user_id, self.username)
352 self.user_id, self.username)
353
353
354 @classmethod
354 @classmethod
355 def get_by_username(cls, username, case_insensitive=False, cache=False):
355 def get_by_username(cls, username, case_insensitive=False, cache=False):
356 if case_insensitive:
356 if case_insensitive:
357 q = cls.query().filter(cls.username.ilike(username))
357 q = cls.query().filter(cls.username.ilike(username))
358 else:
358 else:
359 q = cls.query().filter(cls.username == username)
359 q = cls.query().filter(cls.username == username)
360
360
361 if cache:
361 if cache:
362 q = q.options(FromCache(
362 q = q.options(FromCache(
363 "sql_cache_short",
363 "sql_cache_short",
364 "get_user_%s" % _hash_key(username)
364 "get_user_%s" % _hash_key(username)
365 )
365 )
366 )
366 )
367 return q.scalar()
367 return q.scalar()
368
368
369 @classmethod
369 @classmethod
370 def get_by_api_key(cls, api_key, cache=False):
370 def get_by_api_key(cls, api_key, cache=False):
371 q = cls.query().filter(cls.api_key == api_key)
371 q = cls.query().filter(cls.api_key == api_key)
372
372
373 if cache:
373 if cache:
374 q = q.options(FromCache("sql_cache_short",
374 q = q.options(FromCache("sql_cache_short",
375 "get_api_key_%s" % api_key))
375 "get_api_key_%s" % api_key))
376 return q.scalar()
376 return q.scalar()
377
377
378 @classmethod
378 @classmethod
379 def get_by_email(cls, email, case_insensitive=False, cache=False):
379 def get_by_email(cls, email, case_insensitive=False, cache=False):
380 if case_insensitive:
380 if case_insensitive:
381 q = cls.query().filter(cls.email.ilike(email))
381 q = cls.query().filter(cls.email.ilike(email))
382 else:
382 else:
383 q = cls.query().filter(cls.email == email)
383 q = cls.query().filter(cls.email == email)
384
384
385 if cache:
385 if cache:
386 q = q.options(FromCache("sql_cache_short",
386 q = q.options(FromCache("sql_cache_short",
387 "get_api_key_%s" % email))
387 "get_api_key_%s" % email))
388 return q.scalar()
388 return q.scalar()
389
389
390 def update_lastlogin(self):
390 def update_lastlogin(self):
391 """Update user lastlogin"""
391 """Update user lastlogin"""
392 self.last_login = datetime.datetime.now()
392 self.last_login = datetime.datetime.now()
393 Session.add(self)
393 Session.add(self)
394 log.debug('updated user %s lastlogin' % self.username)
394 log.debug('updated user %s lastlogin' % self.username)
395
395
396 def __json__(self):
396 def __json__(self):
397 return dict(
397 return dict(
398 user_id=self.user_id,
398 user_id=self.user_id,
399 first_name=self.name,
399 first_name=self.name,
400 last_name=self.lastname,
400 last_name=self.lastname,
401 email=self.email,
401 email=self.email,
402 full_name=self.full_name,
402 full_name=self.full_name,
403 full_name_or_username=self.full_name_or_username,
403 full_name_or_username=self.full_name_or_username,
404 short_contact=self.short_contact,
404 short_contact=self.short_contact,
405 full_contact=self.full_contact
405 full_contact=self.full_contact
406 )
406 )
407
407
408
408
409 class UserLog(Base, BaseModel):
409 class UserLog(Base, BaseModel):
410 __tablename__ = 'user_logs'
410 __tablename__ = 'user_logs'
411 __table_args__ = (
411 __table_args__ = (
412 {'extend_existing': True, 'mysql_engine': 'InnoDB',
412 {'extend_existing': True, 'mysql_engine': 'InnoDB',
413 'mysql_charset': 'utf8'},
413 'mysql_charset': 'utf8'},
414 )
414 )
415 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
415 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
416 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
416 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
417 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
417 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
418 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
418 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
419 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
419 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
420 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
420 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
421 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
421 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
422
422
423 @property
423 @property
424 def action_as_day(self):
424 def action_as_day(self):
425 return datetime.date(*self.action_date.timetuple()[:3])
425 return datetime.date(*self.action_date.timetuple()[:3])
426
426
427 user = relationship('User')
427 user = relationship('User')
428 repository = relationship('Repository', cascade='')
428 repository = relationship('Repository', cascade='')
429
429
430
430
431 class UsersGroup(Base, BaseModel):
431 class UsersGroup(Base, BaseModel):
432 __tablename__ = 'users_groups'
432 __tablename__ = 'users_groups'
433 __table_args__ = (
433 __table_args__ = (
434 {'extend_existing': True, 'mysql_engine': 'InnoDB',
434 {'extend_existing': True, 'mysql_engine': 'InnoDB',
435 'mysql_charset': 'utf8'},
435 'mysql_charset': 'utf8'},
436 )
436 )
437
437
438 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
438 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
439 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
439 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
440 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
440 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
441
441
442 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
442 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
443 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
443 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
444 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
444 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
445
445
446 def __unicode__(self):
446 def __unicode__(self):
447 return u'<userGroup(%s)>' % (self.users_group_name)
447 return u'<userGroup(%s)>' % (self.users_group_name)
448
448
449 @classmethod
449 @classmethod
450 def get_by_group_name(cls, group_name, cache=False,
450 def get_by_group_name(cls, group_name, cache=False,
451 case_insensitive=False):
451 case_insensitive=False):
452 if case_insensitive:
452 if case_insensitive:
453 q = cls.query().filter(cls.users_group_name.ilike(group_name))
453 q = cls.query().filter(cls.users_group_name.ilike(group_name))
454 else:
454 else:
455 q = cls.query().filter(cls.users_group_name == group_name)
455 q = cls.query().filter(cls.users_group_name == group_name)
456 if cache:
456 if cache:
457 q = q.options(FromCache(
457 q = q.options(FromCache(
458 "sql_cache_short",
458 "sql_cache_short",
459 "get_user_%s" % _hash_key(group_name)
459 "get_user_%s" % _hash_key(group_name)
460 )
460 )
461 )
461 )
462 return q.scalar()
462 return q.scalar()
463
463
464 @classmethod
464 @classmethod
465 def get(cls, users_group_id, cache=False):
465 def get(cls, users_group_id, cache=False):
466 users_group = cls.query()
466 users_group = cls.query()
467 if cache:
467 if cache:
468 users_group = users_group.options(FromCache("sql_cache_short",
468 users_group = users_group.options(FromCache("sql_cache_short",
469 "get_users_group_%s" % users_group_id))
469 "get_users_group_%s" % users_group_id))
470 return users_group.get(users_group_id)
470 return users_group.get(users_group_id)
471
471
472
472
473 class UsersGroupMember(Base, BaseModel):
473 class UsersGroupMember(Base, BaseModel):
474 __tablename__ = 'users_groups_members'
474 __tablename__ = 'users_groups_members'
475 __table_args__ = (
475 __table_args__ = (
476 {'extend_existing': True, 'mysql_engine': 'InnoDB',
476 {'extend_existing': True, 'mysql_engine': 'InnoDB',
477 'mysql_charset': 'utf8'},
477 'mysql_charset': 'utf8'},
478 )
478 )
479
479
480 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
480 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
481 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
481 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
482 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
482 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
483
483
484 user = relationship('User', lazy='joined')
484 user = relationship('User', lazy='joined')
485 users_group = relationship('UsersGroup')
485 users_group = relationship('UsersGroup')
486
486
487 def __init__(self, gr_id='', u_id=''):
487 def __init__(self, gr_id='', u_id=''):
488 self.users_group_id = gr_id
488 self.users_group_id = gr_id
489 self.user_id = u_id
489 self.user_id = u_id
490
490
491
491
492 class Repository(Base, BaseModel):
492 class Repository(Base, BaseModel):
493 __tablename__ = 'repositories'
493 __tablename__ = 'repositories'
494 __table_args__ = (
494 __table_args__ = (
495 UniqueConstraint('repo_name'),
495 UniqueConstraint('repo_name'),
496 {'extend_existing': True, 'mysql_engine': 'InnoDB',
496 {'extend_existing': True, 'mysql_engine': 'InnoDB',
497 'mysql_charset': 'utf8'},
497 'mysql_charset': 'utf8'},
498 )
498 )
499
499
500 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
500 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
501 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
501 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
502 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
502 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
503 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
503 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
504 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
504 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
505 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
505 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
506 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
506 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
507 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
507 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
508 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
508 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
509 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
509 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
510 landing_rev = Column("landing_revision", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
510
511
511 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
512 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
512 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
513 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
513
514
514 user = relationship('User')
515 user = relationship('User')
515 fork = relationship('Repository', remote_side=repo_id)
516 fork = relationship('Repository', remote_side=repo_id)
516 group = relationship('RepoGroup')
517 group = relationship('RepoGroup')
517 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
518 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
518 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
519 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
519 stats = relationship('Statistics', cascade='all', uselist=False)
520 stats = relationship('Statistics', cascade='all', uselist=False)
520
521
521 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
522 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
522
523
523 logs = relationship('UserLog')
524 logs = relationship('UserLog')
524
525
525 def __unicode__(self):
526 def __unicode__(self):
526 return u"<%s('%s:%s')>" % (self.__class__.__name__,self.repo_id,
527 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
527 self.repo_name)
528 self.repo_name)
528
529
529 @classmethod
530 @classmethod
530 def url_sep(cls):
531 def url_sep(cls):
531 return URL_SEP
532 return URL_SEP
532
533
533 @classmethod
534 @classmethod
534 def get_by_repo_name(cls, repo_name):
535 def get_by_repo_name(cls, repo_name):
535 q = Session.query(cls).filter(cls.repo_name == repo_name)
536 q = Session.query(cls).filter(cls.repo_name == repo_name)
536 q = q.options(joinedload(Repository.fork))\
537 q = q.options(joinedload(Repository.fork))\
537 .options(joinedload(Repository.user))\
538 .options(joinedload(Repository.user))\
538 .options(joinedload(Repository.group))
539 .options(joinedload(Repository.group))
539 return q.scalar()
540 return q.scalar()
540
541
541 @classmethod
542 @classmethod
542 def get_by_full_path(cls, repo_full_path):
543 def get_by_full_path(cls, repo_full_path):
543 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
544 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
544 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
545 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
545
546
546 @classmethod
547 @classmethod
547 def get_repo_forks(cls, repo_id):
548 def get_repo_forks(cls, repo_id):
548 return cls.query().filter(Repository.fork_id == repo_id)
549 return cls.query().filter(Repository.fork_id == repo_id)
549
550
550 @classmethod
551 @classmethod
551 def base_path(cls):
552 def base_path(cls):
552 """
553 """
553 Returns base path when all repos are stored
554 Returns base path when all repos are stored
554
555
555 :param cls:
556 :param cls:
556 """
557 """
557 q = Session.query(RhodeCodeUi)\
558 q = Session.query(RhodeCodeUi)\
558 .filter(RhodeCodeUi.ui_key == cls.url_sep())
559 .filter(RhodeCodeUi.ui_key == cls.url_sep())
559 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
560 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
560 return q.one().ui_value
561 return q.one().ui_value
561
562
562 @property
563 @property
563 def just_name(self):
564 def just_name(self):
564 return self.repo_name.split(Repository.url_sep())[-1]
565 return self.repo_name.split(Repository.url_sep())[-1]
565
566
566 @property
567 @property
567 def groups_with_parents(self):
568 def groups_with_parents(self):
568 groups = []
569 groups = []
569 if self.group is None:
570 if self.group is None:
570 return groups
571 return groups
571
572
572 cur_gr = self.group
573 cur_gr = self.group
573 groups.insert(0, cur_gr)
574 groups.insert(0, cur_gr)
574 while 1:
575 while 1:
575 gr = getattr(cur_gr, 'parent_group', None)
576 gr = getattr(cur_gr, 'parent_group', None)
576 cur_gr = cur_gr.parent_group
577 cur_gr = cur_gr.parent_group
577 if gr is None:
578 if gr is None:
578 break
579 break
579 groups.insert(0, gr)
580 groups.insert(0, gr)
580
581
581 return groups
582 return groups
582
583
583 @property
584 @property
584 def groups_and_repo(self):
585 def groups_and_repo(self):
585 return self.groups_with_parents, self.just_name
586 return self.groups_with_parents, self.just_name
586
587
587 @LazyProperty
588 @LazyProperty
588 def repo_path(self):
589 def repo_path(self):
589 """
590 """
590 Returns base full path for that repository means where it actually
591 Returns base full path for that repository means where it actually
591 exists on a filesystem
592 exists on a filesystem
592 """
593 """
593 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
594 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
594 Repository.url_sep())
595 Repository.url_sep())
595 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
596 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
596 return q.one().ui_value
597 return q.one().ui_value
597
598
598 @property
599 @property
599 def repo_full_path(self):
600 def repo_full_path(self):
600 p = [self.repo_path]
601 p = [self.repo_path]
601 # we need to split the name by / since this is how we store the
602 # we need to split the name by / since this is how we store the
602 # names in the database, but that eventually needs to be converted
603 # names in the database, but that eventually needs to be converted
603 # into a valid system path
604 # into a valid system path
604 p += self.repo_name.split(Repository.url_sep())
605 p += self.repo_name.split(Repository.url_sep())
605 return os.path.join(*p)
606 return os.path.join(*p)
606
607
607 def get_new_name(self, repo_name):
608 def get_new_name(self, repo_name):
608 """
609 """
609 returns new full repository name based on assigned group and new new
610 returns new full repository name based on assigned group and new new
610
611
611 :param group_name:
612 :param group_name:
612 """
613 """
613 path_prefix = self.group.full_path_splitted if self.group else []
614 path_prefix = self.group.full_path_splitted if self.group else []
614 return Repository.url_sep().join(path_prefix + [repo_name])
615 return Repository.url_sep().join(path_prefix + [repo_name])
615
616
616 @property
617 @property
617 def _ui(self):
618 def _ui(self):
618 """
619 """
619 Creates an db based ui object for this repository
620 Creates an db based ui object for this repository
620 """
621 """
621 from mercurial import ui
622 from mercurial import ui
622 from mercurial import config
623 from mercurial import config
623 baseui = ui.ui()
624 baseui = ui.ui()
624
625
625 #clean the baseui object
626 #clean the baseui object
626 baseui._ocfg = config.config()
627 baseui._ocfg = config.config()
627 baseui._ucfg = config.config()
628 baseui._ucfg = config.config()
628 baseui._tcfg = config.config()
629 baseui._tcfg = config.config()
629
630
630 ret = RhodeCodeUi.query()\
631 ret = RhodeCodeUi.query()\
631 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
632 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
632
633
633 hg_ui = ret
634 hg_ui = ret
634 for ui_ in hg_ui:
635 for ui_ in hg_ui:
635 if ui_.ui_active:
636 if ui_.ui_active:
636 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
637 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
637 ui_.ui_key, ui_.ui_value)
638 ui_.ui_key, ui_.ui_value)
638 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
639 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
639
640
640 return baseui
641 return baseui
641
642
642 @classmethod
643 @classmethod
643 def is_valid(cls, repo_name):
644 def is_valid(cls, repo_name):
644 """
645 """
645 returns True if given repo name is a valid filesystem repository
646 returns True if given repo name is a valid filesystem repository
646
647
647 :param cls:
648 :param cls:
648 :param repo_name:
649 :param repo_name:
649 """
650 """
650 from rhodecode.lib.utils import is_valid_repo
651 from rhodecode.lib.utils import is_valid_repo
651
652
652 return is_valid_repo(repo_name, cls.base_path())
653 return is_valid_repo(repo_name, cls.base_path())
653
654
654 #==========================================================================
655 #==========================================================================
655 # SCM PROPERTIES
656 # SCM PROPERTIES
656 #==========================================================================
657 #==========================================================================
657
658
658 def get_changeset(self, rev=None):
659 def get_changeset(self, rev=None):
659 return get_changeset_safe(self.scm_instance, rev)
660 return get_changeset_safe(self.scm_instance, rev)
660
661
661 @property
662 @property
662 def tip(self):
663 def tip(self):
663 return self.get_changeset('tip')
664 return self.get_changeset('tip')
664
665
665 @property
666 @property
666 def author(self):
667 def author(self):
667 return self.tip.author
668 return self.tip.author
668
669
669 @property
670 @property
670 def last_change(self):
671 def last_change(self):
671 return self.scm_instance.last_change
672 return self.scm_instance.last_change
672
673
673 def comments(self, revisions=None):
674 def comments(self, revisions=None):
674 """
675 """
675 Returns comments for this repository grouped by revisions
676 Returns comments for this repository grouped by revisions
676
677
677 :param revisions: filter query by revisions only
678 :param revisions: filter query by revisions only
678 """
679 """
679 cmts = ChangesetComment.query()\
680 cmts = ChangesetComment.query()\
680 .filter(ChangesetComment.repo == self)
681 .filter(ChangesetComment.repo == self)
681 if revisions:
682 if revisions:
682 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
683 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
683 grouped = defaultdict(list)
684 grouped = defaultdict(list)
684 for cmt in cmts.all():
685 for cmt in cmts.all():
685 grouped[cmt.revision].append(cmt)
686 grouped[cmt.revision].append(cmt)
686 return grouped
687 return grouped
687
688
688 #==========================================================================
689 #==========================================================================
689 # SCM CACHE INSTANCE
690 # SCM CACHE INSTANCE
690 #==========================================================================
691 #==========================================================================
691
692
692 @property
693 @property
693 def invalidate(self):
694 def invalidate(self):
694 return CacheInvalidation.invalidate(self.repo_name)
695 return CacheInvalidation.invalidate(self.repo_name)
695
696
696 def set_invalidate(self):
697 def set_invalidate(self):
697 """
698 """
698 set a cache for invalidation for this instance
699 set a cache for invalidation for this instance
699 """
700 """
700 CacheInvalidation.set_invalidate(self.repo_name)
701 CacheInvalidation.set_invalidate(self.repo_name)
701
702
702 @LazyProperty
703 @LazyProperty
703 def scm_instance(self):
704 def scm_instance(self):
704 return self.__get_instance()
705 return self.__get_instance()
705
706
706 def scm_instance_cached(self, cache_map=None):
707 def scm_instance_cached(self, cache_map=None):
707 @cache_region('long_term')
708 @cache_region('long_term')
708 def _c(repo_name):
709 def _c(repo_name):
709 return self.__get_instance()
710 return self.__get_instance()
710 rn = self.repo_name
711 rn = self.repo_name
711 log.debug('Getting cached instance of repo')
712 log.debug('Getting cached instance of repo')
712
713
713 if cache_map:
714 if cache_map:
714 # get using prefilled cache_map
715 # get using prefilled cache_map
715 invalidate_repo = cache_map[self.repo_name]
716 invalidate_repo = cache_map[self.repo_name]
716 if invalidate_repo:
717 if invalidate_repo:
717 invalidate_repo = (None if invalidate_repo.cache_active
718 invalidate_repo = (None if invalidate_repo.cache_active
718 else invalidate_repo)
719 else invalidate_repo)
719 else:
720 else:
720 # get from invalidate
721 # get from invalidate
721 invalidate_repo = self.invalidate
722 invalidate_repo = self.invalidate
722
723
723 if invalidate_repo is not None:
724 if invalidate_repo is not None:
724 region_invalidate(_c, None, rn)
725 region_invalidate(_c, None, rn)
725 # update our cache
726 # update our cache
726 CacheInvalidation.set_valid(invalidate_repo.cache_key)
727 CacheInvalidation.set_valid(invalidate_repo.cache_key)
727 return _c(rn)
728 return _c(rn)
728
729
729 def __get_instance(self):
730 def __get_instance(self):
730 repo_full_path = self.repo_full_path
731 repo_full_path = self.repo_full_path
731 try:
732 try:
732 alias = get_scm(repo_full_path)[0]
733 alias = get_scm(repo_full_path)[0]
733 log.debug('Creating instance of %s repository' % alias)
734 log.debug('Creating instance of %s repository' % alias)
734 backend = get_backend(alias)
735 backend = get_backend(alias)
735 except VCSError:
736 except VCSError:
736 log.error(traceback.format_exc())
737 log.error(traceback.format_exc())
737 log.error('Perhaps this repository is in db and not in '
738 log.error('Perhaps this repository is in db and not in '
738 'filesystem run rescan repositories with '
739 'filesystem run rescan repositories with '
739 '"destroy old data " option from admin panel')
740 '"destroy old data " option from admin panel')
740 return
741 return
741
742
742 if alias == 'hg':
743 if alias == 'hg':
743
744
744 repo = backend(safe_str(repo_full_path), create=False,
745 repo = backend(safe_str(repo_full_path), create=False,
745 baseui=self._ui)
746 baseui=self._ui)
746 # skip hidden web repository
747 # skip hidden web repository
747 if repo._get_hidden():
748 if repo._get_hidden():
748 return
749 return
749 else:
750 else:
750 repo = backend(repo_full_path, create=False)
751 repo = backend(repo_full_path, create=False)
751
752
752 return repo
753 return repo
753
754
754
755
755 class RepoGroup(Base, BaseModel):
756 class RepoGroup(Base, BaseModel):
756 __tablename__ = 'groups'
757 __tablename__ = 'groups'
757 __table_args__ = (
758 __table_args__ = (
758 UniqueConstraint('group_name', 'group_parent_id'),
759 UniqueConstraint('group_name', 'group_parent_id'),
759 CheckConstraint('group_id != group_parent_id'),
760 CheckConstraint('group_id != group_parent_id'),
760 {'extend_existing': True, 'mysql_engine': 'InnoDB',
761 {'extend_existing': True, 'mysql_engine': 'InnoDB',
761 'mysql_charset': 'utf8'},
762 'mysql_charset': 'utf8'},
762 )
763 )
763 __mapper_args__ = {'order_by': 'group_name'}
764 __mapper_args__ = {'order_by': 'group_name'}
764
765
765 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
766 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
766 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
767 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
767 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
768 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
768 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
769 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
769
770
770 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
771 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
771 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
772 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
772
773
773 parent_group = relationship('RepoGroup', remote_side=group_id)
774 parent_group = relationship('RepoGroup', remote_side=group_id)
774
775
775 def __init__(self, group_name='', parent_group=None):
776 def __init__(self, group_name='', parent_group=None):
776 self.group_name = group_name
777 self.group_name = group_name
777 self.parent_group = parent_group
778 self.parent_group = parent_group
778
779
779 def __unicode__(self):
780 def __unicode__(self):
780 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
781 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
781 self.group_name)
782 self.group_name)
782
783
783 @classmethod
784 @classmethod
784 def groups_choices(cls):
785 def groups_choices(cls):
785 from webhelpers.html import literal as _literal
786 from webhelpers.html import literal as _literal
786 repo_groups = [('', '')]
787 repo_groups = [('', '')]
787 sep = ' &raquo; '
788 sep = ' &raquo; '
788 _name = lambda k: _literal(sep.join(k))
789 _name = lambda k: _literal(sep.join(k))
789
790
790 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
791 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
791 for x in cls.query().all()])
792 for x in cls.query().all()])
792
793
793 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
794 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
794 return repo_groups
795 return repo_groups
795
796
796 @classmethod
797 @classmethod
797 def url_sep(cls):
798 def url_sep(cls):
798 return URL_SEP
799 return URL_SEP
799
800
800 @classmethod
801 @classmethod
801 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
802 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
802 if case_insensitive:
803 if case_insensitive:
803 gr = cls.query()\
804 gr = cls.query()\
804 .filter(cls.group_name.ilike(group_name))
805 .filter(cls.group_name.ilike(group_name))
805 else:
806 else:
806 gr = cls.query()\
807 gr = cls.query()\
807 .filter(cls.group_name == group_name)
808 .filter(cls.group_name == group_name)
808 if cache:
809 if cache:
809 gr = gr.options(FromCache(
810 gr = gr.options(FromCache(
810 "sql_cache_short",
811 "sql_cache_short",
811 "get_group_%s" % _hash_key(group_name)
812 "get_group_%s" % _hash_key(group_name)
812 )
813 )
813 )
814 )
814 return gr.scalar()
815 return gr.scalar()
815
816
816 @property
817 @property
817 def parents(self):
818 def parents(self):
818 parents_recursion_limit = 5
819 parents_recursion_limit = 5
819 groups = []
820 groups = []
820 if self.parent_group is None:
821 if self.parent_group is None:
821 return groups
822 return groups
822 cur_gr = self.parent_group
823 cur_gr = self.parent_group
823 groups.insert(0, cur_gr)
824 groups.insert(0, cur_gr)
824 cnt = 0
825 cnt = 0
825 while 1:
826 while 1:
826 cnt += 1
827 cnt += 1
827 gr = getattr(cur_gr, 'parent_group', None)
828 gr = getattr(cur_gr, 'parent_group', None)
828 cur_gr = cur_gr.parent_group
829 cur_gr = cur_gr.parent_group
829 if gr is None:
830 if gr is None:
830 break
831 break
831 if cnt == parents_recursion_limit:
832 if cnt == parents_recursion_limit:
832 # this will prevent accidental infinit loops
833 # this will prevent accidental infinit loops
833 log.error('group nested more than %s' %
834 log.error('group nested more than %s' %
834 parents_recursion_limit)
835 parents_recursion_limit)
835 break
836 break
836
837
837 groups.insert(0, gr)
838 groups.insert(0, gr)
838 return groups
839 return groups
839
840
840 @property
841 @property
841 def children(self):
842 def children(self):
842 return RepoGroup.query().filter(RepoGroup.parent_group == self)
843 return RepoGroup.query().filter(RepoGroup.parent_group == self)
843
844
844 @property
845 @property
845 def name(self):
846 def name(self):
846 return self.group_name.split(RepoGroup.url_sep())[-1]
847 return self.group_name.split(RepoGroup.url_sep())[-1]
847
848
848 @property
849 @property
849 def full_path(self):
850 def full_path(self):
850 return self.group_name
851 return self.group_name
851
852
852 @property
853 @property
853 def full_path_splitted(self):
854 def full_path_splitted(self):
854 return self.group_name.split(RepoGroup.url_sep())
855 return self.group_name.split(RepoGroup.url_sep())
855
856
856 @property
857 @property
857 def repositories(self):
858 def repositories(self):
858 return Repository.query()\
859 return Repository.query()\
859 .filter(Repository.group == self)\
860 .filter(Repository.group == self)\
860 .order_by(Repository.repo_name)
861 .order_by(Repository.repo_name)
861
862
862 @property
863 @property
863 def repositories_recursive_count(self):
864 def repositories_recursive_count(self):
864 cnt = self.repositories.count()
865 cnt = self.repositories.count()
865
866
866 def children_count(group):
867 def children_count(group):
867 cnt = 0
868 cnt = 0
868 for child in group.children:
869 for child in group.children:
869 cnt += child.repositories.count()
870 cnt += child.repositories.count()
870 cnt += children_count(child)
871 cnt += children_count(child)
871 return cnt
872 return cnt
872
873
873 return cnt + children_count(self)
874 return cnt + children_count(self)
874
875
875 def get_new_name(self, group_name):
876 def get_new_name(self, group_name):
876 """
877 """
877 returns new full group name based on parent and new name
878 returns new full group name based on parent and new name
878
879
879 :param group_name:
880 :param group_name:
880 """
881 """
881 path_prefix = (self.parent_group.full_path_splitted if
882 path_prefix = (self.parent_group.full_path_splitted if
882 self.parent_group else [])
883 self.parent_group else [])
883 return RepoGroup.url_sep().join(path_prefix + [group_name])
884 return RepoGroup.url_sep().join(path_prefix + [group_name])
884
885
885
886
886 class Permission(Base, BaseModel):
887 class Permission(Base, BaseModel):
887 __tablename__ = 'permissions'
888 __tablename__ = 'permissions'
888 __table_args__ = (
889 __table_args__ = (
889 {'extend_existing': True, 'mysql_engine': 'InnoDB',
890 {'extend_existing': True, 'mysql_engine': 'InnoDB',
890 'mysql_charset': 'utf8'},
891 'mysql_charset': 'utf8'},
891 )
892 )
892 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
893 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
893 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
894 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
894 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
895 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
895
896
896 def __unicode__(self):
897 def __unicode__(self):
897 return u"<%s('%s:%s')>" % (
898 return u"<%s('%s:%s')>" % (
898 self.__class__.__name__, self.permission_id, self.permission_name
899 self.__class__.__name__, self.permission_id, self.permission_name
899 )
900 )
900
901
901 @classmethod
902 @classmethod
902 def get_by_key(cls, key):
903 def get_by_key(cls, key):
903 return cls.query().filter(cls.permission_name == key).scalar()
904 return cls.query().filter(cls.permission_name == key).scalar()
904
905
905 @classmethod
906 @classmethod
906 def get_default_perms(cls, default_user_id):
907 def get_default_perms(cls, default_user_id):
907 q = Session.query(UserRepoToPerm, Repository, cls)\
908 q = Session.query(UserRepoToPerm, Repository, cls)\
908 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
909 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
909 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
910 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
910 .filter(UserRepoToPerm.user_id == default_user_id)
911 .filter(UserRepoToPerm.user_id == default_user_id)
911
912
912 return q.all()
913 return q.all()
913
914
914 @classmethod
915 @classmethod
915 def get_default_group_perms(cls, default_user_id):
916 def get_default_group_perms(cls, default_user_id):
916 q = Session.query(UserRepoGroupToPerm, RepoGroup, cls)\
917 q = Session.query(UserRepoGroupToPerm, RepoGroup, cls)\
917 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
918 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
918 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
919 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
919 .filter(UserRepoGroupToPerm.user_id == default_user_id)
920 .filter(UserRepoGroupToPerm.user_id == default_user_id)
920
921
921 return q.all()
922 return q.all()
922
923
923
924
924 class UserRepoToPerm(Base, BaseModel):
925 class UserRepoToPerm(Base, BaseModel):
925 __tablename__ = 'repo_to_perm'
926 __tablename__ = 'repo_to_perm'
926 __table_args__ = (
927 __table_args__ = (
927 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
928 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
928 {'extend_existing': True, 'mysql_engine': 'InnoDB',
929 {'extend_existing': True, 'mysql_engine': 'InnoDB',
929 'mysql_charset': 'utf8'}
930 'mysql_charset': 'utf8'}
930 )
931 )
931 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
932 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
932 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
933 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
933 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
934 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
934 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
935 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
935
936
936 user = relationship('User')
937 user = relationship('User')
937 repository = relationship('Repository')
938 repository = relationship('Repository')
938 permission = relationship('Permission')
939 permission = relationship('Permission')
939
940
940 @classmethod
941 @classmethod
941 def create(cls, user, repository, permission):
942 def create(cls, user, repository, permission):
942 n = cls()
943 n = cls()
943 n.user = user
944 n.user = user
944 n.repository = repository
945 n.repository = repository
945 n.permission = permission
946 n.permission = permission
946 Session.add(n)
947 Session.add(n)
947 return n
948 return n
948
949
949 def __unicode__(self):
950 def __unicode__(self):
950 return u'<user:%s => %s >' % (self.user, self.repository)
951 return u'<user:%s => %s >' % (self.user, self.repository)
951
952
952
953
953 class UserToPerm(Base, BaseModel):
954 class UserToPerm(Base, BaseModel):
954 __tablename__ = 'user_to_perm'
955 __tablename__ = 'user_to_perm'
955 __table_args__ = (
956 __table_args__ = (
956 UniqueConstraint('user_id', 'permission_id'),
957 UniqueConstraint('user_id', 'permission_id'),
957 {'extend_existing': True, 'mysql_engine': 'InnoDB',
958 {'extend_existing': True, 'mysql_engine': 'InnoDB',
958 'mysql_charset': 'utf8'}
959 'mysql_charset': 'utf8'}
959 )
960 )
960 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
961 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
961 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
962 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
962 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
963 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
963
964
964 user = relationship('User')
965 user = relationship('User')
965 permission = relationship('Permission', lazy='joined')
966 permission = relationship('Permission', lazy='joined')
966
967
967
968
968 class UsersGroupRepoToPerm(Base, BaseModel):
969 class UsersGroupRepoToPerm(Base, BaseModel):
969 __tablename__ = 'users_group_repo_to_perm'
970 __tablename__ = 'users_group_repo_to_perm'
970 __table_args__ = (
971 __table_args__ = (
971 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
972 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
972 {'extend_existing': True, 'mysql_engine': 'InnoDB',
973 {'extend_existing': True, 'mysql_engine': 'InnoDB',
973 'mysql_charset': 'utf8'}
974 'mysql_charset': 'utf8'}
974 )
975 )
975 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
976 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
976 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
977 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
977 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
978 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
978 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
979 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
979
980
980 users_group = relationship('UsersGroup')
981 users_group = relationship('UsersGroup')
981 permission = relationship('Permission')
982 permission = relationship('Permission')
982 repository = relationship('Repository')
983 repository = relationship('Repository')
983
984
984 @classmethod
985 @classmethod
985 def create(cls, users_group, repository, permission):
986 def create(cls, users_group, repository, permission):
986 n = cls()
987 n = cls()
987 n.users_group = users_group
988 n.users_group = users_group
988 n.repository = repository
989 n.repository = repository
989 n.permission = permission
990 n.permission = permission
990 Session.add(n)
991 Session.add(n)
991 return n
992 return n
992
993
993 def __unicode__(self):
994 def __unicode__(self):
994 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
995 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
995
996
996
997
997 class UsersGroupToPerm(Base, BaseModel):
998 class UsersGroupToPerm(Base, BaseModel):
998 __tablename__ = 'users_group_to_perm'
999 __tablename__ = 'users_group_to_perm'
999 __table_args__ = (
1000 __table_args__ = (
1000 UniqueConstraint('users_group_id', 'permission_id',),
1001 UniqueConstraint('users_group_id', 'permission_id',),
1001 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1002 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1002 'mysql_charset': 'utf8'}
1003 'mysql_charset': 'utf8'}
1003 )
1004 )
1004 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1005 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1005 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1006 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1006 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1007 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1007
1008
1008 users_group = relationship('UsersGroup')
1009 users_group = relationship('UsersGroup')
1009 permission = relationship('Permission')
1010 permission = relationship('Permission')
1010
1011
1011
1012
1012 class UserRepoGroupToPerm(Base, BaseModel):
1013 class UserRepoGroupToPerm(Base, BaseModel):
1013 __tablename__ = 'user_repo_group_to_perm'
1014 __tablename__ = 'user_repo_group_to_perm'
1014 __table_args__ = (
1015 __table_args__ = (
1015 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1016 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1016 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1017 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1017 'mysql_charset': 'utf8'}
1018 'mysql_charset': 'utf8'}
1018 )
1019 )
1019
1020
1020 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1021 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1021 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1022 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1022 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1023 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1023 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1024 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1024
1025
1025 user = relationship('User')
1026 user = relationship('User')
1026 group = relationship('RepoGroup')
1027 group = relationship('RepoGroup')
1027 permission = relationship('Permission')
1028 permission = relationship('Permission')
1028
1029
1029
1030
1030 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1031 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1031 __tablename__ = 'users_group_repo_group_to_perm'
1032 __tablename__ = 'users_group_repo_group_to_perm'
1032 __table_args__ = (
1033 __table_args__ = (
1033 UniqueConstraint('users_group_id', 'group_id'),
1034 UniqueConstraint('users_group_id', 'group_id'),
1034 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1035 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1035 'mysql_charset': 'utf8'}
1036 'mysql_charset': 'utf8'}
1036 )
1037 )
1037
1038
1038 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1039 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1039 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1040 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1040 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1041 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1041 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1042 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1042
1043
1043 users_group = relationship('UsersGroup')
1044 users_group = relationship('UsersGroup')
1044 permission = relationship('Permission')
1045 permission = relationship('Permission')
1045 group = relationship('RepoGroup')
1046 group = relationship('RepoGroup')
1046
1047
1047
1048
1048 class Statistics(Base, BaseModel):
1049 class Statistics(Base, BaseModel):
1049 __tablename__ = 'statistics'
1050 __tablename__ = 'statistics'
1050 __table_args__ = (
1051 __table_args__ = (
1051 UniqueConstraint('repository_id'),
1052 UniqueConstraint('repository_id'),
1052 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1053 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1053 'mysql_charset': 'utf8'}
1054 'mysql_charset': 'utf8'}
1054 )
1055 )
1055 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1056 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1056 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1057 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1057 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1058 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1058 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1059 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1059 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1060 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1060 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1061 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1061
1062
1062 repository = relationship('Repository', single_parent=True)
1063 repository = relationship('Repository', single_parent=True)
1063
1064
1064
1065
1065 class UserFollowing(Base, BaseModel):
1066 class UserFollowing(Base, BaseModel):
1066 __tablename__ = 'user_followings'
1067 __tablename__ = 'user_followings'
1067 __table_args__ = (
1068 __table_args__ = (
1068 UniqueConstraint('user_id', 'follows_repository_id'),
1069 UniqueConstraint('user_id', 'follows_repository_id'),
1069 UniqueConstraint('user_id', 'follows_user_id'),
1070 UniqueConstraint('user_id', 'follows_user_id'),
1070 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1071 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1071 'mysql_charset': 'utf8'}
1072 'mysql_charset': 'utf8'}
1072 )
1073 )
1073
1074
1074 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1075 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1075 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1076 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1076 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1077 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1077 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1078 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1078 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1079 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1079
1080
1080 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1081 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1081
1082
1082 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1083 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1083 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1084 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1084
1085
1085 @classmethod
1086 @classmethod
1086 def get_repo_followers(cls, repo_id):
1087 def get_repo_followers(cls, repo_id):
1087 return cls.query().filter(cls.follows_repo_id == repo_id)
1088 return cls.query().filter(cls.follows_repo_id == repo_id)
1088
1089
1089
1090
1090 class CacheInvalidation(Base, BaseModel):
1091 class CacheInvalidation(Base, BaseModel):
1091 __tablename__ = 'cache_invalidation'
1092 __tablename__ = 'cache_invalidation'
1092 __table_args__ = (
1093 __table_args__ = (
1093 UniqueConstraint('cache_key'),
1094 UniqueConstraint('cache_key'),
1094 Index('key_idx', 'cache_key'),
1095 Index('key_idx', 'cache_key'),
1095 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1096 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1096 'mysql_charset': 'utf8'},
1097 'mysql_charset': 'utf8'},
1097 )
1098 )
1098 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1099 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1099 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1100 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1100 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1101 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1101 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1102 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1102
1103
1103 def __init__(self, cache_key, cache_args=''):
1104 def __init__(self, cache_key, cache_args=''):
1104 self.cache_key = cache_key
1105 self.cache_key = cache_key
1105 self.cache_args = cache_args
1106 self.cache_args = cache_args
1106 self.cache_active = False
1107 self.cache_active = False
1107
1108
1108 def __unicode__(self):
1109 def __unicode__(self):
1109 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1110 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1110 self.cache_id, self.cache_key)
1111 self.cache_id, self.cache_key)
1111
1112
1112 @classmethod
1113 @classmethod
1113 def clear_cache(cls):
1114 def clear_cache(cls):
1114 cls.query().delete()
1115 cls.query().delete()
1115
1116
1116 @classmethod
1117 @classmethod
1117 def _get_key(cls, key):
1118 def _get_key(cls, key):
1118 """
1119 """
1119 Wrapper for generating a key, together with a prefix
1120 Wrapper for generating a key, together with a prefix
1120
1121
1121 :param key:
1122 :param key:
1122 """
1123 """
1123 import rhodecode
1124 import rhodecode
1124 prefix = ''
1125 prefix = ''
1125 iid = rhodecode.CONFIG.get('instance_id')
1126 iid = rhodecode.CONFIG.get('instance_id')
1126 if iid:
1127 if iid:
1127 prefix = iid
1128 prefix = iid
1128 return "%s%s" % (prefix, key), prefix, key.rstrip('_README')
1129 return "%s%s" % (prefix, key), prefix, key.rstrip('_README')
1129
1130
1130 @classmethod
1131 @classmethod
1131 def get_by_key(cls, key):
1132 def get_by_key(cls, key):
1132 return cls.query().filter(cls.cache_key == key).scalar()
1133 return cls.query().filter(cls.cache_key == key).scalar()
1133
1134
1134 @classmethod
1135 @classmethod
1135 def _get_or_create_key(cls, key, prefix, org_key):
1136 def _get_or_create_key(cls, key, prefix, org_key):
1136 inv_obj = Session.query(cls).filter(cls.cache_key == key).scalar()
1137 inv_obj = Session.query(cls).filter(cls.cache_key == key).scalar()
1137 if not inv_obj:
1138 if not inv_obj:
1138 try:
1139 try:
1139 inv_obj = CacheInvalidation(key, org_key)
1140 inv_obj = CacheInvalidation(key, org_key)
1140 Session.add(inv_obj)
1141 Session.add(inv_obj)
1141 Session.commit()
1142 Session.commit()
1142 except Exception:
1143 except Exception:
1143 log.error(traceback.format_exc())
1144 log.error(traceback.format_exc())
1144 Session.rollback()
1145 Session.rollback()
1145 return inv_obj
1146 return inv_obj
1146
1147
1147 @classmethod
1148 @classmethod
1148 def invalidate(cls, key):
1149 def invalidate(cls, key):
1149 """
1150 """
1150 Returns Invalidation object if this given key should be invalidated
1151 Returns Invalidation object if this given key should be invalidated
1151 None otherwise. `cache_active = False` means that this cache
1152 None otherwise. `cache_active = False` means that this cache
1152 state is not valid and needs to be invalidated
1153 state is not valid and needs to be invalidated
1153
1154
1154 :param key:
1155 :param key:
1155 """
1156 """
1156
1157
1157 key, _prefix, _org_key = cls._get_key(key)
1158 key, _prefix, _org_key = cls._get_key(key)
1158 inv = cls._get_or_create_key(key, _prefix, _org_key)
1159 inv = cls._get_or_create_key(key, _prefix, _org_key)
1159
1160
1160 if inv and inv.cache_active is False:
1161 if inv and inv.cache_active is False:
1161 return inv
1162 return inv
1162
1163
1163 @classmethod
1164 @classmethod
1164 def set_invalidate(cls, key):
1165 def set_invalidate(cls, key):
1165 """
1166 """
1166 Mark this Cache key for invalidation
1167 Mark this Cache key for invalidation
1167
1168
1168 :param key:
1169 :param key:
1169 """
1170 """
1170
1171
1171 key, _prefix, _org_key = cls._get_key(key)
1172 key, _prefix, _org_key = cls._get_key(key)
1172 inv_objs = Session.query(cls).filter(cls.cache_args == _org_key).all()
1173 inv_objs = Session.query(cls).filter(cls.cache_args == _org_key).all()
1173 log.debug('marking %s key[s] %s for invalidation' % (len(inv_objs),
1174 log.debug('marking %s key[s] %s for invalidation' % (len(inv_objs),
1174 _org_key))
1175 _org_key))
1175 try:
1176 try:
1176 for inv_obj in inv_objs:
1177 for inv_obj in inv_objs:
1177 if inv_obj:
1178 if inv_obj:
1178 inv_obj.cache_active = False
1179 inv_obj.cache_active = False
1179
1180
1180 Session.add(inv_obj)
1181 Session.add(inv_obj)
1181 Session.commit()
1182 Session.commit()
1182 except Exception:
1183 except Exception:
1183 log.error(traceback.format_exc())
1184 log.error(traceback.format_exc())
1184 Session.rollback()
1185 Session.rollback()
1185
1186
1186 @classmethod
1187 @classmethod
1187 def set_valid(cls, key):
1188 def set_valid(cls, key):
1188 """
1189 """
1189 Mark this cache key as active and currently cached
1190 Mark this cache key as active and currently cached
1190
1191
1191 :param key:
1192 :param key:
1192 """
1193 """
1193 inv_obj = cls.get_by_key(key)
1194 inv_obj = cls.get_by_key(key)
1194 inv_obj.cache_active = True
1195 inv_obj.cache_active = True
1195 Session.add(inv_obj)
1196 Session.add(inv_obj)
1196 Session.commit()
1197 Session.commit()
1197
1198
1198 @classmethod
1199 @classmethod
1199 def get_cache_map(cls):
1200 def get_cache_map(cls):
1200
1201
1201 class cachemapdict(dict):
1202 class cachemapdict(dict):
1202
1203
1203 def __init__(self, *args, **kwargs):
1204 def __init__(self, *args, **kwargs):
1204 fixkey = kwargs.get('fixkey')
1205 fixkey = kwargs.get('fixkey')
1205 if fixkey:
1206 if fixkey:
1206 del kwargs['fixkey']
1207 del kwargs['fixkey']
1207 self.fixkey = fixkey
1208 self.fixkey = fixkey
1208 super(cachemapdict, self).__init__(*args, **kwargs)
1209 super(cachemapdict, self).__init__(*args, **kwargs)
1209
1210
1210 def __getattr__(self, name):
1211 def __getattr__(self, name):
1211 key = name
1212 key = name
1212 if self.fixkey:
1213 if self.fixkey:
1213 key, _prefix, _org_key = cls._get_key(key)
1214 key, _prefix, _org_key = cls._get_key(key)
1214 if key in self.__dict__:
1215 if key in self.__dict__:
1215 return self.__dict__[key]
1216 return self.__dict__[key]
1216 else:
1217 else:
1217 return self[key]
1218 return self[key]
1218
1219
1219 def __getitem__(self, key):
1220 def __getitem__(self, key):
1220 if self.fixkey:
1221 if self.fixkey:
1221 key, _prefix, _org_key = cls._get_key(key)
1222 key, _prefix, _org_key = cls._get_key(key)
1222 try:
1223 try:
1223 return super(cachemapdict, self).__getitem__(key)
1224 return super(cachemapdict, self).__getitem__(key)
1224 except KeyError:
1225 except KeyError:
1225 return
1226 return
1226
1227
1227 cache_map = cachemapdict(fixkey=True)
1228 cache_map = cachemapdict(fixkey=True)
1228 for obj in cls.query().all():
1229 for obj in cls.query().all():
1229 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1230 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1230 return cache_map
1231 return cache_map
1231
1232
1232
1233
1233 class ChangesetComment(Base, BaseModel):
1234 class ChangesetComment(Base, BaseModel):
1234 __tablename__ = 'changeset_comments'
1235 __tablename__ = 'changeset_comments'
1235 __table_args__ = (
1236 __table_args__ = (
1236 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1237 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1237 'mysql_charset': 'utf8'},
1238 'mysql_charset': 'utf8'},
1238 )
1239 )
1239 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1240 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1240 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1241 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1241 revision = Column('revision', String(40), nullable=False)
1242 revision = Column('revision', String(40), nullable=False)
1242 line_no = Column('line_no', Unicode(10), nullable=True)
1243 line_no = Column('line_no', Unicode(10), nullable=True)
1243 f_path = Column('f_path', Unicode(1000), nullable=True)
1244 f_path = Column('f_path', Unicode(1000), nullable=True)
1244 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1245 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1245 text = Column('text', Unicode(25000), nullable=False)
1246 text = Column('text', Unicode(25000), nullable=False)
1246 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1247 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1247
1248
1248 author = relationship('User', lazy='joined')
1249 author = relationship('User', lazy='joined')
1249 repo = relationship('Repository')
1250 repo = relationship('Repository')
1250
1251
1251 @classmethod
1252 @classmethod
1252 def get_users(cls, revision):
1253 def get_users(cls, revision):
1253 """
1254 """
1254 Returns user associated with this changesetComment. ie those
1255 Returns user associated with this changesetComment. ie those
1255 who actually commented
1256 who actually commented
1256
1257
1257 :param cls:
1258 :param cls:
1258 :param revision:
1259 :param revision:
1259 """
1260 """
1260 return Session.query(User)\
1261 return Session.query(User)\
1261 .filter(cls.revision == revision)\
1262 .filter(cls.revision == revision)\
1262 .join(ChangesetComment.author).all()
1263 .join(ChangesetComment.author).all()
1263
1264
1264
1265
1265 class Notification(Base, BaseModel):
1266 class Notification(Base, BaseModel):
1266 __tablename__ = 'notifications'
1267 __tablename__ = 'notifications'
1267 __table_args__ = (
1268 __table_args__ = (
1268 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1269 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1269 'mysql_charset': 'utf8'},
1270 'mysql_charset': 'utf8'},
1270 )
1271 )
1271
1272
1272 TYPE_CHANGESET_COMMENT = u'cs_comment'
1273 TYPE_CHANGESET_COMMENT = u'cs_comment'
1273 TYPE_MESSAGE = u'message'
1274 TYPE_MESSAGE = u'message'
1274 TYPE_MENTION = u'mention'
1275 TYPE_MENTION = u'mention'
1275 TYPE_REGISTRATION = u'registration'
1276 TYPE_REGISTRATION = u'registration'
1276
1277
1277 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1278 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1278 subject = Column('subject', Unicode(512), nullable=True)
1279 subject = Column('subject', Unicode(512), nullable=True)
1279 body = Column('body', Unicode(50000), nullable=True)
1280 body = Column('body', Unicode(50000), nullable=True)
1280 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1281 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1281 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1282 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1282 type_ = Column('type', Unicode(256))
1283 type_ = Column('type', Unicode(256))
1283
1284
1284 created_by_user = relationship('User')
1285 created_by_user = relationship('User')
1285 notifications_to_users = relationship('UserNotification', lazy='joined',
1286 notifications_to_users = relationship('UserNotification', lazy='joined',
1286 cascade="all, delete, delete-orphan")
1287 cascade="all, delete, delete-orphan")
1287
1288
1288 @property
1289 @property
1289 def recipients(self):
1290 def recipients(self):
1290 return [x.user for x in UserNotification.query()\
1291 return [x.user for x in UserNotification.query()\
1291 .filter(UserNotification.notification == self)\
1292 .filter(UserNotification.notification == self)\
1292 .order_by(UserNotification.user).all()]
1293 .order_by(UserNotification.user).all()]
1293
1294
1294 @classmethod
1295 @classmethod
1295 def create(cls, created_by, subject, body, recipients, type_=None):
1296 def create(cls, created_by, subject, body, recipients, type_=None):
1296 if type_ is None:
1297 if type_ is None:
1297 type_ = Notification.TYPE_MESSAGE
1298 type_ = Notification.TYPE_MESSAGE
1298
1299
1299 notification = cls()
1300 notification = cls()
1300 notification.created_by_user = created_by
1301 notification.created_by_user = created_by
1301 notification.subject = subject
1302 notification.subject = subject
1302 notification.body = body
1303 notification.body = body
1303 notification.type_ = type_
1304 notification.type_ = type_
1304 notification.created_on = datetime.datetime.now()
1305 notification.created_on = datetime.datetime.now()
1305
1306
1306 for u in recipients:
1307 for u in recipients:
1307 assoc = UserNotification()
1308 assoc = UserNotification()
1308 assoc.notification = notification
1309 assoc.notification = notification
1309 u.notifications.append(assoc)
1310 u.notifications.append(assoc)
1310 Session.add(notification)
1311 Session.add(notification)
1311 return notification
1312 return notification
1312
1313
1313 @property
1314 @property
1314 def description(self):
1315 def description(self):
1315 from rhodecode.model.notification import NotificationModel
1316 from rhodecode.model.notification import NotificationModel
1316 return NotificationModel().make_description(self)
1317 return NotificationModel().make_description(self)
1317
1318
1318
1319
1319 class UserNotification(Base, BaseModel):
1320 class UserNotification(Base, BaseModel):
1320 __tablename__ = 'user_to_notification'
1321 __tablename__ = 'user_to_notification'
1321 __table_args__ = (
1322 __table_args__ = (
1322 UniqueConstraint('user_id', 'notification_id'),
1323 UniqueConstraint('user_id', 'notification_id'),
1323 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1324 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1324 'mysql_charset': 'utf8'}
1325 'mysql_charset': 'utf8'}
1325 )
1326 )
1326 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1327 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1327 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1328 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1328 read = Column('read', Boolean, default=False)
1329 read = Column('read', Boolean, default=False)
1329 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1330 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1330
1331
1331 user = relationship('User', lazy="joined")
1332 user = relationship('User', lazy="joined")
1332 notification = relationship('Notification', lazy="joined",
1333 notification = relationship('Notification', lazy="joined",
1333 order_by=lambda: Notification.created_on.desc(),)
1334 order_by=lambda: Notification.created_on.desc(),)
1334
1335
1335 def mark_as_read(self):
1336 def mark_as_read(self):
1336 self.read = True
1337 self.read = True
1337 Session.add(self)
1338 Session.add(self)
1338
1339
1339
1340
1340 class DbMigrateVersion(Base, BaseModel):
1341 class DbMigrateVersion(Base, BaseModel):
1341 __tablename__ = 'db_migrate_version'
1342 __tablename__ = 'db_migrate_version'
1342 __table_args__ = (
1343 __table_args__ = (
1343 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1344 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1344 'mysql_charset': 'utf8'},
1345 'mysql_charset': 'utf8'},
1345 )
1346 )
1346 repository_id = Column('repository_id', String(250), primary_key=True)
1347 repository_id = Column('repository_id', String(250), primary_key=True)
1347 repository_path = Column('repository_path', Text)
1348 repository_path = Column('repository_path', Text)
1348 version = Column('version', Integer)
1349 version = Column('version', Integer)
@@ -1,773 +1,774 b''
1 """ this is forms validation classes
1 """ this is forms validation classes
2 http://formencode.org/module-formencode.validators.html
2 http://formencode.org/module-formencode.validators.html
3 for list off all availible validators
3 for list off all availible validators
4
4
5 we can create our own validators
5 we can create our own validators
6
6
7 The table below outlines the options which can be used in a schema in addition to the validators themselves
7 The table below outlines the options which can be used in a schema in addition to the validators themselves
8 pre_validators [] These validators will be applied before the schema
8 pre_validators [] These validators will be applied before the schema
9 chained_validators [] These validators will be applied after the schema
9 chained_validators [] These validators will be applied after the schema
10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
14
14
15
15
16 <name> = formencode.validators.<name of validator>
16 <name> = formencode.validators.<name of validator>
17 <name> must equal form name
17 <name> must equal form name
18 list=[1,2,3,4,5]
18 list=[1,2,3,4,5]
19 for SELECT use formencode.All(OneOf(list), Int())
19 for SELECT use formencode.All(OneOf(list), Int())
20
20
21 """
21 """
22 import os
22 import os
23 import re
23 import re
24 import logging
24 import logging
25 import traceback
25 import traceback
26
26
27 import formencode
27 import formencode
28 from formencode import All
28 from formencode import All
29 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
29 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
30 Email, Bool, StringBoolean, Set
30 Email, Bool, StringBoolean, Set
31
31
32 from pylons.i18n.translation import _
32 from pylons.i18n.translation import _
33 from webhelpers.pylonslib.secure_form import authentication_token
33 from webhelpers.pylonslib.secure_form import authentication_token
34
34
35 from rhodecode.config.routing import ADMIN_PREFIX
35 from rhodecode.config.routing import ADMIN_PREFIX
36 from rhodecode.lib.utils import repo_name_slug
36 from rhodecode.lib.utils import repo_name_slug
37 from rhodecode.lib.auth import authenticate, get_crypt_password
37 from rhodecode.lib.auth import authenticate, get_crypt_password
38 from rhodecode.lib.exceptions import LdapImportError
38 from rhodecode.lib.exceptions import LdapImportError
39 from rhodecode.model.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=[]):
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=True)
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
670
670 if edit:
671 if edit:
671 #this is repo owner
672 #this is repo owner
672 user = All(UnicodeString(not_empty=True), ValidRepoUser)
673 user = All(UnicodeString(not_empty=True), ValidRepoUser)
673
674
674 chained_validators = [ValidCloneUri()(),
675 chained_validators = [ValidCloneUri()(),
675 ValidRepoName(edit, old_data),
676 ValidRepoName(edit, old_data),
676 ValidPerms()]
677 ValidPerms()]
677 return _RepoForm
678 return _RepoForm
678
679
679
680
680 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
681 def RepoForkForm(edit=False, old_data={},
681 repo_groups=[]):
682 supported_backends=BACKENDS.keys(), repo_groups=[]):
682 class _RepoForkForm(formencode.Schema):
683 class _RepoForkForm(formencode.Schema):
683 allow_extra_fields = True
684 allow_extra_fields = True
684 filter_extra_fields = False
685 filter_extra_fields = False
685 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
686 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
686 SlugifyName())
687 SlugifyName())
687 repo_group = OneOf(repo_groups, hideList=True)
688 repo_group = OneOf(repo_groups, hideList=True)
688 repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
689 repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
689 description = UnicodeString(strip=True, min=1, not_empty=True)
690 description = UnicodeString(strip=True, min=1, not_empty=True)
690 private = StringBoolean(if_missing=False)
691 private = StringBoolean(if_missing=False)
691 copy_permissions = StringBoolean(if_missing=False)
692 copy_permissions = StringBoolean(if_missing=False)
692 update_after_clone = StringBoolean(if_missing=False)
693 update_after_clone = StringBoolean(if_missing=False)
693 fork_parent_id = UnicodeString()
694 fork_parent_id = UnicodeString()
694 chained_validators = [ValidForkName(edit, old_data)]
695 chained_validators = [ValidForkName(edit, old_data)]
695
696
696 return _RepoForkForm
697 return _RepoForkForm
697
698
698
699
699 def RepoSettingsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
700 def RepoSettingsForm(edit=False, old_data={},
700 repo_groups=[]):
701 supported_backends=BACKENDS.keys(), repo_groups=[]):
701 class _RepoForm(formencode.Schema):
702 class _RepoForm(formencode.Schema):
702 allow_extra_fields = True
703 allow_extra_fields = True
703 filter_extra_fields = False
704 filter_extra_fields = False
704 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
705 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
705 SlugifyName())
706 SlugifyName())
706 description = UnicodeString(strip=True, min=1, not_empty=True)
707 description = UnicodeString(strip=True, min=1, not_empty=True)
707 repo_group = OneOf(repo_groups, hideList=True)
708 repo_group = OneOf(repo_groups, hideList=True)
708 private = StringBoolean(if_missing=False)
709 private = StringBoolean(if_missing=False)
709
710 landing_rev = UnicodeString(strip=True, min=1, not_empty=True)
710 chained_validators = [ValidRepoName(edit, old_data), ValidPerms(),
711 chained_validators = [ValidRepoName(edit, old_data), ValidPerms(),
711 ValidSettings]
712 ValidSettings]
712 return _RepoForm
713 return _RepoForm
713
714
714
715
715 def ApplicationSettingsForm():
716 def ApplicationSettingsForm():
716 class _ApplicationSettingsForm(formencode.Schema):
717 class _ApplicationSettingsForm(formencode.Schema):
717 allow_extra_fields = True
718 allow_extra_fields = True
718 filter_extra_fields = False
719 filter_extra_fields = False
719 rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
720 rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
720 rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
721 rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
721 rhodecode_ga_code = UnicodeString(strip=True, min=1, not_empty=False)
722 rhodecode_ga_code = UnicodeString(strip=True, min=1, not_empty=False)
722
723
723 return _ApplicationSettingsForm
724 return _ApplicationSettingsForm
724
725
725
726
726 def ApplicationUiSettingsForm():
727 def ApplicationUiSettingsForm():
727 class _ApplicationUiSettingsForm(formencode.Schema):
728 class _ApplicationUiSettingsForm(formencode.Schema):
728 allow_extra_fields = True
729 allow_extra_fields = True
729 filter_extra_fields = False
730 filter_extra_fields = False
730 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
731 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
731 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))
732 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
733 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
733 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
734 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
734 hooks_changegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
735 hooks_changegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
735 hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
736 hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
736
737
737 return _ApplicationUiSettingsForm
738 return _ApplicationUiSettingsForm
738
739
739
740
740 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
741 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
741 class _DefaultPermissionsForm(formencode.Schema):
742 class _DefaultPermissionsForm(formencode.Schema):
742 allow_extra_fields = True
743 allow_extra_fields = True
743 filter_extra_fields = True
744 filter_extra_fields = True
744 overwrite_default = StringBoolean(if_missing=False)
745 overwrite_default = StringBoolean(if_missing=False)
745 anonymous = OneOf(['True', 'False'], if_missing=False)
746 anonymous = OneOf(['True', 'False'], if_missing=False)
746 default_perm = OneOf(perms_choices)
747 default_perm = OneOf(perms_choices)
747 default_register = OneOf(register_choices)
748 default_register = OneOf(register_choices)
748 default_create = OneOf(create_choices)
749 default_create = OneOf(create_choices)
749
750
750 return _DefaultPermissionsForm
751 return _DefaultPermissionsForm
751
752
752
753
753 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices, tls_kind_choices):
754 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices, tls_kind_choices):
754 class _LdapSettingsForm(formencode.Schema):
755 class _LdapSettingsForm(formencode.Schema):
755 allow_extra_fields = True
756 allow_extra_fields = True
756 filter_extra_fields = True
757 filter_extra_fields = True
757 #pre_validators = [LdapLibValidator]
758 #pre_validators = [LdapLibValidator]
758 ldap_active = StringBoolean(if_missing=False)
759 ldap_active = StringBoolean(if_missing=False)
759 ldap_host = UnicodeString(strip=True,)
760 ldap_host = UnicodeString(strip=True,)
760 ldap_port = Number(strip=True,)
761 ldap_port = Number(strip=True,)
761 ldap_tls_kind = OneOf(tls_kind_choices)
762 ldap_tls_kind = OneOf(tls_kind_choices)
762 ldap_tls_reqcert = OneOf(tls_reqcert_choices)
763 ldap_tls_reqcert = OneOf(tls_reqcert_choices)
763 ldap_dn_user = UnicodeString(strip=True,)
764 ldap_dn_user = UnicodeString(strip=True,)
764 ldap_dn_pass = UnicodeString(strip=True,)
765 ldap_dn_pass = UnicodeString(strip=True,)
765 ldap_base_dn = UnicodeString(strip=True,)
766 ldap_base_dn = UnicodeString(strip=True,)
766 ldap_filter = UnicodeString(strip=True,)
767 ldap_filter = UnicodeString(strip=True,)
767 ldap_search_scope = OneOf(search_scope_choices)
768 ldap_search_scope = OneOf(search_scope_choices)
768 ldap_attr_login = All(AttrLoginValidator, UnicodeString(strip=True,))
769 ldap_attr_login = All(AttrLoginValidator, UnicodeString(strip=True,))
769 ldap_attr_firstname = UnicodeString(strip=True,)
770 ldap_attr_firstname = UnicodeString(strip=True,)
770 ldap_attr_lastname = UnicodeString(strip=True,)
771 ldap_attr_lastname = UnicodeString(strip=True,)
771 ldap_attr_email = UnicodeString(strip=True,)
772 ldap_attr_email = UnicodeString(strip=True,)
772
773
773 return _LdapSettingsForm
774 return _LdapSettingsForm
@@ -1,474 +1,505 b''
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
33
33 from rhodecode.lib.vcs import get_backend
34 from rhodecode.lib.vcs import get_backend
34 from rhodecode.lib.vcs.exceptions import RepositoryError
35 from rhodecode.lib.vcs.exceptions import RepositoryError
35 from rhodecode.lib.vcs.utils.lazy import LazyProperty
36 from rhodecode.lib.vcs.utils.lazy import LazyProperty
36 from rhodecode.lib.vcs.nodes import FileNode
37 from rhodecode.lib.vcs.nodes import FileNode
37
38
38 from rhodecode import BACKENDS
39 from rhodecode import BACKENDS
39 from rhodecode.lib import helpers as h
40 from rhodecode.lib import helpers as h
40 from rhodecode.lib.utils2 import safe_str, safe_unicode
41 from rhodecode.lib.utils2 import safe_str, safe_unicode
41 from rhodecode.lib.auth import HasRepoPermissionAny, HasReposGroupPermissionAny
42 from rhodecode.lib.auth import HasRepoPermissionAny, HasReposGroupPermissionAny
42 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, \
43 action_logger, EmptyChangeset, REMOVED_REPO_PAT
44 action_logger, EmptyChangeset, REMOVED_REPO_PAT
44 from rhodecode.model import BaseModel
45 from rhodecode.model import BaseModel
45 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
46 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
46 UserFollowing, UserLog, User, RepoGroup
47 UserFollowing, UserLog, User, RepoGroup
47
48
48 log = logging.getLogger(__name__)
49 log = logging.getLogger(__name__)
49
50
50
51
51 class UserTemp(object):
52 class UserTemp(object):
52 def __init__(self, user_id):
53 def __init__(self, user_id):
53 self.user_id = user_id
54 self.user_id = user_id
54
55
55 def __repr__(self):
56 def __repr__(self):
56 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
57 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
57
58
58
59
59 class RepoTemp(object):
60 class RepoTemp(object):
60 def __init__(self, repo_id):
61 def __init__(self, repo_id):
61 self.repo_id = repo_id
62 self.repo_id = repo_id
62
63
63 def __repr__(self):
64 def __repr__(self):
64 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
65 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
65
66
66
67
67 class CachedRepoList(object):
68 class CachedRepoList(object):
68
69
69 def __init__(self, db_repo_list, repos_path, order_by=None):
70 def __init__(self, db_repo_list, repos_path, order_by=None):
70 self.db_repo_list = db_repo_list
71 self.db_repo_list = db_repo_list
71 self.repos_path = repos_path
72 self.repos_path = repos_path
72 self.order_by = order_by
73 self.order_by = order_by
73 self.reversed = (order_by or '').startswith('-')
74 self.reversed = (order_by or '').startswith('-')
74
75
75 def __len__(self):
76 def __len__(self):
76 return len(self.db_repo_list)
77 return len(self.db_repo_list)
77
78
78 def __repr__(self):
79 def __repr__(self):
79 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
80 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
80
81
81 def __iter__(self):
82 def __iter__(self):
82 # pre-propagated cache_map to save executing select statements
83 # pre-propagated cache_map to save executing select statements
83 # for each repo
84 # for each repo
84 cache_map = CacheInvalidation.get_cache_map()
85 cache_map = CacheInvalidation.get_cache_map()
85
86
86 for dbr in self.db_repo_list:
87 for dbr in self.db_repo_list:
87 scmr = dbr.scm_instance_cached(cache_map)
88 scmr = dbr.scm_instance_cached(cache_map)
88 # check permission at this level
89 # check permission at this level
89 if not HasRepoPermissionAny(
90 if not HasRepoPermissionAny(
90 'repository.read', 'repository.write', 'repository.admin'
91 'repository.read', 'repository.write', 'repository.admin'
91 )(dbr.repo_name, 'get repo check'):
92 )(dbr.repo_name, 'get repo check'):
92 continue
93 continue
93
94
94 if scmr is None:
95 if scmr is None:
95 log.error(
96 log.error(
96 '%s this repository is present in database but it '
97 '%s this repository is present in database but it '
97 'cannot be created as an scm instance' % dbr.repo_name
98 'cannot be created as an scm instance' % dbr.repo_name
98 )
99 )
99 continue
100 continue
100
101
101 last_change = scmr.last_change
102 last_change = scmr.last_change
102 tip = h.get_changeset_safe(scmr, 'tip')
103 tip = h.get_changeset_safe(scmr, 'tip')
103
104
104 tmp_d = {}
105 tmp_d = {}
105 tmp_d['name'] = dbr.repo_name
106 tmp_d['name'] = dbr.repo_name
106 tmp_d['name_sort'] = tmp_d['name'].lower()
107 tmp_d['name_sort'] = tmp_d['name'].lower()
107 tmp_d['description'] = dbr.description
108 tmp_d['description'] = dbr.description
108 tmp_d['description_sort'] = tmp_d['description']
109 tmp_d['description_sort'] = tmp_d['description']
109 tmp_d['last_change'] = last_change
110 tmp_d['last_change'] = last_change
110 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
111 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
111 tmp_d['tip'] = tip.raw_id
112 tmp_d['tip'] = tip.raw_id
112 tmp_d['tip_sort'] = tip.revision
113 tmp_d['tip_sort'] = tip.revision
113 tmp_d['rev'] = tip.revision
114 tmp_d['rev'] = tip.revision
114 tmp_d['contact'] = dbr.user.full_contact
115 tmp_d['contact'] = dbr.user.full_contact
115 tmp_d['contact_sort'] = tmp_d['contact']
116 tmp_d['contact_sort'] = tmp_d['contact']
116 tmp_d['owner_sort'] = tmp_d['contact']
117 tmp_d['owner_sort'] = tmp_d['contact']
117 tmp_d['repo_archives'] = list(scmr._get_archives())
118 tmp_d['repo_archives'] = list(scmr._get_archives())
118 tmp_d['last_msg'] = tip.message
119 tmp_d['last_msg'] = tip.message
119 tmp_d['author'] = tip.author
120 tmp_d['author'] = tip.author
120 tmp_d['dbrepo'] = dbr.get_dict()
121 tmp_d['dbrepo'] = dbr.get_dict()
121 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 {}
122 yield tmp_d
123 yield tmp_d
123
124
124
125
125 class GroupList(object):
126 class GroupList(object):
126
127
127 def __init__(self, db_repo_group_list):
128 def __init__(self, db_repo_group_list):
128 self.db_repo_group_list = db_repo_group_list
129 self.db_repo_group_list = db_repo_group_list
129
130
130 def __len__(self):
131 def __len__(self):
131 return len(self.db_repo_group_list)
132 return len(self.db_repo_group_list)
132
133
133 def __repr__(self):
134 def __repr__(self):
134 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
135 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
135
136
136 def __iter__(self):
137 def __iter__(self):
137 for dbgr in self.db_repo_group_list:
138 for dbgr in self.db_repo_group_list:
138 # check permission at this level
139 # check permission at this level
139 if not HasReposGroupPermissionAny(
140 if not HasReposGroupPermissionAny(
140 'group.read', 'group.write', 'group.admin'
141 'group.read', 'group.write', 'group.admin'
141 )(dbgr.group_name, 'get group repo check'):
142 )(dbgr.group_name, 'get group repo check'):
142 continue
143 continue
143
144
144 yield dbgr
145 yield dbgr
145
146
146
147
147 class ScmModel(BaseModel):
148 class ScmModel(BaseModel):
148 """
149 """
149 Generic Scm Model
150 Generic Scm Model
150 """
151 """
151
152
152 def __get_repo(self, instance):
153 def __get_repo(self, instance):
153 cls = Repository
154 cls = Repository
154 if isinstance(instance, cls):
155 if isinstance(instance, cls):
155 return instance
156 return instance
156 elif isinstance(instance, int) or str(instance).isdigit():
157 elif isinstance(instance, int) or str(instance).isdigit():
157 return cls.get(instance)
158 return cls.get(instance)
158 elif isinstance(instance, basestring):
159 elif isinstance(instance, basestring):
159 return cls.get_by_repo_name(instance)
160 return cls.get_by_repo_name(instance)
160 elif instance:
161 elif instance:
161 raise Exception('given object must be int, basestr or Instance'
162 raise Exception('given object must be int, basestr or Instance'
162 ' of %s got %s' % (type(cls), type(instance)))
163 ' of %s got %s' % (type(cls), type(instance)))
163
164
164 @LazyProperty
165 @LazyProperty
165 def repos_path(self):
166 def repos_path(self):
166 """
167 """
167 Get's the repositories root path from database
168 Get's the repositories root path from database
168 """
169 """
169
170
170 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
171 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
171
172
172 return q.ui_value
173 return q.ui_value
173
174
174 def repo_scan(self, repos_path=None):
175 def repo_scan(self, repos_path=None):
175 """
176 """
176 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
177 repository itself. Return a dictionary of repository objects
178 repository itself. Return a dictionary of repository objects
178
179
179 :param repos_path: path to directory containing repositories
180 :param repos_path: path to directory containing repositories
180 """
181 """
181
182
182 if repos_path is None:
183 if repos_path is None:
183 repos_path = self.repos_path
184 repos_path = self.repos_path
184
185
185 log.info('scanning for repositories in %s' % repos_path)
186 log.info('scanning for repositories in %s' % repos_path)
186
187
187 baseui = make_ui('db')
188 baseui = make_ui('db')
188 repos = {}
189 repos = {}
189
190
190 for name, path in get_filesystem_repos(repos_path, recursive=True):
191 for name, path in get_filesystem_repos(repos_path, recursive=True):
191 # skip removed repos
192 # skip removed repos
192 if REMOVED_REPO_PAT.match(name):
193 if REMOVED_REPO_PAT.match(name):
193 continue
194 continue
194
195
195 # name need to be decomposed and put back together using the /
196 # name need to be decomposed and put back together using the /
196 # since this is internal storage separator for rhodecode
197 # since this is internal storage separator for rhodecode
197 name = Repository.url_sep().join(name.split(os.sep))
198 name = Repository.url_sep().join(name.split(os.sep))
198
199
199 try:
200 try:
200 if name in repos:
201 if name in repos:
201 raise RepositoryError('Duplicate repository name %s '
202 raise RepositoryError('Duplicate repository name %s '
202 'found in %s' % (name, path))
203 'found in %s' % (name, path))
203 else:
204 else:
204
205
205 klass = get_backend(path[0])
206 klass = get_backend(path[0])
206
207
207 if path[0] == 'hg' and path[0] in BACKENDS.keys():
208 if path[0] == 'hg' and path[0] in BACKENDS.keys():
208 repos[name] = klass(safe_str(path[1]), baseui=baseui)
209 repos[name] = klass(safe_str(path[1]), baseui=baseui)
209
210
210 if path[0] == 'git' and path[0] in BACKENDS.keys():
211 if path[0] == 'git' and path[0] in BACKENDS.keys():
211 repos[name] = klass(path[1])
212 repos[name] = klass(path[1])
212 except OSError:
213 except OSError:
213 continue
214 continue
214
215
215 return repos
216 return repos
216
217
217 def get_repos(self, all_repos=None, sort_key=None):
218 def get_repos(self, all_repos=None, sort_key=None):
218 """
219 """
219 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
220 backend instance and fill that backed with information from database
221 backend instance and fill that backed with information from database
221
222
222 :param all_repos: list of repository names as strings
223 :param all_repos: list of repository names as strings
223 give specific repositories list, good for filtering
224 give specific repositories list, good for filtering
224 """
225 """
225 if all_repos is None:
226 if all_repos is None:
226 all_repos = self.sa.query(Repository)\
227 all_repos = self.sa.query(Repository)\
227 .filter(Repository.group_id == None)\
228 .filter(Repository.group_id == None)\
228 .order_by(func.lower(Repository.repo_name)).all()
229 .order_by(func.lower(Repository.repo_name)).all()
229
230
230 repo_iter = CachedRepoList(all_repos, repos_path=self.repos_path,
231 repo_iter = CachedRepoList(all_repos, repos_path=self.repos_path,
231 order_by=sort_key)
232 order_by=sort_key)
232
233
233 return repo_iter
234 return repo_iter
234
235
235 def get_repos_groups(self, all_groups=None):
236 def get_repos_groups(self, all_groups=None):
236 if all_groups is None:
237 if all_groups is None:
237 all_groups = RepoGroup.query()\
238 all_groups = RepoGroup.query()\
238 .filter(RepoGroup.group_parent_id == None).all()
239 .filter(RepoGroup.group_parent_id == None).all()
239 group_iter = GroupList(all_groups)
240 group_iter = GroupList(all_groups)
240
241
241 return group_iter
242 return group_iter
242
243
243 def mark_for_invalidation(self, repo_name):
244 def mark_for_invalidation(self, repo_name):
244 """
245 """
245 Puts cache invalidation task into db for
246 Puts cache invalidation task into db for
246 further global cache invalidation
247 further global cache invalidation
247
248
248 :param repo_name: this repo that should invalidation take place
249 :param repo_name: this repo that should invalidation take place
249 """
250 """
250 CacheInvalidation.set_invalidate(repo_name)
251 CacheInvalidation.set_invalidate(repo_name)
251
252
252 def toggle_following_repo(self, follow_repo_id, user_id):
253 def toggle_following_repo(self, follow_repo_id, user_id):
253
254
254 f = self.sa.query(UserFollowing)\
255 f = self.sa.query(UserFollowing)\
255 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
256 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
256 .filter(UserFollowing.user_id == user_id).scalar()
257 .filter(UserFollowing.user_id == user_id).scalar()
257
258
258 if f is not None:
259 if f is not None:
259 try:
260 try:
260 self.sa.delete(f)
261 self.sa.delete(f)
261 action_logger(UserTemp(user_id),
262 action_logger(UserTemp(user_id),
262 'stopped_following_repo',
263 'stopped_following_repo',
263 RepoTemp(follow_repo_id))
264 RepoTemp(follow_repo_id))
264 return
265 return
265 except:
266 except:
266 log.error(traceback.format_exc())
267 log.error(traceback.format_exc())
267 raise
268 raise
268
269
269 try:
270 try:
270 f = UserFollowing()
271 f = UserFollowing()
271 f.user_id = user_id
272 f.user_id = user_id
272 f.follows_repo_id = follow_repo_id
273 f.follows_repo_id = follow_repo_id
273 self.sa.add(f)
274 self.sa.add(f)
274
275
275 action_logger(UserTemp(user_id),
276 action_logger(UserTemp(user_id),
276 'started_following_repo',
277 'started_following_repo',
277 RepoTemp(follow_repo_id))
278 RepoTemp(follow_repo_id))
278 except:
279 except:
279 log.error(traceback.format_exc())
280 log.error(traceback.format_exc())
280 raise
281 raise
281
282
282 def toggle_following_user(self, follow_user_id, user_id):
283 def toggle_following_user(self, follow_user_id, user_id):
283 f = self.sa.query(UserFollowing)\
284 f = self.sa.query(UserFollowing)\
284 .filter(UserFollowing.follows_user_id == follow_user_id)\
285 .filter(UserFollowing.follows_user_id == follow_user_id)\
285 .filter(UserFollowing.user_id == user_id).scalar()
286 .filter(UserFollowing.user_id == user_id).scalar()
286
287
287 if f is not None:
288 if f is not None:
288 try:
289 try:
289 self.sa.delete(f)
290 self.sa.delete(f)
290 return
291 return
291 except:
292 except:
292 log.error(traceback.format_exc())
293 log.error(traceback.format_exc())
293 raise
294 raise
294
295
295 try:
296 try:
296 f = UserFollowing()
297 f = UserFollowing()
297 f.user_id = user_id
298 f.user_id = user_id
298 f.follows_user_id = follow_user_id
299 f.follows_user_id = follow_user_id
299 self.sa.add(f)
300 self.sa.add(f)
300 except:
301 except:
301 log.error(traceback.format_exc())
302 log.error(traceback.format_exc())
302 raise
303 raise
303
304
304 def is_following_repo(self, repo_name, user_id, cache=False):
305 def is_following_repo(self, repo_name, user_id, cache=False):
305 r = self.sa.query(Repository)\
306 r = self.sa.query(Repository)\
306 .filter(Repository.repo_name == repo_name).scalar()
307 .filter(Repository.repo_name == repo_name).scalar()
307
308
308 f = self.sa.query(UserFollowing)\
309 f = self.sa.query(UserFollowing)\
309 .filter(UserFollowing.follows_repository == r)\
310 .filter(UserFollowing.follows_repository == r)\
310 .filter(UserFollowing.user_id == user_id).scalar()
311 .filter(UserFollowing.user_id == user_id).scalar()
311
312
312 return f is not None
313 return f is not None
313
314
314 def is_following_user(self, username, user_id, cache=False):
315 def is_following_user(self, username, user_id, cache=False):
315 u = User.get_by_username(username)
316 u = User.get_by_username(username)
316
317
317 f = self.sa.query(UserFollowing)\
318 f = self.sa.query(UserFollowing)\
318 .filter(UserFollowing.follows_user == u)\
319 .filter(UserFollowing.follows_user == u)\
319 .filter(UserFollowing.user_id == user_id).scalar()
320 .filter(UserFollowing.user_id == user_id).scalar()
320
321
321 return f is not None
322 return f is not None
322
323
323 def get_followers(self, repo_id):
324 def get_followers(self, repo_id):
324 if not isinstance(repo_id, int):
325 if not isinstance(repo_id, int):
325 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')
326
327
327 return self.sa.query(UserFollowing)\
328 return self.sa.query(UserFollowing)\
328 .filter(UserFollowing.follows_repo_id == repo_id).count()
329 .filter(UserFollowing.follows_repo_id == repo_id).count()
329
330
330 def get_forks(self, repo_id):
331 def get_forks(self, repo_id):
331 if not isinstance(repo_id, int):
332 if not isinstance(repo_id, int):
332 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')
333
334
334 return self.sa.query(Repository)\
335 return self.sa.query(Repository)\
335 .filter(Repository.fork_id == repo_id).count()
336 .filter(Repository.fork_id == repo_id).count()
336
337
337 def mark_as_fork(self, repo, fork, user):
338 def mark_as_fork(self, repo, fork, user):
338 repo = self.__get_repo(repo)
339 repo = self.__get_repo(repo)
339 fork = self.__get_repo(fork)
340 fork = self.__get_repo(fork)
340 repo.fork = fork
341 repo.fork = fork
341 self.sa.add(repo)
342 self.sa.add(repo)
342 return repo
343 return repo
343
344
344 def pull_changes(self, repo_name, username):
345 def pull_changes(self, repo_name, username):
345 dbrepo = Repository.get_by_repo_name(repo_name)
346 dbrepo = Repository.get_by_repo_name(repo_name)
346 clone_uri = dbrepo.clone_uri
347 clone_uri = dbrepo.clone_uri
347 if not clone_uri:
348 if not clone_uri:
348 raise Exception("This repository doesn't have a clone uri")
349 raise Exception("This repository doesn't have a clone uri")
349
350
350 repo = dbrepo.scm_instance
351 repo = dbrepo.scm_instance
351 try:
352 try:
352 extras = {
353 extras = {
353 'ip': '',
354 'ip': '',
354 'username': username,
355 'username': username,
355 'action': 'push_remote',
356 'action': 'push_remote',
356 'repository': repo_name,
357 'repository': repo_name,
357 'scm': repo.alias,
358 'scm': repo.alias,
358 }
359 }
359
360
360 # inject ui extra param to log this action via push logger
361 # inject ui extra param to log this action via push logger
361 for k, v in extras.items():
362 for k, v in extras.items():
362 repo._repo.ui.setconfig('rhodecode_extras', k, v)
363 repo._repo.ui.setconfig('rhodecode_extras', k, v)
363 if repo.alias == 'git':
364 if repo.alias == 'git':
364 repo.fetch(clone_uri)
365 repo.fetch(clone_uri)
365 else:
366 else:
366 repo.pull(clone_uri)
367 repo.pull(clone_uri)
367 self.mark_for_invalidation(repo_name)
368 self.mark_for_invalidation(repo_name)
368 except:
369 except:
369 log.error(traceback.format_exc())
370 log.error(traceback.format_exc())
370 raise
371 raise
371
372
372 def commit_change(self, repo, repo_name, cs, user, author, message,
373 def commit_change(self, repo, repo_name, cs, user, author, message,
373 content, f_path):
374 content, f_path):
374
375
375 if repo.alias == 'hg':
376 if repo.alias == 'hg':
376 from rhodecode.lib.vcs.backends.hg import \
377 from rhodecode.lib.vcs.backends.hg import \
377 MercurialInMemoryChangeset as IMC
378 MercurialInMemoryChangeset as IMC
378 elif repo.alias == 'git':
379 elif repo.alias == 'git':
379 from rhodecode.lib.vcs.backends.git import \
380 from rhodecode.lib.vcs.backends.git import \
380 GitInMemoryChangeset as IMC
381 GitInMemoryChangeset as IMC
381
382
382 # decoding here will force that we have proper encoded values
383 # decoding here will force that we have proper encoded values
383 # in any other case this will throw exceptions and deny commit
384 # in any other case this will throw exceptions and deny commit
384 content = safe_str(content)
385 content = safe_str(content)
385 path = safe_str(f_path)
386 path = safe_str(f_path)
386 # message and author needs to be unicode
387 # message and author needs to be unicode
387 # proper backend should then translate that into required type
388 # proper backend should then translate that into required type
388 message = safe_unicode(message)
389 message = safe_unicode(message)
389 author = safe_unicode(author)
390 author = safe_unicode(author)
390 m = IMC(repo)
391 m = IMC(repo)
391 m.change(FileNode(path, content))
392 m.change(FileNode(path, content))
392 tip = m.commit(message=message,
393 tip = m.commit(message=message,
393 author=author,
394 author=author,
394 parents=[cs], branch=cs.branch)
395 parents=[cs], branch=cs.branch)
395
396
396 new_cs = tip.short_id
397 new_cs = tip.short_id
397 action = 'push_local:%s' % new_cs
398 action = 'push_local:%s' % new_cs
398
399
399 action_logger(user, action, repo_name)
400 action_logger(user, action, repo_name)
400
401
401 self.mark_for_invalidation(repo_name)
402 self.mark_for_invalidation(repo_name)
402
403
403 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,
404 f_path):
405 f_path):
405 if repo.alias == 'hg':
406 if repo.alias == 'hg':
406 from rhodecode.lib.vcs.backends.hg import MercurialInMemoryChangeset as IMC
407 from rhodecode.lib.vcs.backends.hg import MercurialInMemoryChangeset as IMC
407 elif repo.alias == 'git':
408 elif repo.alias == 'git':
408 from rhodecode.lib.vcs.backends.git import GitInMemoryChangeset as IMC
409 from rhodecode.lib.vcs.backends.git import GitInMemoryChangeset as IMC
409 # decoding here will force that we have proper encoded values
410 # decoding here will force that we have proper encoded values
410 # in any other case this will throw exceptions and deny commit
411 # in any other case this will throw exceptions and deny commit
411
412
412 if isinstance(content, (basestring,)):
413 if isinstance(content, (basestring,)):
413 content = safe_str(content)
414 content = safe_str(content)
414 elif isinstance(content, (file, cStringIO.OutputType,)):
415 elif isinstance(content, (file, cStringIO.OutputType,)):
415 content = content.read()
416 content = content.read()
416 else:
417 else:
417 raise Exception('Content is of unrecognized type %s' % (
418 raise Exception('Content is of unrecognized type %s' % (
418 type(content)
419 type(content)
419 ))
420 ))
420
421
421 message = safe_unicode(message)
422 message = safe_unicode(message)
422 author = safe_unicode(author)
423 author = safe_unicode(author)
423 path = safe_str(f_path)
424 path = safe_str(f_path)
424 m = IMC(repo)
425 m = IMC(repo)
425
426
426 if isinstance(cs, EmptyChangeset):
427 if isinstance(cs, EmptyChangeset):
427 # EmptyChangeset means we we're editing empty repository
428 # EmptyChangeset means we we're editing empty repository
428 parents = None
429 parents = None
429 else:
430 else:
430 parents = [cs]
431 parents = [cs]
431
432
432 m.add(FileNode(path, content=content))
433 m.add(FileNode(path, content=content))
433 tip = m.commit(message=message,
434 tip = m.commit(message=message,
434 author=author,
435 author=author,
435 parents=parents, branch=cs.branch)
436 parents=parents, branch=cs.branch)
436 new_cs = tip.short_id
437 new_cs = tip.short_id
437 action = 'push_local:%s' % new_cs
438 action = 'push_local:%s' % new_cs
438
439
439 action_logger(user, action, repo_name)
440 action_logger(user, action, repo_name)
440
441
441 self.mark_for_invalidation(repo_name)
442 self.mark_for_invalidation(repo_name)
442
443
443 def get_nodes(self, repo_name, revision, root_path='/', flat=True):
444 def get_nodes(self, repo_name, revision, root_path='/', flat=True):
444 """
445 """
445 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
446 based on repository walk function
447 based on repository walk function
447
448
448 :param repo_name: name of repository
449 :param repo_name: name of repository
449 :param revision: revision for which to list nodes
450 :param revision: revision for which to list nodes
450 :param root_path: root path to list
451 :param root_path: root path to list
451 :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
452
453
453 """
454 """
454 _files = list()
455 _files = list()
455 _dirs = list()
456 _dirs = list()
456 try:
457 try:
457 _repo = self.__get_repo(repo_name)
458 _repo = self.__get_repo(repo_name)
458 changeset = _repo.scm_instance.get_changeset(revision)
459 changeset = _repo.scm_instance.get_changeset(revision)
459 root_path = root_path.lstrip('/')
460 root_path = root_path.lstrip('/')
460 for topnode, dirs, files in changeset.walk(root_path):
461 for topnode, dirs, files in changeset.walk(root_path):
461 for f in files:
462 for f in files:
462 _files.append(f.path if flat else {"name": f.path,
463 _files.append(f.path if flat else {"name": f.path,
463 "type": "file"})
464 "type": "file"})
464 for d in dirs:
465 for d in dirs:
465 _dirs.append(d.path if flat else {"name": d.path,
466 _dirs.append(d.path if flat else {"name": d.path,
466 "type": "dir"})
467 "type": "dir"})
467 except RepositoryError:
468 except RepositoryError:
468 log.debug(traceback.format_exc())
469 log.debug(traceback.format_exc())
469 raise
470 raise
470
471
471 return _dirs, _files
472 return _dirs, _files
472
473
473 def get_unread_journal(self):
474 def get_unread_journal(self):
474 return self.sa.query(UserLog).count()
475 return self.sa.query(UserLog).count()
476
477 def get_repo_landing_revs(self, repo=None):
478 """
479 Generates select option with tags branches and bookmarks (for hg only)
480 grouped by type
481
482 :param repo:
483 :type repo:
484 """
485 hist_l = []
486 repo = self.__get_repo(repo)
487 hist_l.append(['tip', _('latest tip')])
488 if not repo:
489 return hist_l
490
491 repo = repo.scm_instance
492 branches_group = ([(k, k) for k, v in
493 repo.branches.iteritems()], _("Branches"))
494 hist_l.append(branches_group)
495
496 if repo.alias == 'hg':
497 bookmarks_group = ([(k, k) for k, v in
498 repo.bookmarks.iteritems()], _("Bookmarks"))
499 hist_l.append(bookmarks_group)
500
501 tags_group = ([(k, k) for k, v in
502 repo.tags.iteritems()], _("Tags"))
503 hist_l.append(tags_group)
504
505 return hist_l
@@ -1,68 +1,77 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2
2
3 ${h.form(url('repos'))}
3 ${h.form(url('repos'))}
4 <div class="form">
4 <div class="form">
5 <!-- fields -->
5 <!-- fields -->
6 <div class="fields">
6 <div class="fields">
7 <div class="field">
7 <div class="field">
8 <div class="label">
8 <div class="label">
9 <label for="repo_name">${_('Name')}:</label>
9 <label for="repo_name">${_('Name')}:</label>
10 </div>
10 </div>
11 <div class="input">
11 <div class="input">
12 ${h.text('repo_name',c.new_repo,class_="small")}
12 ${h.text('repo_name',c.new_repo,class_="small")}
13 %if not h.HasPermissionAll('hg.admin')('repo create form'):
13 %if not h.HasPermissionAll('hg.admin')('repo create form'):
14 ${h.hidden('user_created',True)}
14 ${h.hidden('user_created',True)}
15 %endif
15 %endif
16 </div>
16 </div>
17 </div>
17 </div>
18 <div class="field">
18 <div class="field">
19 <div class="label">
19 <div class="label">
20 <label for="clone_uri">${_('Clone from')}:</label>
20 <label for="clone_uri">${_('Clone from')}:</label>
21 </div>
21 </div>
22 <div class="input">
22 <div class="input">
23 ${h.text('clone_uri',class_="small")}
23 ${h.text('clone_uri',class_="small")}
24 <span class="help-block">${_('Optional http[s] url from which repository should be cloned.')}</span>
24 <span class="help-block">${_('Optional http[s] url from which repository should be cloned.')}</span>
25 </div>
25 </div>
26 </div>
26 </div>
27 <div class="field">
27 <div class="field">
28 <div class="label">
28 <div class="label">
29 <label for="repo_group">${_('Repository group')}:</label>
29 <label for="repo_group">${_('Repository group')}:</label>
30 </div>
30 </div>
31 <div class="input">
31 <div class="input">
32 ${h.select('repo_group',request.GET.get('parent_group'),c.repo_groups,class_="medium")}
32 ${h.select('repo_group',request.GET.get('parent_group'),c.repo_groups,class_="medium")}
33 <span class="help-block">${_('Optional select a group to put this repository into.')}</span>
33 <span class="help-block">${_('Optional select a group to put this repository into.')}</span>
34 </div>
34 </div>
35 </div>
35 </div>
36 <div class="field">
36 <div class="field">
37 <div class="label">
37 <div class="label">
38 <label for="repo_type">${_('Type')}:</label>
38 <label for="repo_type">${_('Type')}:</label>
39 </div>
39 </div>
40 <div class="input">
40 <div class="input">
41 ${h.select('repo_type','hg',c.backends,class_="small")}
41 ${h.select('repo_type','hg',c.backends,class_="small")}
42 <span class="help-block">${_('Type of repository to create.')}</span>
42 <span class="help-block">${_('Type of repository to create.')}</span>
43 </div>
43 </div>
44 </div>
44 </div>
45 <div class="field">
45 <div class="field">
46 <div class="label">
47 <label for="landing_rev">${_('Landing revision')}:</label>
48 </div>
49 <div class="input">
50 ${h.select('landing_rev','',c.landing_revs,class_="medium")}
51 <span class="help-block">${_('Default revision for files page, downloads, whoosh and readme')}</span>
52 </div>
53 </div>
54 <div class="field">
46 <div class="label label-textarea">
55 <div class="label label-textarea">
47 <label for="description">${_('Description')}:</label>
56 <label for="description">${_('Description')}:</label>
48 </div>
57 </div>
49 <div class="textarea text-area editor">
58 <div class="textarea text-area editor">
50 ${h.textarea('description')}
59 ${h.textarea('description')}
51 <span class="help-block">${_('Keep it short and to the point. Use a README file for longer descriptions.')}</span>
60 <span class="help-block">${_('Keep it short and to the point. Use a README file for longer descriptions.')}</span>
52 </div>
61 </div>
53 </div>
62 </div>
54 <div class="field">
63 <div class="field">
55 <div class="label label-checkbox">
64 <div class="label label-checkbox">
56 <label for="private">${_('Private repository')}:</label>
65 <label for="private">${_('Private repository')}:</label>
57 </div>
66 </div>
58 <div class="checkboxes">
67 <div class="checkboxes">
59 ${h.checkbox('private',value="True")}
68 ${h.checkbox('private',value="True")}
60 <span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span>
69 <span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span>
61 </div>
70 </div>
62 </div>
71 </div>
63 <div class="buttons">
72 <div class="buttons">
64 ${h.submit('add',_('add'),class_="ui-button")}
73 ${h.submit('add',_('add'),class_="ui-button")}
65 </div>
74 </div>
66 </div>
75 </div>
67 </div>
76 </div>
68 ${h.end_form()}
77 ${h.end_form()}
@@ -1,231 +1,240 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Edit repository')} ${c.repo_info.repo_name} - ${c.rhodecode_name}
5 ${_('Edit repository')} ${c.repo_info.repo_name} - ${c.rhodecode_name}
6 </%def>
6 </%def>
7
7
8 <%def name="breadcrumbs_links()">
8 <%def name="breadcrumbs_links()">
9 ${h.link_to(_('Admin'),h.url('admin_home'))}
9 ${h.link_to(_('Admin'),h.url('admin_home'))}
10 &raquo;
10 &raquo;
11 ${h.link_to(_('Repositories'),h.url('repos'))}
11 ${h.link_to(_('Repositories'),h.url('repos'))}
12 &raquo;
12 &raquo;
13 ${_('edit')} &raquo; ${h.link_to(c.repo_info.just_name,h.url('summary_home',repo_name=c.repo_name))}
13 ${_('edit')} &raquo; ${h.link_to(c.repo_info.just_name,h.url('summary_home',repo_name=c.repo_name))}
14 </%def>
14 </%def>
15
15
16 <%def name="page_nav()">
16 <%def name="page_nav()">
17 ${self.menu('admin')}
17 ${self.menu('admin')}
18 </%def>
18 </%def>
19
19
20 <%def name="main()">
20 <%def name="main()">
21 <div class="box box-left">
21 <div class="box box-left">
22 <!-- box / title -->
22 <!-- box / title -->
23 <div class="title">
23 <div class="title">
24 ${self.breadcrumbs()}
24 ${self.breadcrumbs()}
25 </div>
25 </div>
26 ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='put')}
26 ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='put')}
27 <div class="form">
27 <div class="form">
28 <!-- fields -->
28 <!-- fields -->
29 <div class="fields">
29 <div class="fields">
30 <div class="field">
30 <div class="field">
31 <div class="label">
31 <div class="label">
32 <label for="repo_name">${_('Name')}:</label>
32 <label for="repo_name">${_('Name')}:</label>
33 </div>
33 </div>
34 <div class="input">
34 <div class="input">
35 ${h.text('repo_name',class_="medium")}
35 ${h.text('repo_name',class_="medium")}
36 </div>
36 </div>
37 </div>
37 </div>
38 <div class="field">
38 <div class="field">
39 <div class="label">
39 <div class="label">
40 <label for="clone_uri">${_('Clone uri')}:</label>
40 <label for="clone_uri">${_('Clone uri')}:</label>
41 </div>
41 </div>
42 <div class="input">
42 <div class="input">
43 ${h.text('clone_uri',class_="medium")}
43 ${h.text('clone_uri',class_="medium")}
44 <span class="help-block">${_('Optional http[s] url from which repository should be cloned.')}</span>
44 <span class="help-block">${_('Optional http[s] url from which repository should be cloned.')}</span>
45 </div>
45 </div>
46 </div>
46 </div>
47 <div class="field">
47 <div class="field">
48 <div class="label">
48 <div class="label">
49 <label for="repo_group">${_('Repository group')}:</label>
49 <label for="repo_group">${_('Repository group')}:</label>
50 </div>
50 </div>
51 <div class="input">
51 <div class="input">
52 ${h.select('repo_group','',c.repo_groups,class_="medium")}
52 ${h.select('repo_group','',c.repo_groups,class_="medium")}
53 <span class="help-block">${_('Optional select a group to put this repository into.')}</span>
53 <span class="help-block">${_('Optional select a group to put this repository into.')}</span>
54 </div>
54 </div>
55 </div>
55 </div>
56 <div class="field">
56 <div class="field">
57 <div class="label">
57 <div class="label">
58 <label for="repo_type">${_('Type')}:</label>
58 <label for="repo_type">${_('Type')}:</label>
59 </div>
59 </div>
60 <div class="input">
60 <div class="input">
61 ${h.select('repo_type','hg',c.backends,class_="medium")}
61 ${h.select('repo_type','hg',c.backends,class_="medium")}
62 </div>
62 </div>
63 </div>
63 </div>
64 <div class="field">
64 <div class="field">
65 <div class="label">
66 <label for="landing_rev">${_('Landing revision')}:</label>
67 </div>
68 <div class="input">
69 ${h.select('landing_rev','',c.landing_revs,class_="medium")}
70 <span class="help-block">${_('Default revision for files page, downloads, whoosh and readme')}</span>
71 </div>
72 </div>
73 <div class="field">
65 <div class="label label-textarea">
74 <div class="label label-textarea">
66 <label for="description">${_('Description')}:</label>
75 <label for="description">${_('Description')}:</label>
67 </div>
76 </div>
68 <div class="textarea text-area editor">
77 <div class="textarea text-area editor">
69 ${h.textarea('description')}
78 ${h.textarea('description')}
70 <span class="help-block">${_('Keep it short and to the point. Use a README file for longer descriptions.')}</span>
79 <span class="help-block">${_('Keep it short and to the point. Use a README file for longer descriptions.')}</span>
71 </div>
80 </div>
72 </div>
81 </div>
73
82
74 <div class="field">
83 <div class="field">
75 <div class="label label-checkbox">
84 <div class="label label-checkbox">
76 <label for="private">${_('Private repository')}:</label>
85 <label for="private">${_('Private repository')}:</label>
77 </div>
86 </div>
78 <div class="checkboxes">
87 <div class="checkboxes">
79 ${h.checkbox('private',value="True")}
88 ${h.checkbox('private',value="True")}
80 <span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span>
89 <span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span>
81 </div>
90 </div>
82 </div>
91 </div>
83 <div class="field">
92 <div class="field">
84 <div class="label label-checkbox">
93 <div class="label label-checkbox">
85 <label for="enable_statistics">${_('Enable statistics')}:</label>
94 <label for="enable_statistics">${_('Enable statistics')}:</label>
86 </div>
95 </div>
87 <div class="checkboxes">
96 <div class="checkboxes">
88 ${h.checkbox('enable_statistics',value="True")}
97 ${h.checkbox('enable_statistics',value="True")}
89 <span class="help-block">${_('Enable statistics window on summary page.')}</span>
98 <span class="help-block">${_('Enable statistics window on summary page.')}</span>
90 </div>
99 </div>
91 </div>
100 </div>
92 <div class="field">
101 <div class="field">
93 <div class="label label-checkbox">
102 <div class="label label-checkbox">
94 <label for="enable_downloads">${_('Enable downloads')}:</label>
103 <label for="enable_downloads">${_('Enable downloads')}:</label>
95 </div>
104 </div>
96 <div class="checkboxes">
105 <div class="checkboxes">
97 ${h.checkbox('enable_downloads',value="True")}
106 ${h.checkbox('enable_downloads',value="True")}
98 <span class="help-block">${_('Enable download menu on summary page.')}</span>
107 <span class="help-block">${_('Enable download menu on summary page.')}</span>
99 </div>
108 </div>
100 </div>
109 </div>
101 <div class="field">
110 <div class="field">
102 <div class="label">
111 <div class="label">
103 <label for="user">${_('Owner')}:</label>
112 <label for="user">${_('Owner')}:</label>
104 </div>
113 </div>
105 <div class="input input-medium ac">
114 <div class="input input-medium ac">
106 <div class="perm_ac">
115 <div class="perm_ac">
107 ${h.text('user',class_='yui-ac-input')}
116 ${h.text('user',class_='yui-ac-input')}
108 <span class="help-block">${_('Change owner of this repository.')}</span>
117 <span class="help-block">${_('Change owner of this repository.')}</span>
109 <div id="owner_container"></div>
118 <div id="owner_container"></div>
110 </div>
119 </div>
111 </div>
120 </div>
112 </div>
121 </div>
113
122
114 <div class="field">
123 <div class="field">
115 <div class="label">
124 <div class="label">
116 <label for="input">${_('Permissions')}:</label>
125 <label for="input">${_('Permissions')}:</label>
117 </div>
126 </div>
118 <div class="input">
127 <div class="input">
119 <%include file="repo_edit_perms.html"/>
128 <%include file="repo_edit_perms.html"/>
120 </div>
129 </div>
121
130
122 <div class="buttons">
131 <div class="buttons">
123 ${h.submit('save','Save',class_="ui-button")}
132 ${h.submit('save','Save',class_="ui-button")}
124 ${h.reset('reset','Reset',class_="ui-button")}
133 ${h.reset('reset','Reset',class_="ui-button")}
125 </div>
134 </div>
126 </div>
135 </div>
127 </div>
136 </div>
128 </div>
137 </div>
129 ${h.end_form()}
138 ${h.end_form()}
130 </div>
139 </div>
131
140
132 <div class="box box-right">
141 <div class="box box-right">
133 <div class="title">
142 <div class="title">
134 <h5>${_('Administration')}</h5>
143 <h5>${_('Administration')}</h5>
135 </div>
144 </div>
136
145
137 <h3>${_('Statistics')}</h3>
146 <h3>${_('Statistics')}</h3>
138 ${h.form(url('repo_stats', repo_name=c.repo_info.repo_name),method='delete')}
147 ${h.form(url('repo_stats', repo_name=c.repo_info.repo_name),method='delete')}
139 <div class="form">
148 <div class="form">
140 <div class="fields">
149 <div class="fields">
141 ${h.submit('reset_stats_%s' % c.repo_info.repo_name,_('Reset current statistics'),class_="ui-btn",onclick="return confirm('"+_('Confirm to remove current statistics')+"');")}
150 ${h.submit('reset_stats_%s' % c.repo_info.repo_name,_('Reset current statistics'),class_="ui-btn",onclick="return confirm('"+_('Confirm to remove current statistics')+"');")}
142 <div class="field" style="border:none;color:#888">
151 <div class="field" style="border:none;color:#888">
143 <ul>
152 <ul>
144 <li>${_('Fetched to rev')}: ${c.stats_revision}/${c.repo_last_rev}</li>
153 <li>${_('Fetched to rev')}: ${c.stats_revision}/${c.repo_last_rev}</li>
145 <li>${_('Stats gathered')}: ${c.stats_percentage}%</li>
154 <li>${_('Stats gathered')}: ${c.stats_percentage}%</li>
146 </ul>
155 </ul>
147 </div>
156 </div>
148 </div>
157 </div>
149 </div>
158 </div>
150 ${h.end_form()}
159 ${h.end_form()}
151
160
152 %if c.repo_info.clone_uri:
161 %if c.repo_info.clone_uri:
153 <h3>${_('Remote')}</h3>
162 <h3>${_('Remote')}</h3>
154 ${h.form(url('repo_pull', repo_name=c.repo_info.repo_name),method='put')}
163 ${h.form(url('repo_pull', repo_name=c.repo_info.repo_name),method='put')}
155 <div class="form">
164 <div class="form">
156 <div class="fields">
165 <div class="fields">
157 ${h.submit('remote_pull_%s' % c.repo_info.repo_name,_('Pull changes from remote location'),class_="ui-btn",onclick="return confirm('"+_('Confirm to pull changes from remote side')+"');")}
166 ${h.submit('remote_pull_%s' % c.repo_info.repo_name,_('Pull changes from remote location'),class_="ui-btn",onclick="return confirm('"+_('Confirm to pull changes from remote side')+"');")}
158 <div class="field" style="border:none">
167 <div class="field" style="border:none">
159 <ul>
168 <ul>
160 <li><a href="${c.repo_info.clone_uri}">${c.repo_info.clone_uri}</a></li>
169 <li><a href="${c.repo_info.clone_uri}">${c.repo_info.clone_uri}</a></li>
161 </ul>
170 </ul>
162 </div>
171 </div>
163 </div>
172 </div>
164 </div>
173 </div>
165 ${h.end_form()}
174 ${h.end_form()}
166 %endif
175 %endif
167
176
168 <h3>${_('Cache')}</h3>
177 <h3>${_('Cache')}</h3>
169 ${h.form(url('repo_cache', repo_name=c.repo_info.repo_name),method='delete')}
178 ${h.form(url('repo_cache', repo_name=c.repo_info.repo_name),method='delete')}
170 <div class="form">
179 <div class="form">
171 <div class="fields">
180 <div class="fields">
172 ${h.submit('reset_cache_%s' % c.repo_info.repo_name,_('Invalidate repository cache'),class_="ui-btn",onclick="return confirm('"+_('Confirm to invalidate repository cache')+"');")}
181 ${h.submit('reset_cache_%s' % c.repo_info.repo_name,_('Invalidate repository cache'),class_="ui-btn",onclick="return confirm('"+_('Confirm to invalidate repository cache')+"');")}
173 </div>
182 </div>
174 </div>
183 </div>
175 ${h.end_form()}
184 ${h.end_form()}
176
185
177 <h3>${_('Public journal')}</h3>
186 <h3>${_('Public journal')}</h3>
178 ${h.form(url('repo_public_journal', repo_name=c.repo_info.repo_name),method='put')}
187 ${h.form(url('repo_public_journal', repo_name=c.repo_info.repo_name),method='put')}
179 <div class="form">
188 <div class="form">
180 ${h.hidden('auth_token',str(h.get_token()))}
189 ${h.hidden('auth_token',str(h.get_token()))}
181 <div class="field">
190 <div class="field">
182 %if c.in_public_journal:
191 %if c.in_public_journal:
183 ${h.submit('set_public_%s' % c.repo_info.repo_name,_('Remove from public journal'),class_="ui-btn")}
192 ${h.submit('set_public_%s' % c.repo_info.repo_name,_('Remove from public journal'),class_="ui-btn")}
184 %else:
193 %else:
185 ${h.submit('set_public_%s' % c.repo_info.repo_name,_('Add to public journal'),class_="ui-btn")}
194 ${h.submit('set_public_%s' % c.repo_info.repo_name,_('Add to public journal'),class_="ui-btn")}
186 %endif
195 %endif
187 </div>
196 </div>
188 <div class="field" style="border:none;color:#888">
197 <div class="field" style="border:none;color:#888">
189 <ul>
198 <ul>
190 <li>${_('''All actions made on this repository will be accessible to everyone in public journal''')}
199 <li>${_('''All actions made on this repository will be accessible to everyone in public journal''')}
191 </li>
200 </li>
192 </ul>
201 </ul>
193 </div>
202 </div>
194 </div>
203 </div>
195 ${h.end_form()}
204 ${h.end_form()}
196
205
197 <h3>${_('Delete')}</h3>
206 <h3>${_('Delete')}</h3>
198 ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='delete')}
207 ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='delete')}
199 <div class="form">
208 <div class="form">
200 <div class="fields">
209 <div class="fields">
201 ${h.submit('remove_%s' % c.repo_info.repo_name,_('Remove this repository'),class_="ui-btn red",onclick="return confirm('"+_('Confirm to delete this repository')+"');")}
210 ${h.submit('remove_%s' % c.repo_info.repo_name,_('Remove this repository'),class_="ui-btn red",onclick="return confirm('"+_('Confirm to delete this repository')+"');")}
202 </div>
211 </div>
203 <div class="field" style="border:none;color:#888">
212 <div class="field" style="border:none;color:#888">
204 <ul>
213 <ul>
205 <li>${_('''This repository will be renamed in a special way in order to be unaccesible for RhodeCode and VCS systems.
214 <li>${_('''This repository will be renamed in a special way in order to be unaccesible for RhodeCode and VCS systems.
206 If you need fully delete it from filesystem please do it manually''')}
215 If you need fully delete it from filesystem please do it manually''')}
207 </li>
216 </li>
208 </ul>
217 </ul>
209 </div>
218 </div>
210 </div>
219 </div>
211 ${h.end_form()}
220 ${h.end_form()}
212
221
213 <h3>${_('Set as fork')}</h3>
222 <h3>${_('Set as fork of')}</h3>
214 ${h.form(url('repo_as_fork', repo_name=c.repo_info.repo_name),method='put')}
223 ${h.form(url('repo_as_fork', repo_name=c.repo_info.repo_name),method='put')}
215 <div class="form">
224 <div class="form">
216 <div class="fields">
225 <div class="fields">
217 ${h.select('id_fork_of','',c.repos_list,class_="medium")}
226 ${h.select('id_fork_of','',c.repos_list,class_="medium")}
218 ${h.submit('set_as_fork_%s' % c.repo_info.repo_name,_('set'),class_="ui-btn",)}
227 ${h.submit('set_as_fork_%s' % c.repo_info.repo_name,_('set'),class_="ui-btn",)}
219 </div>
228 </div>
220 <div class="field" style="border:none;color:#888">
229 <div class="field" style="border:none;color:#888">
221 <ul>
230 <ul>
222 <li>${_('''Manually set this repository as a fork of another''')}</li>
231 <li>${_('''Manually set this repository as a fork of another from the list''')}</li>
223 </ul>
232 </ul>
224 </div>
233 </div>
225 </div>
234 </div>
226 ${h.end_form()}
235 ${h.end_form()}
227
236
228 </div>
237 </div>
229
238
230
239
231 </%def>
240 </%def>
@@ -1,158 +1,158 b''
1 """Pylons application test package
1 """Pylons application test package
2
2
3 This package assumes the Pylons environment is already loaded, such as
3 This package assumes the Pylons environment is already loaded, such as
4 when this script is imported from the `nosetests --with-pylons=test.ini`
4 when this script is imported from the `nosetests --with-pylons=test.ini`
5 command.
5 command.
6
6
7 This module initializes the application via ``websetup`` (`paster
7 This module initializes the application via ``websetup`` (`paster
8 setup-app`) and provides the base testing objects.
8 setup-app`) and provides the base testing objects.
9 """
9 """
10 import os
10 import os
11 import time
11 import time
12 import logging
12 import logging
13 import datetime
13 import datetime
14 import hashlib
14 import hashlib
15 import tempfile
15 import tempfile
16 from os.path import join as jn
16 from os.path import join as jn
17
17
18 from unittest import TestCase
18 from unittest import TestCase
19 from tempfile import _RandomNameSequence
19 from tempfile import _RandomNameSequence
20
20
21 from paste.deploy import loadapp
21 from paste.deploy import loadapp
22 from paste.script.appinstall import SetupCommand
22 from paste.script.appinstall import SetupCommand
23 from pylons import config, url
23 from pylons import config, url
24 from routes.util import URLGenerator
24 from routes.util import URLGenerator
25 from webtest import TestApp
25 from webtest import TestApp
26
26
27 from rhodecode import is_windows
27 from rhodecode import is_windows
28 from rhodecode.model.meta import Session
28 from rhodecode.model.meta import Session
29 from rhodecode.model.db import User
29 from rhodecode.model.db import User
30
30
31 import pylons.test
31 import pylons.test
32
32
33
33
34 os.environ['TZ'] = 'UTC'
34 os.environ['TZ'] = 'UTC'
35 if not is_windows:
35 if not is_windows:
36 time.tzset()
36 time.tzset()
37
37
38 log = logging.getLogger(__name__)
38 log = logging.getLogger(__name__)
39
39
40 __all__ = [
40 __all__ = [
41 'environ', 'url', 'TestController', 'TESTS_TMP_PATH', 'HG_REPO',
41 'environ', 'url', 'get_new_dir', 'TestController', 'TESTS_TMP_PATH',
42 'GIT_REPO', 'NEW_HG_REPO', 'NEW_GIT_REPO', 'HG_FORK', 'GIT_FORK',
42 'HG_REPO', 'GIT_REPO', 'NEW_HG_REPO', 'NEW_GIT_REPO', 'HG_FORK',
43 'TEST_USER_ADMIN_LOGIN', 'TEST_USER_REGULAR_LOGIN',
43 'GIT_FORK', 'TEST_USER_ADMIN_LOGIN', 'TEST_USER_REGULAR_LOGIN',
44 'TEST_USER_REGULAR_PASS', 'TEST_USER_REGULAR_EMAIL',
44 'TEST_USER_REGULAR_PASS', 'TEST_USER_REGULAR_EMAIL',
45 'TEST_USER_REGULAR2_LOGIN', 'TEST_USER_REGULAR2_PASS',
45 'TEST_USER_REGULAR2_LOGIN', 'TEST_USER_REGULAR2_PASS',
46 'TEST_USER_REGULAR2_EMAIL', 'TEST_HG_REPO', 'TEST_GIT_REPO',
46 'TEST_USER_REGULAR2_EMAIL', 'TEST_HG_REPO', 'TEST_HG_REPO_CLONE',
47 'HG_REMOTE_REPO', 'GIT_REMOTE_REPO', 'SCM_TESTS',
47 'TEST_HG_REPO_PULL', 'TEST_GIT_REPO', 'TEST_GIT_REPO_CLONE',
48 'TEST_GIT_REPO_PULL', 'HG_REMOTE_REPO', 'GIT_REMOTE_REPO', 'SCM_TESTS',
48 ]
49 ]
49
50
50 # Invoke websetup with the current config file
51 # Invoke websetup with the current config file
51 # SetupCommand('setup-app').run([config_file])
52 # SetupCommand('setup-app').run([config_file])
52
53
53 ##RUNNING DESIRED TESTS
54 ##RUNNING DESIRED TESTS
54 # nosetests -x rhodecode.tests.functional.test_admin_settings:TestSettingsController.test_my_account
55 # nosetests -x rhodecode.tests.functional.test_admin_settings:TestSettingsController.test_my_account
55 # nosetests --pdb --pdb-failures
56 # nosetests --pdb --pdb-failures
56 environ = {}
57 environ = {}
57
58
58 #SOME GLOBALS FOR TESTS
59 #SOME GLOBALS FOR TESTS
59
60
60 TESTS_TMP_PATH = jn('/', 'tmp', 'rc_test_%s' % _RandomNameSequence().next())
61 TESTS_TMP_PATH = jn('/', 'tmp', 'rc_test_%s' % _RandomNameSequence().next())
61 TEST_USER_ADMIN_LOGIN = 'test_admin'
62 TEST_USER_ADMIN_LOGIN = 'test_admin'
62 TEST_USER_ADMIN_PASS = 'test12'
63 TEST_USER_ADMIN_PASS = 'test12'
63 TEST_USER_ADMIN_EMAIL = 'test_admin@mail.com'
64 TEST_USER_ADMIN_EMAIL = 'test_admin@mail.com'
64
65
65 TEST_USER_REGULAR_LOGIN = 'test_regular'
66 TEST_USER_REGULAR_LOGIN = 'test_regular'
66 TEST_USER_REGULAR_PASS = 'test12'
67 TEST_USER_REGULAR_PASS = 'test12'
67 TEST_USER_REGULAR_EMAIL = 'test_regular@mail.com'
68 TEST_USER_REGULAR_EMAIL = 'test_regular@mail.com'
68
69
69 TEST_USER_REGULAR2_LOGIN = 'test_regular2'
70 TEST_USER_REGULAR2_LOGIN = 'test_regular2'
70 TEST_USER_REGULAR2_PASS = 'test12'
71 TEST_USER_REGULAR2_PASS = 'test12'
71 TEST_USER_REGULAR2_EMAIL = 'test_regular2@mail.com'
72 TEST_USER_REGULAR2_EMAIL = 'test_regular2@mail.com'
72
73
73 HG_REPO = 'vcs_test_hg'
74 HG_REPO = 'vcs_test_hg'
74 GIT_REPO = 'vcs_test_git'
75 GIT_REPO = 'vcs_test_git'
75
76
76 NEW_HG_REPO = 'vcs_test_hg_new'
77 NEW_HG_REPO = 'vcs_test_hg_new'
77 NEW_GIT_REPO = 'vcs_test_git_new'
78 NEW_GIT_REPO = 'vcs_test_git_new'
78
79
79 HG_FORK = 'vcs_test_hg_fork'
80 HG_FORK = 'vcs_test_hg_fork'
80 GIT_FORK = 'vcs_test_git_fork'
81 GIT_FORK = 'vcs_test_git_fork'
81
82
82 ## VCS
83 ## VCS
83 SCM_TESTS = ['hg', 'git']
84 SCM_TESTS = ['hg', 'git']
84 uniq_suffix = str(int(time.mktime(datetime.datetime.now().timetuple())))
85 uniq_suffix = str(int(time.mktime(datetime.datetime.now().timetuple())))
85
86
86 THIS = os.path.abspath(os.path.dirname(__file__))
87 GIT_REMOTE_REPO = 'git://github.com/codeinn/vcs.git'
87
88
88 GIT_REMOTE_REPO = 'git://github.com/codeinn/vcs.git'
89 TEST_GIT_REPO = jn(TESTS_TMP_PATH, GIT_REPO)
89 TEST_GIT_REPO = jn(TESTS_TMP_PATH, GIT_REPO)
90 TEST_GIT_REPO_CLONE = jn(TESTS_TMP_PATH, 'vcsgitclone%s' % uniq_suffix)
90 TEST_GIT_REPO_CLONE = jn(TESTS_TMP_PATH, 'vcsgitclone%s' % uniq_suffix)
91 TEST_GIT_REPO_PULL = jn(TESTS_TMP_PATH, 'vcsgitpull%s' % uniq_suffix)
91 TEST_GIT_REPO_PULL = jn(TESTS_TMP_PATH, 'vcsgitpull%s' % uniq_suffix)
92
92
93
93
94 HG_REMOTE_REPO = 'http://bitbucket.org/marcinkuzminski/vcs'
94 HG_REMOTE_REPO = 'http://bitbucket.org/marcinkuzminski/vcs'
95 TEST_HG_REPO = jn(TESTS_TMP_PATH, 'vcs-hg')
95
96 TEST_HG_REPO = jn(TESTS_TMP_PATH, HG_REPO)
96 TEST_HG_REPO_CLONE = jn(TESTS_TMP_PATH, 'vcshgclone%s' % uniq_suffix)
97 TEST_HG_REPO_CLONE = jn(TESTS_TMP_PATH, 'vcshgclone%s' % uniq_suffix)
97 TEST_HG_REPO_PULL = jn(TESTS_TMP_PATH, 'vcshgpull%s' % uniq_suffix)
98 TEST_HG_REPO_PULL = jn(TESTS_TMP_PATH, 'vcshgpull%s' % uniq_suffix)
98
99
99 TEST_DIR = tempfile.gettempdir()
100 TEST_DIR = tempfile.gettempdir()
100 TEST_REPO_PREFIX = 'vcs-test'
101 TEST_REPO_PREFIX = 'vcs-test'
101
102
103 # cached repos if any !
104 # comment out to get some other repos from bb or github
105 GIT_REMOTE_REPO = jn(TESTS_TMP_PATH, GIT_REPO)
106 HG_REMOTE_REPO = jn(TESTS_TMP_PATH, HG_REPO)
107
102
108
103 def get_new_dir(title):
109 def get_new_dir(title):
104 """
110 """
105 Returns always new directory path.
111 Returns always new directory path.
106 """
112 """
107 from rhodecode.tests.vcs.utils import get_normalized_path
113 from rhodecode.tests.vcs.utils import get_normalized_path
108 name = TEST_REPO_PREFIX
114 name = TEST_REPO_PREFIX
109 if title:
115 if title:
110 name = '-'.join((name, title))
116 name = '-'.join((name, title))
111 hex = hashlib.sha1(str(time.time())).hexdigest()
117 hex = hashlib.sha1(str(time.time())).hexdigest()
112 name = '-'.join((name, hex))
118 name = '-'.join((name, hex))
113 path = os.path.join(TEST_DIR, name)
119 path = os.path.join(TEST_DIR, name)
114 return get_normalized_path(path)
120 return get_normalized_path(path)
115
121
116
122
117 PACKAGE_DIR = os.path.abspath(os.path.join(
118 os.path.dirname(__file__), '..'))
119
120 TEST_USER_CONFIG_FILE = jn(THIS, 'aconfig')
121
122
123 class TestController(TestCase):
123 class TestController(TestCase):
124
124
125 def __init__(self, *args, **kwargs):
125 def __init__(self, *args, **kwargs):
126 wsgiapp = pylons.test.pylonsapp
126 wsgiapp = pylons.test.pylonsapp
127 config = wsgiapp.config
127 config = wsgiapp.config
128
128
129 self.app = TestApp(wsgiapp)
129 self.app = TestApp(wsgiapp)
130 url._push_object(URLGenerator(config['routes.map'], environ))
130 url._push_object(URLGenerator(config['routes.map'], environ))
131 self.Session = Session
131 self.Session = Session
132 self.index_location = config['app_conf']['index_dir']
132 self.index_location = config['app_conf']['index_dir']
133 TestCase.__init__(self, *args, **kwargs)
133 TestCase.__init__(self, *args, **kwargs)
134
134
135 def log_user(self, username=TEST_USER_ADMIN_LOGIN,
135 def log_user(self, username=TEST_USER_ADMIN_LOGIN,
136 password=TEST_USER_ADMIN_PASS):
136 password=TEST_USER_ADMIN_PASS):
137 self._logged_username = username
137 self._logged_username = username
138 response = self.app.post(url(controller='login', action='index'),
138 response = self.app.post(url(controller='login', action='index'),
139 {'username': username,
139 {'username': username,
140 'password': password})
140 'password': password})
141
141
142 if 'invalid user name' in response.body:
142 if 'invalid user name' in response.body:
143 self.fail('could not login using %s %s' % (username, password))
143 self.fail('could not login using %s %s' % (username, password))
144
144
145 self.assertEqual(response.status, '302 Found')
145 self.assertEqual(response.status, '302 Found')
146 ses = response.session['rhodecode_user']
146 ses = response.session['rhodecode_user']
147 self.assertEqual(ses.get('username'), username)
147 self.assertEqual(ses.get('username'), username)
148 response = response.follow()
148 response = response.follow()
149 self.assertEqual(ses.get('is_authenticated'), True)
149 self.assertEqual(ses.get('is_authenticated'), True)
150
150
151 return response.session['rhodecode_user']
151 return response.session['rhodecode_user']
152
152
153 def _get_logged_user(self):
153 def _get_logged_user(self):
154 return User.get_by_username(self._logged_username)
154 return User.get_by_username(self._logged_username)
155
155
156 def checkSessionFlash(self, response, msg):
156 def checkSessionFlash(self, response, msg):
157 self.assertTrue('flash' in response.session)
157 self.assertTrue('flash' in response.session)
158 self.assertTrue(msg in response.session['flash'][0][1])
158 self.assertTrue(msg in response.session['flash'][0][1])
@@ -1,210 +1,295 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 import os
3 import os
4 from rhodecode.lib import vcs
4 from rhodecode.lib import vcs
5
5
6 from rhodecode.model.db import Repository
6 from rhodecode.model.db import Repository
7 from rhodecode.tests import *
7 from rhodecode.tests import *
8
8
9
9 class TestAdminReposController(TestController):
10 class TestAdminReposController(TestController):
10
11
11 def __make_repo(self):
12 def __make_repo(self):
12 pass
13 pass
13
14
14 def test_index(self):
15 def test_index(self):
15 self.log_user()
16 self.log_user()
16 response = self.app.get(url('repos'))
17 response = self.app.get(url('repos'))
17 # Test response...
18 # Test response...
18
19
19 def test_index_as_xml(self):
20 def test_index_as_xml(self):
20 response = self.app.get(url('formatted_repos', format='xml'))
21 response = self.app.get(url('formatted_repos', format='xml'))
21
22
22 def test_create_hg(self):
23 def test_create_hg(self):
23 self.log_user()
24 self.log_user()
24 repo_name = NEW_HG_REPO
25 repo_name = NEW_HG_REPO
25 description = 'description for newly created repo'
26 description = 'description for newly created repo'
26 private = False
27 private = False
27 response = self.app.post(url('repos'), {'repo_name':repo_name,
28 response = self.app.post(url('repos'), {'repo_name': repo_name,
28 'repo_type':'hg',
29 'repo_type': 'hg',
29 'clone_uri':'',
30 'clone_uri': '',
30 'repo_group':'',
31 'repo_group': '',
31 'description':description,
32 'description': description,
32 'private':private})
33 'private': private,
33 self.checkSessionFlash(response, 'created repository %s' % (repo_name))
34 'landing_rev': 'tip'})
35 self.checkSessionFlash(response,
36 'created repository %s' % (repo_name))
34
37
35 #test if the repo was created in the database
38 #test if the repo was created in the database
36 new_repo = self.Session.query(Repository).filter(Repository.repo_name ==
39 new_repo = self.Session.query(Repository)\
37 repo_name).one()
40 .filter(Repository.repo_name == repo_name).one()
38
41
39 self.assertEqual(new_repo.repo_name, repo_name)
42 self.assertEqual(new_repo.repo_name, repo_name)
40 self.assertEqual(new_repo.description, description)
43 self.assertEqual(new_repo.description, description)
41
44
42 #test if repository is visible in the list ?
45 #test if repository is visible in the list ?
43 response = response.follow()
46 response = response.follow()
44
47
45 self.assertTrue(repo_name in response.body)
48 response.mustcontain(repo_name)
46
47
49
48 #test if repository was created on filesystem
50 #test if repository was created on filesystem
49 try:
51 try:
50 vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
52 vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
51 except:
53 except:
52 self.fail('no repo in filesystem')
54 self.fail('no repo %s in filesystem' % repo_name)
53
54
55
55 def test_create_hg_non_ascii(self):
56 def test_create_hg_non_ascii(self):
56 self.log_user()
57 self.log_user()
57 non_ascii = "ąęł"
58 non_ascii = "ąęł"
58 repo_name = "%s%s" % (NEW_HG_REPO, non_ascii)
59 repo_name = "%s%s" % (NEW_HG_REPO, non_ascii)
59 repo_name_unicode = repo_name.decode('utf8')
60 repo_name_unicode = repo_name.decode('utf8')
60 description = 'description for newly created repo' + non_ascii
61 description = 'description for newly created repo' + non_ascii
61 description_unicode = description.decode('utf8')
62 description_unicode = description.decode('utf8')
62 private = False
63 private = False
63 response = self.app.post(url('repos'), {'repo_name':repo_name,
64 response = self.app.post(url('repos'), {'repo_name': repo_name,
64 'repo_type':'hg',
65 'repo_type': 'hg',
65 'clone_uri':'',
66 'clone_uri': '',
66 'repo_group':'',
67 'repo_group': '',
67 'description':description,
68 'description': description,
68 'private':private})
69 'private': private,
70 'landing_rev': 'tip'})
69 self.checkSessionFlash(response,
71 self.checkSessionFlash(response,
70 'created repository %s' % (repo_name_unicode))
72 'created repository %s' % (repo_name_unicode))
71
73
72 #test if the repo was created in the database
74 #test if the repo was created in the database
73 new_repo = self.Session.query(Repository).filter(Repository.repo_name ==
75 new_repo = self.Session.query(Repository)\
74 repo_name_unicode).one()
76 .filter(Repository.repo_name == repo_name_unicode).one()
75
77
76 self.assertEqual(new_repo.repo_name, repo_name_unicode)
78 self.assertEqual(new_repo.repo_name, repo_name_unicode)
77 self.assertEqual(new_repo.description, description_unicode)
79 self.assertEqual(new_repo.description, description_unicode)
78
80
79 #test if repository is visible in the list ?
81 #test if repository is visible in the list ?
80 response = response.follow()
82 response = response.follow()
81
83
82 self.assertTrue(repo_name in response.body)
84 response.mustcontain(repo_name)
83
85
84 #test if repository was created on filesystem
86 #test if repository was created on filesystem
85 try:
87 try:
86 vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
88 vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
87 except:
89 except:
88 self.fail('no repo in filesystem')
90 self.fail('no repo %s in filesystem' % repo_name)
89
90
91
91 def test_create_hg_in_group(self):
92 def test_create_hg_in_group(self):
92 #TODO: write test !
93 #TODO: write test !
93 pass
94 pass
94
95
95 def test_create_git(self):
96 def test_create_git(self):
96 return
97 self.log_user()
97 self.log_user()
98 repo_name = NEW_GIT_REPO
98 repo_name = NEW_GIT_REPO
99 description = 'description for newly created repo'
99 description = 'description for newly created repo'
100 private = False
100 private = False
101 response = self.app.post(url('repos'), {'repo_name':repo_name,
101 response = self.app.post(url('repos'), {'repo_name': repo_name,
102 'repo_type':'git',
102 'repo_type': 'git',
103 'clone_uri':'',
103 'clone_uri': '',
104 'repo_group':'',
104 'repo_group': '',
105 'description':description,
105 'description': description,
106 'private':private})
106 'private': private,
107
107 'landing_rev': 'tip'})
108
108 self.checkSessionFlash(response,
109 #test if we have a message for that repository
109 'created repository %s' % (repo_name))
110 assert '''created repository %s''' % (repo_name) in response.session['flash'][0], 'No flash message about new repo'
111
110
112 #test if the fork was created in the database
111 #test if the repo was created in the database
113 new_repo = self.Session.query(Repository).filter(Repository.repo_name == repo_name).one()
112 new_repo = self.Session.query(Repository)\
113 .filter(Repository.repo_name == repo_name).one()
114
114
115 assert new_repo.repo_name == repo_name, 'wrong name of repo name in db'
115 self.assertEqual(new_repo.repo_name, repo_name)
116 assert new_repo.description == description, 'wrong description'
116 self.assertEqual(new_repo.description, description)
117
117
118 #test if repository is visible in the list ?
118 #test if repository is visible in the list ?
119 response = response.follow()
119 response = response.follow()
120
120
121 assert repo_name in response.body, 'missing new repo from the main repos list'
121 response.mustcontain(repo_name)
122
122
123 #test if repository was created on filesystem
123 #test if repository was created on filesystem
124 try:
124 try:
125 vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
125 vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
126 except:
126 except:
127 assert False , 'no repo in filesystem'
127 self.fail('no repo %s in filesystem' % repo_name)
128
129 def test_create_git_non_ascii(self):
130 self.log_user()
131 non_ascii = "ąęł"
132 repo_name = "%s%s" % (NEW_GIT_REPO, non_ascii)
133 repo_name_unicode = repo_name.decode('utf8')
134 description = 'description for newly created repo' + non_ascii
135 description_unicode = description.decode('utf8')
136 private = False
137 response = self.app.post(url('repos'), {'repo_name': repo_name,
138 'repo_type': 'git',
139 'clone_uri': '',
140 'repo_group': '',
141 'description': description,
142 'private': private,
143 'landing_rev': 'tip'})
144 self.checkSessionFlash(response,
145 'created repository %s' % (repo_name_unicode))
146
147 #test if the repo was created in the database
148 new_repo = self.Session.query(Repository)\
149 .filter(Repository.repo_name == repo_name_unicode).one()
150
151 self.assertEqual(new_repo.repo_name, repo_name_unicode)
152 self.assertEqual(new_repo.description, description_unicode)
153
154 #test if repository is visible in the list ?
155 response = response.follow()
156
157 response.mustcontain(repo_name)
158
159 #test if repository was created on filesystem
160 try:
161 vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
162 except:
163 self.fail('no repo %s in filesystem' % repo_name)
128
164
129 def test_new(self):
165 def test_new(self):
130 self.log_user()
166 self.log_user()
131 response = self.app.get(url('new_repo'))
167 response = self.app.get(url('new_repo'))
132
168
133 def test_new_as_xml(self):
169 def test_new_as_xml(self):
134 response = self.app.get(url('formatted_new_repo', format='xml'))
170 response = self.app.get(url('formatted_new_repo', format='xml'))
135
171
136 def test_update(self):
172 def test_update(self):
137 response = self.app.put(url('repo', repo_name=HG_REPO))
173 response = self.app.put(url('repo', repo_name=HG_REPO))
138
174
139 def test_update_browser_fakeout(self):
175 def test_update_browser_fakeout(self):
140 response = self.app.post(url('repo', repo_name=HG_REPO),
176 response = self.app.post(url('repo', repo_name=HG_REPO),
141 params=dict(_method='put'))
177 params=dict(_method='put'))
142
178
143 def test_delete(self):
179 def test_delete_hg(self):
144 self.log_user()
180 self.log_user()
145 repo_name = 'vcs_test_new_to_delete'
181 repo_name = 'vcs_test_new_to_delete'
146 description = 'description for newly created repo'
182 description = 'description for newly created repo'
147 private = False
183 private = False
148
149 response = self.app.post(url('repos'), {'repo_name':repo_name,
184 response = self.app.post(url('repos'), {'repo_name': repo_name,
150 'repo_type':'hg',
185 'repo_type': 'hg',
151 'clone_uri':'',
186 'clone_uri': '',
152 'repo_group':'',
187 'repo_group': '',
153 'description':description,
188 'description': description,
154 'private':private})
189 'private': private,
155 self.assertTrue('flash' in response.session)
190 'landing_rev': 'tip'})
156
191 self.checkSessionFlash(response,
157 #test if we have a message for that repository
192 'created repository %s' % (repo_name))
158 self.assertTrue('''created repository %s''' % (repo_name) in
159 response.session['flash'][0])
160
193
161 #test if the repo was created in the database
194 #test if the repo was created in the database
162 new_repo = self.Session.query(Repository).filter(Repository.repo_name ==
195 new_repo = self.Session.query(Repository)\
163 repo_name).one()
196 .filter(Repository.repo_name == repo_name).one()
164
197
165 self.assertEqual(new_repo.repo_name, repo_name)
198 self.assertEqual(new_repo.repo_name, repo_name)
166 self.assertEqual(new_repo.description, description)
199 self.assertEqual(new_repo.description, description)
167
200
168 #test if repository is visible in the list ?
201 #test if repository is visible in the list ?
169 response = response.follow()
202 response = response.follow()
170
203
171 self.assertTrue(repo_name in response.body)
204 response.mustcontain(repo_name)
172
205
206 #test if repository was created on filesystem
207 try:
208 vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
209 except:
210 self.fail('no repo %s in filesystem' % repo_name)
173
211
174 response = self.app.delete(url('repo', repo_name=repo_name))
212 response = self.app.delete(url('repo', repo_name=repo_name))
175
213
176 self.assertTrue('''deleted repository %s''' % (repo_name) in
214 self.assertTrue('''deleted repository %s''' % (repo_name) in
177 response.session['flash'][0])
215 response.session['flash'][0])
178
216
179 response.follow()
217 response.follow()
180
218
181 #check if repo was deleted from db
219 #check if repo was deleted from db
182 deleted_repo = self.Session.query(Repository).filter(Repository.repo_name
220 deleted_repo = self.Session.query(Repository)\
183 == repo_name).scalar()
221 .filter(Repository.repo_name == repo_name).scalar()
184
222
185 self.assertEqual(deleted_repo, None)
223 self.assertEqual(deleted_repo, None)
186
224
225 self.assertEqual(os.path.isdir(os.path.join(TESTS_TMP_PATH, repo_name)),
226 False)
227
228 def test_delete_git(self):
229 self.log_user()
230 repo_name = 'vcs_test_new_to_delete'
231 description = 'description for newly created repo'
232 private = False
233 response = self.app.post(url('repos'), {'repo_name': repo_name,
234 'repo_type': 'git',
235 'clone_uri': '',
236 'repo_group': '',
237 'description': description,
238 'private': private,
239 'landing_rev': 'tip'})
240 self.checkSessionFlash(response,
241 'created repository %s' % (repo_name))
242
243 #test if the repo was created in the database
244 new_repo = self.Session.query(Repository)\
245 .filter(Repository.repo_name == repo_name).one()
246
247 self.assertEqual(new_repo.repo_name, repo_name)
248 self.assertEqual(new_repo.description, description)
249
250 #test if repository is visible in the list ?
251 response = response.follow()
252
253 response.mustcontain(repo_name)
254
255 #test if repository was created on filesystem
256 try:
257 vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
258 except:
259 self.fail('no repo %s in filesystem' % repo_name)
260
261 response = self.app.delete(url('repo', repo_name=repo_name))
262
263 self.assertTrue('''deleted repository %s''' % (repo_name) in
264 response.session['flash'][0])
265
266 response.follow()
267
268 #check if repo was deleted from db
269 deleted_repo = self.Session.query(Repository)\
270 .filter(Repository.repo_name == repo_name).scalar()
271
272 self.assertEqual(deleted_repo, None)
273
274 self.assertEqual(os.path.isdir(os.path.join(TESTS_TMP_PATH, repo_name)),
275 False)
187
276
188 def test_delete_repo_with_group(self):
277 def test_delete_repo_with_group(self):
189 #TODO:
278 #TODO:
190 pass
279 pass
191
280
192
193 def test_delete_browser_fakeout(self):
281 def test_delete_browser_fakeout(self):
194 response = self.app.post(url('repo', repo_name=HG_REPO),
282 response = self.app.post(url('repo', repo_name=HG_REPO),
195 params=dict(_method='delete'))
283 params=dict(_method='delete'))
196
284
197 def test_show(self):
285 def test_show_hg(self):
198 self.log_user()
286 self.log_user()
199 response = self.app.get(url('repo', repo_name=HG_REPO))
287 response = self.app.get(url('repo', repo_name=HG_REPO))
200
288
201 def test_show_as_xml(self):
289 def test_show_git(self):
202 response = self.app.get(url('formatted_repo', repo_name=HG_REPO,
290 self.log_user()
203 format='xml'))
291 response = self.app.get(url('repo', repo_name=GIT_REPO))
292
204
293
205 def test_edit(self):
294 def test_edit(self):
206 response = self.app.get(url('edit_repo', repo_name=HG_REPO))
295 response = self.app.get(url('edit_repo', repo_name=HG_REPO))
207
208 def test_edit_as_xml(self):
209 response = self.app.get(url('formatted_edit_repo', repo_name=HG_REPO,
210 format='xml'))
@@ -1,133 +1,160 b''
1 from rhodecode.tests import *
1 from rhodecode.tests import *
2
2
3 from rhodecode.model.db import Repository
3 from rhodecode.model.db import Repository
4 from rhodecode.model.repo import RepoModel
4 from rhodecode.model.repo import RepoModel
5 from rhodecode.model.user import UserModel
5 from rhodecode.model.user import UserModel
6
6
7
7
8 class TestForksController(TestController):
8 class TestForksController(TestController):
9
9
10 def setUp(self):
10 def setUp(self):
11 self.username = u'forkuser'
11 self.username = u'forkuser'
12 self.password = u'qweqwe'
12 self.password = u'qweqwe'
13 self.u1 = UserModel().create_or_update(
13 self.u1 = UserModel().create_or_update(
14 username=self.username, password=self.password,
14 username=self.username, password=self.password,
15 email=u'fork_king@rhodecode.org', name=u'u1', lastname=u'u1'
15 email=u'fork_king@rhodecode.org', name=u'u1', lastname=u'u1'
16 )
16 )
17 self.Session.commit()
17 self.Session.commit()
18
18
19 def tearDown(self):
19 def tearDown(self):
20 self.Session.delete(self.u1)
20 self.Session.delete(self.u1)
21 self.Session.commit()
21 self.Session.commit()
22
22
23 def test_index(self):
23 def test_index(self):
24 self.log_user()
24 self.log_user()
25 repo_name = HG_REPO
25 repo_name = HG_REPO
26 response = self.app.get(url(controller='forks', action='forks',
26 response = self.app.get(url(controller='forks', action='forks',
27 repo_name=repo_name))
27 repo_name=repo_name))
28
28
29 self.assertTrue("""There are no forks yet""" in response.body)
29 self.assertTrue("""There are no forks yet""" in response.body)
30
30
31 def test_index_with_fork(self):
31 def test_index_with_fork_hg(self):
32 self.log_user()
32 self.log_user()
33
33
34 # create a fork
34 # create a fork
35 fork_name = HG_FORK
35 fork_name = HG_FORK
36 description = 'fork of vcs test'
36 description = 'fork of vcs test'
37 repo_name = HG_REPO
37 repo_name = HG_REPO
38 org_repo = Repository.get_by_repo_name(repo_name)
38 org_repo = Repository.get_by_repo_name(repo_name)
39 response = self.app.post(url(controller='forks',
39 response = self.app.post(url(controller='forks',
40 action='fork_create',
40 action='fork_create',
41 repo_name=repo_name),
41 repo_name=repo_name),
42 {'repo_name':fork_name,
42 {'repo_name': fork_name,
43 'repo_group':'',
43 'repo_group': '',
44 'fork_parent_id':org_repo.repo_id,
44 'fork_parent_id': org_repo.repo_id,
45 'repo_type':'hg',
45 'repo_type': 'hg',
46 'description':description,
46 'description': description,
47 'private':'False'})
47 'private': 'False',
48 'landing_rev': 'tip'})
48
49
49 response = self.app.get(url(controller='forks', action='forks',
50 response = self.app.get(url(controller='forks', action='forks',
50 repo_name=repo_name))
51 repo_name=repo_name))
51
52
52 self.assertTrue("""<a href="/%s/summary">"""
53 response.mustcontain(
53 """vcs_test_hg_fork</a>""" % fork_name
54 """<a href="/%s/summary">%s</a>""" % (fork_name, fork_name)
54 in response.body)
55 )
56
57 #remove this fork
58 response = self.app.delete(url('repo', repo_name=fork_name))
59
60 def test_index_with_fork_git(self):
61 self.log_user()
62
63 # create a fork
64 fork_name = GIT_FORK
65 description = 'fork of vcs test'
66 repo_name = GIT_REPO
67 org_repo = Repository.get_by_repo_name(repo_name)
68 response = self.app.post(url(controller='forks',
69 action='fork_create',
70 repo_name=repo_name),
71 {'repo_name': fork_name,
72 'repo_group': '',
73 'fork_parent_id': org_repo.repo_id,
74 'repo_type': 'git',
75 'description': description,
76 'private': 'False',
77 'landing_rev': 'tip'})
78
79 response = self.app.get(url(controller='forks', action='forks',
80 repo_name=repo_name))
81
82 response.mustcontain(
83 """<a href="/%s/summary">%s</a>""" % (fork_name, fork_name)
84 )
55
85
56 #remove this fork
86 #remove this fork
57 response = self.app.delete(url('repo', repo_name=fork_name))
87 response = self.app.delete(url('repo', repo_name=fork_name))
58
88
59 def test_z_fork_create(self):
89 def test_z_fork_create(self):
60 self.log_user()
90 self.log_user()
61 fork_name = HG_FORK
91 fork_name = HG_FORK
62 description = 'fork of vcs test'
92 description = 'fork of vcs test'
63 repo_name = HG_REPO
93 repo_name = HG_REPO
64 org_repo = Repository.get_by_repo_name(repo_name)
94 org_repo = Repository.get_by_repo_name(repo_name)
65 response = self.app.post(url(controller='forks', action='fork_create',
95 response = self.app.post(url(controller='forks', action='fork_create',
66 repo_name=repo_name),
96 repo_name=repo_name),
67 {'repo_name':fork_name,
97 {'repo_name':fork_name,
68 'repo_group':'',
98 'repo_group':'',
69 'fork_parent_id':org_repo.repo_id,
99 'fork_parent_id':org_repo.repo_id,
70 'repo_type':'hg',
100 'repo_type':'hg',
71 'description':description,
101 'description':description,
72 'private':'False'})
102 'private':'False',
103 'landing_rev': 'tip'})
73
104
74 #test if we have a message that fork is ok
105 #test if we have a message that fork is ok
75 self.assertTrue('forked %s repository as %s' \
106 self.checkSessionFlash(response,
76 % (repo_name, fork_name) in response.session['flash'][0])
107 'forked %s repository as %s' % (repo_name, fork_name))
77
108
78 #test if the fork was created in the database
109 #test if the fork was created in the database
79 fork_repo = self.Session.query(Repository)\
110 fork_repo = self.Session.query(Repository)\
80 .filter(Repository.repo_name == fork_name).one()
111 .filter(Repository.repo_name == fork_name).one()
81
112
82 self.assertEqual(fork_repo.repo_name, fork_name)
113 self.assertEqual(fork_repo.repo_name, fork_name)
83 self.assertEqual(fork_repo.fork.repo_name, repo_name)
114 self.assertEqual(fork_repo.fork.repo_name, repo_name)
84
115
85 #test if fork is visible in the list ?
116 #test if fork is visible in the list ?
86 response = response.follow()
117 response = response.follow()
87
118
88 # check if fork is marked as fork
89 # wait for cache to expire
90 import time
91 time.sleep(10)
92 response = self.app.get(url(controller='summary', action='index',
119 response = self.app.get(url(controller='summary', action='index',
93 repo_name=fork_name))
120 repo_name=fork_name))
94
121
95 self.assertTrue('Fork of %s' % repo_name in response.body)
122 self.assertTrue('Fork of %s' % repo_name in response.body)
96
123
97 def test_zz_fork_permission_page(self):
124 def test_zz_fork_permission_page(self):
98 usr = self.log_user(self.username, self.password)['user_id']
125 usr = self.log_user(self.username, self.password)['user_id']
99 repo_name = HG_REPO
126 repo_name = HG_REPO
100
127
101 forks = self.Session.query(Repository)\
128 forks = self.Session.query(Repository)\
102 .filter(Repository.fork_id != None)\
129 .filter(Repository.fork_id != None)\
103 .all()
130 .all()
104 self.assertEqual(1, len(forks))
131 self.assertEqual(1, len(forks))
105
132
106 # set read permissions for this
133 # set read permissions for this
107 RepoModel().grant_user_permission(repo=forks[0],
134 RepoModel().grant_user_permission(repo=forks[0],
108 user=usr,
135 user=usr,
109 perm='repository.read')
136 perm='repository.read')
110 self.Session.commit()
137 self.Session.commit()
111
138
112 response = self.app.get(url(controller='forks', action='forks',
139 response = self.app.get(url(controller='forks', action='forks',
113 repo_name=repo_name))
140 repo_name=repo_name))
114
141
115 response.mustcontain('<div style="padding:5px 3px 3px 42px;">fork of vcs test</div>')
142 response.mustcontain('<div style="padding:5px 3px 3px 42px;">fork of vcs test</div>')
116
143
117 def test_zzz_fork_permission_page(self):
144 def test_zzz_fork_permission_page(self):
118 usr = self.log_user(self.username, self.password)['user_id']
145 usr = self.log_user(self.username, self.password)['user_id']
119 repo_name = HG_REPO
146 repo_name = HG_REPO
120
147
121 forks = self.Session.query(Repository)\
148 forks = self.Session.query(Repository)\
122 .filter(Repository.fork_id != None)\
149 .filter(Repository.fork_id != None)\
123 .all()
150 .all()
124 self.assertEqual(1, len(forks))
151 self.assertEqual(1, len(forks))
125
152
126 # set none
153 # set none
127 RepoModel().grant_user_permission(repo=forks[0],
154 RepoModel().grant_user_permission(repo=forks[0],
128 user=usr, perm='repository.none')
155 user=usr, perm='repository.none')
129 self.Session.commit()
156 self.Session.commit()
130 # fork shouldn't be there
157 # fork shouldn't be there
131 response = self.app.get(url(controller='forks', action='forks',
158 response = self.app.get(url(controller='forks', action='forks',
132 repo_name=repo_name))
159 repo_name=repo_name))
133 response.mustcontain('There are no forks yet')
160 response.mustcontain('There are no forks yet')
@@ -1,39 +1,37 b''
1 import os
1 from rhodecode.tests import *
2 from rhodecode.tests import *
2 import os
3 from nose.plugins.skip import SkipTest
3 from nose.plugins.skip import SkipTest
4
4
5
5
6 class TestSearchController(TestController):
6 class TestSearchController(TestController):
7
7
8 def test_index(self):
8 def test_index(self):
9 self.log_user()
9 self.log_user()
10 response = self.app.get(url(controller='search', action='index'))
10 response = self.app.get(url(controller='search', action='index'))
11
11
12 self.assertTrue('class="small" id="q" name="q" type="text"' in
12 self.assertTrue('class="small" id="q" name="q" type="text"' in
13 response.body)
13 response.body)
14 # Test response...
14 # Test response...
15
15
16 def test_empty_search(self):
16 def test_empty_search(self):
17 if os.path.isdir(self.index_location):
17 if os.path.isdir(self.index_location):
18 raise SkipTest('skipped due to existing index')
18 raise SkipTest('skipped due to existing index')
19 else:
19 else:
20 self.log_user()
20 self.log_user()
21 response = self.app.get(url(controller='search', action='index'),
21 response = self.app.get(url(controller='search', action='index'),
22 {'q': HG_REPO})
22 {'q': HG_REPO})
23 self.assertTrue('There is no index to search in. '
23 self.assertTrue('There is no index to search in. '
24 'Please run whoosh indexer' in response.body)
24 'Please run whoosh indexer' in response.body)
25
25
26 def test_normal_search(self):
26 def test_normal_search(self):
27 self.log_user()
27 self.log_user()
28 response = self.app.get(url(controller='search', action='index'),
28 response = self.app.get(url(controller='search', action='index'),
29 {'q': 'def repo'})
29 {'q': 'def repo'})
30 response.mustcontain('10 results')
30 response.mustcontain('39 results')
31 response.mustcontain('Permission denied')
32
31
33 def test_repo_search(self):
32 def test_repo_search(self):
34 self.log_user()
33 self.log_user()
35 response = self.app.get(url(controller='search', action='index'),
34 response = self.app.get(url(controller='search', action='index'),
36 {'q': 'repository:%s def test' % HG_REPO})
35 {'q': 'repository:%s def test' % HG_REPO})
37
36
38 response.mustcontain('4 results')
37 response.mustcontain('4 results')
39 response.mustcontain('Permission denied')
@@ -1,104 +1,104 b''
1 from rhodecode.tests import *
1 from rhodecode.tests import *
2 from rhodecode.model.db import Repository
2 from rhodecode.model.db import Repository
3 from rhodecode.lib.utils import invalidate_cache
3 from rhodecode.lib.utils import invalidate_cache
4
4
5
5
6 class TestSummaryController(TestController):
6 class TestSummaryController(TestController):
7
7
8 def test_index(self):
8 def test_index(self):
9 self.log_user()
9 self.log_user()
10 ID = Repository.get_by_repo_name(HG_REPO).repo_id
10 ID = Repository.get_by_repo_name(HG_REPO).repo_id
11 response = self.app.get(url(controller='summary',
11 response = self.app.get(url(controller='summary',
12 action='index',
12 action='index',
13 repo_name=HG_REPO))
13 repo_name=HG_REPO))
14
14
15 #repo type
15 #repo type
16 response.mustcontain(
16 response.mustcontain(
17 """<img style="margin-bottom:2px" class="icon" """
17 """<img style="margin-bottom:2px" class="icon" """
18 """title="Mercurial repository" alt="Mercurial repository" """
18 """title="Mercurial repository" alt="Mercurial repository" """
19 """src="/images/icons/hgicon.png"/>"""
19 """src="/images/icons/hgicon.png"/>"""
20 )
20 )
21 response.mustcontain(
21 response.mustcontain(
22 """<img style="margin-bottom:2px" class="icon" """
22 """<img style="margin-bottom:2px" class="icon" """
23 """title="public repository" alt="public """
23 """title="public repository" alt="public """
24 """repository" src="/images/icons/lock_open.png"/>"""
24 """repository" src="/images/icons/lock_open.png"/>"""
25 )
25 )
26
26
27 #codes stats
27 #codes stats
28 self._enable_stats()
28 self._enable_stats()
29
29
30 invalidate_cache('get_repo_cached_%s' % HG_REPO)
30 invalidate_cache('get_repo_cached_%s' % HG_REPO)
31 response = self.app.get(url(controller='summary', action='index',
31 response = self.app.get(url(controller='summary', action='index',
32 repo_name=HG_REPO))
32 repo_name=HG_REPO))
33 response.mustcontain(
33 response.mustcontain(
34 """var data = [["py", {"count": 42, "desc": ["Python"]}], """
34 """var data = [["py", {"count": 42, "desc": ["Python"]}], """
35 """["rst", {"count": 11, "desc": ["Rst"]}], """
35 """["rst", {"count": 11, "desc": ["Rst"]}], """
36 """["sh", {"count": 2, "desc": ["Bash"]}], """
36 """["sh", {"count": 2, "desc": ["Bash"]}], """
37 """["makefile", {"count": 1, "desc": ["Makefile", "Makefile"]}],"""
37 """["makefile", {"count": 1, "desc": ["Makefile", "Makefile"]}],"""
38 """ ["cfg", {"count": 1, "desc": ["Ini"]}], """
38 """ ["cfg", {"count": 1, "desc": ["Ini"]}], """
39 """["css", {"count": 1, "desc": ["Css"]}], """
39 """["css", {"count": 1, "desc": ["Css"]}], """
40 """["bat", {"count": 1, "desc": ["Batch"]}]];"""
40 """["bat", {"count": 1, "desc": ["Batch"]}]];"""
41 )
41 )
42
42
43 # clone url...
43 # clone url...
44 response.mustcontain("""<input style="width:80%%;margin-left:105px" type="text" id="clone_url" readonly="readonly" value="http://test_admin@localhost:80/%s"/>""" % HG_REPO)
44 response.mustcontain("""<input style="width:80%%;margin-left:105px" type="text" id="clone_url" readonly="readonly" value="http://test_admin@localhost:80/%s"/>""" % HG_REPO)
45 response.mustcontain("""<input style="display:none;width:80%%;margin-left:105px" type="text" id="clone_url_id" readonly="readonly" value="http://test_admin@localhost:80/_%s"/>""" % ID)
45 response.mustcontain("""<input style="display:none;width:80%%;margin-left:105px" type="text" id="clone_url_id" readonly="readonly" value="http://test_admin@localhost:80/_%s"/>""" % ID)
46
46
47 def test_index_git(self):
47 def test_index_git(self):
48 self.log_user()
48 self.log_user()
49 ID = Repository.get_by_repo_name(GIT_REPO).repo_id
49 ID = Repository.get_by_repo_name(GIT_REPO).repo_id
50 response = self.app.get(url(controller='summary',
50 response = self.app.get(url(controller='summary',
51 action='index',
51 action='index',
52 repo_name=GIT_REPO))
52 repo_name=GIT_REPO))
53
53
54 #repo type
54 #repo type
55 response.mustcontain(
55 response.mustcontain(
56 """<img style="margin-bottom:2px" class="icon" """
56 """<img style="margin-bottom:2px" class="icon" """
57 """title="Git repository" alt="Git repository" """
57 """title="Git repository" alt="Git repository" """
58 """src="/images/icons/giticon.png"/>"""
58 """src="/images/icons/giticon.png"/>"""
59 )
59 )
60 response.mustcontain(
60 response.mustcontain(
61 """<img style="margin-bottom:2px" class="icon" """
61 """<img style="margin-bottom:2px" class="icon" """
62 """title="public repository" alt="public """
62 """title="public repository" alt="public """
63 """repository" src="/images/icons/lock_open.png"/>"""
63 """repository" src="/images/icons/lock_open.png"/>"""
64 )
64 )
65
65
66 # clone url...
66 # clone url...
67 response.mustcontain("""<input style="width:80%%;margin-left:105px" type="text" id="clone_url" readonly="readonly" value="http://test_admin@localhost:80/%s"/>""" % GIT_REPO)
67 response.mustcontain("""<input style="width:80%%;margin-left:105px" type="text" id="clone_url" readonly="readonly" value="http://test_admin@localhost:80/%s"/>""" % GIT_REPO)
68 response.mustcontain("""<input style="display:none;width:80%%;margin-left:105px" type="text" id="clone_url_id" readonly="readonly" value="http://test_admin@localhost:80/_%s"/>""" % ID)
68 response.mustcontain("""<input style="display:none;width:80%%;margin-left:105px" type="text" id="clone_url_id" readonly="readonly" value="http://test_admin@localhost:80/_%s"/>""" % ID)
69
69
70 def test_index_by_id_hg(self):
70 def test_index_by_id_hg(self):
71 self.log_user()
71 self.log_user()
72 ID = Repository.get_by_repo_name(HG_REPO).repo_id
72 ID = Repository.get_by_repo_name(HG_REPO).repo_id
73 response = self.app.get(url(controller='summary',
73 response = self.app.get(url(controller='summary',
74 action='index',
74 action='index',
75 repo_name='_%s' % ID))
75 repo_name='_%s' % ID))
76
76
77 #repo type
77 #repo type
78 response.mustcontain("""<img style="margin-bottom:2px" class="icon" """
78 response.mustcontain("""<img style="margin-bottom:2px" class="icon" """
79 """title="Mercurial repository" alt="Mercurial """
79 """title="Mercurial repository" alt="Mercurial """
80 """repository" src="/images/icons/hgicon.png"/>""")
80 """repository" src="/images/icons/hgicon.png"/>""")
81 response.mustcontain("""<img style="margin-bottom:2px" class="icon" """
81 response.mustcontain("""<img style="margin-bottom:2px" class="icon" """
82 """title="public repository" alt="public """
82 """title="public repository" alt="public """
83 """repository" src="/images/icons/lock_open.png"/>""")
83 """repository" src="/images/icons/lock_open.png"/>""")
84
84
85 def test_index_by_id_git(self):
85 def test_index_by_id_git(self):
86 self.log_user()
86 self.log_user()
87 ID = Repository.get_by_repo_name(GIT_REPO).repo_id
87 ID = Repository.get_by_repo_name(GIT_REPO).repo_id
88 response = self.app.get(url(controller='summary',
88 response = self.app.get(url(controller='summary',
89 action='index',
89 action='index',
90 repo_name='_%s' % ID))
90 repo_name='_%s' % ID))
91
91
92 #repo type
92 #repo type
93 response.mustcontain("""<img style="margin-bottom:2px" class="icon" """
93 response.mustcontain("""<img style="margin-bottom:2px" class="icon" """
94 """title="Git repository" alt="Git """
94 """title="Git repository" alt="Git """
95 """repository" src="/images/icons/hgicon.png"/>""")
95 """repository" src="/images/icons/giticon.png"/>""")
96 response.mustcontain("""<img style="margin-bottom:2px" class="icon" """
96 response.mustcontain("""<img style="margin-bottom:2px" class="icon" """
97 """title="public repository" alt="public """
97 """title="public repository" alt="public """
98 """repository" src="/images/icons/lock_open.png"/>""")
98 """repository" src="/images/icons/lock_open.png"/>""")
99
99
100 def _enable_stats(self):
100 def _enable_stats(self):
101 r = Repository.get_by_repo_name(HG_REPO)
101 r = Repository.get_by_repo_name(HG_REPO)
102 r.enable_statistics = True
102 r.enable_statistics = True
103 self.Session.add(r)
103 self.Session.add(r)
104 self.Session.commit()
104 self.Session.commit()
@@ -1,715 +1,716 b''
1 import os
1 import os
2 import unittest
2 import unittest
3 from rhodecode.tests import *
3 from rhodecode.tests import *
4
4
5 from rhodecode.model.repos_group import ReposGroupModel
5 from rhodecode.model.repos_group import ReposGroupModel
6 from rhodecode.model.repo import RepoModel
6 from rhodecode.model.repo import RepoModel
7 from rhodecode.model.db import RepoGroup, User, Notification, UserNotification, \
7 from rhodecode.model.db import RepoGroup, User, Notification, UserNotification, \
8 UsersGroup, UsersGroupMember, Permission, UsersGroupRepoGroupToPerm,\
8 UsersGroup, UsersGroupMember, Permission, UsersGroupRepoGroupToPerm,\
9 Repository
9 Repository
10 from sqlalchemy.exc import IntegrityError
10 from sqlalchemy.exc import IntegrityError
11 from rhodecode.model.user import UserModel
11 from rhodecode.model.user import UserModel
12
12
13 from rhodecode.model.meta import Session
13 from rhodecode.model.meta import Session
14 from rhodecode.model.notification import NotificationModel
14 from rhodecode.model.notification import NotificationModel
15 from rhodecode.model.users_group import UsersGroupModel
15 from rhodecode.model.users_group import UsersGroupModel
16 from rhodecode.lib.auth import AuthUser
16 from rhodecode.lib.auth import AuthUser
17
17
18
18
19 def _make_group(path, desc='desc', parent_id=None,
19 def _make_group(path, desc='desc', parent_id=None,
20 skip_if_exists=False):
20 skip_if_exists=False):
21
21
22 gr = RepoGroup.get_by_group_name(path)
22 gr = RepoGroup.get_by_group_name(path)
23 if gr and skip_if_exists:
23 if gr and skip_if_exists:
24 return gr
24 return gr
25
25
26 gr = ReposGroupModel().create(path, desc, parent_id)
26 gr = ReposGroupModel().create(path, desc, parent_id)
27 return gr
27 return gr
28
28
29
29
30 class TestReposGroups(unittest.TestCase):
30 class TestReposGroups(unittest.TestCase):
31
31
32 def setUp(self):
32 def setUp(self):
33 self.g1 = _make_group('test1', skip_if_exists=True)
33 self.g1 = _make_group('test1', skip_if_exists=True)
34 Session.commit()
34 Session.commit()
35 self.g2 = _make_group('test2', skip_if_exists=True)
35 self.g2 = _make_group('test2', skip_if_exists=True)
36 Session.commit()
36 Session.commit()
37 self.g3 = _make_group('test3', skip_if_exists=True)
37 self.g3 = _make_group('test3', skip_if_exists=True)
38 Session.commit()
38 Session.commit()
39
39
40 def tearDown(self):
40 def tearDown(self):
41 print 'out'
41 print 'out'
42
42
43 def __check_path(self, *path):
43 def __check_path(self, *path):
44 """
44 """
45 Checks the path for existance !
45 Checks the path for existance !
46 """
46 """
47 path = [TESTS_TMP_PATH] + list(path)
47 path = [TESTS_TMP_PATH] + list(path)
48 path = os.path.join(*path)
48 path = os.path.join(*path)
49 return os.path.isdir(path)
49 return os.path.isdir(path)
50
50
51 def _check_folders(self):
51 def _check_folders(self):
52 print os.listdir(TESTS_TMP_PATH)
52 print os.listdir(TESTS_TMP_PATH)
53
53
54 def __delete_group(self, id_):
54 def __delete_group(self, id_):
55 ReposGroupModel().delete(id_)
55 ReposGroupModel().delete(id_)
56
56
57 def __update_group(self, id_, path, desc='desc', parent_id=None):
57 def __update_group(self, id_, path, desc='desc', parent_id=None):
58 form_data = dict(
58 form_data = dict(
59 group_name=path,
59 group_name=path,
60 group_description=desc,
60 group_description=desc,
61 group_parent_id=parent_id,
61 group_parent_id=parent_id,
62 perms_updates=[],
62 perms_updates=[],
63 perms_new=[]
63 perms_new=[]
64 )
64 )
65 gr = ReposGroupModel().update(id_, form_data)
65 gr = ReposGroupModel().update(id_, form_data)
66 return gr
66 return gr
67
67
68 def test_create_group(self):
68 def test_create_group(self):
69 g = _make_group('newGroup')
69 g = _make_group('newGroup')
70 self.assertEqual(g.full_path, 'newGroup')
70 self.assertEqual(g.full_path, 'newGroup')
71
71
72 self.assertTrue(self.__check_path('newGroup'))
72 self.assertTrue(self.__check_path('newGroup'))
73
73
74 def test_create_same_name_group(self):
74 def test_create_same_name_group(self):
75 self.assertRaises(IntegrityError, lambda:_make_group('newGroup'))
75 self.assertRaises(IntegrityError, lambda:_make_group('newGroup'))
76 Session.rollback()
76 Session.rollback()
77
77
78 def test_same_subgroup(self):
78 def test_same_subgroup(self):
79 sg1 = _make_group('sub1', parent_id=self.g1.group_id)
79 sg1 = _make_group('sub1', parent_id=self.g1.group_id)
80 self.assertEqual(sg1.parent_group, self.g1)
80 self.assertEqual(sg1.parent_group, self.g1)
81 self.assertEqual(sg1.full_path, 'test1/sub1')
81 self.assertEqual(sg1.full_path, 'test1/sub1')
82 self.assertTrue(self.__check_path('test1', 'sub1'))
82 self.assertTrue(self.__check_path('test1', 'sub1'))
83
83
84 ssg1 = _make_group('subsub1', parent_id=sg1.group_id)
84 ssg1 = _make_group('subsub1', parent_id=sg1.group_id)
85 self.assertEqual(ssg1.parent_group, sg1)
85 self.assertEqual(ssg1.parent_group, sg1)
86 self.assertEqual(ssg1.full_path, 'test1/sub1/subsub1')
86 self.assertEqual(ssg1.full_path, 'test1/sub1/subsub1')
87 self.assertTrue(self.__check_path('test1', 'sub1', 'subsub1'))
87 self.assertTrue(self.__check_path('test1', 'sub1', 'subsub1'))
88
88
89 def test_remove_group(self):
89 def test_remove_group(self):
90 sg1 = _make_group('deleteme')
90 sg1 = _make_group('deleteme')
91 self.__delete_group(sg1.group_id)
91 self.__delete_group(sg1.group_id)
92
92
93 self.assertEqual(RepoGroup.get(sg1.group_id), None)
93 self.assertEqual(RepoGroup.get(sg1.group_id), None)
94 self.assertFalse(self.__check_path('deteteme'))
94 self.assertFalse(self.__check_path('deteteme'))
95
95
96 sg1 = _make_group('deleteme', parent_id=self.g1.group_id)
96 sg1 = _make_group('deleteme', parent_id=self.g1.group_id)
97 self.__delete_group(sg1.group_id)
97 self.__delete_group(sg1.group_id)
98
98
99 self.assertEqual(RepoGroup.get(sg1.group_id), None)
99 self.assertEqual(RepoGroup.get(sg1.group_id), None)
100 self.assertFalse(self.__check_path('test1', 'deteteme'))
100 self.assertFalse(self.__check_path('test1', 'deteteme'))
101
101
102 def test_rename_single_group(self):
102 def test_rename_single_group(self):
103 sg1 = _make_group('initial')
103 sg1 = _make_group('initial')
104
104
105 new_sg1 = self.__update_group(sg1.group_id, 'after')
105 new_sg1 = self.__update_group(sg1.group_id, 'after')
106 self.assertTrue(self.__check_path('after'))
106 self.assertTrue(self.__check_path('after'))
107 self.assertEqual(RepoGroup.get_by_group_name('initial'), None)
107 self.assertEqual(RepoGroup.get_by_group_name('initial'), None)
108
108
109 def test_update_group_parent(self):
109 def test_update_group_parent(self):
110
110
111 sg1 = _make_group('initial', parent_id=self.g1.group_id)
111 sg1 = _make_group('initial', parent_id=self.g1.group_id)
112
112
113 new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g1.group_id)
113 new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g1.group_id)
114 self.assertTrue(self.__check_path('test1', 'after'))
114 self.assertTrue(self.__check_path('test1', 'after'))
115 self.assertEqual(RepoGroup.get_by_group_name('test1/initial'), None)
115 self.assertEqual(RepoGroup.get_by_group_name('test1/initial'), None)
116
116
117 new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g3.group_id)
117 new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g3.group_id)
118 self.assertTrue(self.__check_path('test3', 'after'))
118 self.assertTrue(self.__check_path('test3', 'after'))
119 self.assertEqual(RepoGroup.get_by_group_name('test3/initial'), None)
119 self.assertEqual(RepoGroup.get_by_group_name('test3/initial'), None)
120
120
121 new_sg1 = self.__update_group(sg1.group_id, 'hello')
121 new_sg1 = self.__update_group(sg1.group_id, 'hello')
122 self.assertTrue(self.__check_path('hello'))
122 self.assertTrue(self.__check_path('hello'))
123
123
124 self.assertEqual(RepoGroup.get_by_group_name('hello'), new_sg1)
124 self.assertEqual(RepoGroup.get_by_group_name('hello'), new_sg1)
125
125
126 def test_subgrouping_with_repo(self):
126 def test_subgrouping_with_repo(self):
127
127
128 g1 = _make_group('g1')
128 g1 = _make_group('g1')
129 g2 = _make_group('g2')
129 g2 = _make_group('g2')
130
130
131 # create new repo
131 # create new repo
132 form_data = dict(repo_name='john',
132 form_data = dict(repo_name='john',
133 repo_name_full='john',
133 repo_name_full='john',
134 fork_name=None,
134 fork_name=None,
135 description=None,
135 description=None,
136 repo_group=None,
136 repo_group=None,
137 private=False,
137 private=False,
138 repo_type='hg',
138 repo_type='hg',
139 clone_uri=None)
139 clone_uri=None,
140 landing_rev='tip')
140 cur_user = User.get_by_username(TEST_USER_ADMIN_LOGIN)
141 cur_user = User.get_by_username(TEST_USER_ADMIN_LOGIN)
141 r = RepoModel().create(form_data, cur_user)
142 r = RepoModel().create(form_data, cur_user)
142
143
143 self.assertEqual(r.repo_name, 'john')
144 self.assertEqual(r.repo_name, 'john')
144
145
145 # put repo into group
146 # put repo into group
146 form_data = form_data
147 form_data = form_data
147 form_data['repo_group'] = g1.group_id
148 form_data['repo_group'] = g1.group_id
148 form_data['perms_new'] = []
149 form_data['perms_new'] = []
149 form_data['perms_updates'] = []
150 form_data['perms_updates'] = []
150 RepoModel().update(r.repo_name, form_data)
151 RepoModel().update(r.repo_name, form_data)
151 self.assertEqual(r.repo_name, 'g1/john')
152 self.assertEqual(r.repo_name, 'g1/john')
152
153
153 self.__update_group(g1.group_id, 'g1', parent_id=g2.group_id)
154 self.__update_group(g1.group_id, 'g1', parent_id=g2.group_id)
154 self.assertTrue(self.__check_path('g2', 'g1'))
155 self.assertTrue(self.__check_path('g2', 'g1'))
155
156
156 # test repo
157 # test repo
157 self.assertEqual(r.repo_name, RepoGroup.url_sep().join(['g2', 'g1', r.just_name]))
158 self.assertEqual(r.repo_name, RepoGroup.url_sep().join(['g2', 'g1', r.just_name]))
158
159
159 def test_move_to_root(self):
160 def test_move_to_root(self):
160 g1 = _make_group('t11')
161 g1 = _make_group('t11')
161 Session.commit()
162 Session.commit()
162 g2 = _make_group('t22', parent_id=g1.group_id)
163 g2 = _make_group('t22', parent_id=g1.group_id)
163 Session.commit()
164 Session.commit()
164
165
165 self.assertEqual(g2.full_path, 't11/t22')
166 self.assertEqual(g2.full_path, 't11/t22')
166 self.assertTrue(self.__check_path('t11', 't22'))
167 self.assertTrue(self.__check_path('t11', 't22'))
167
168
168 g2 = self.__update_group(g2.group_id, 'g22', parent_id=None)
169 g2 = self.__update_group(g2.group_id, 'g22', parent_id=None)
169 Session.commit()
170 Session.commit()
170
171
171 self.assertEqual(g2.group_name, 'g22')
172 self.assertEqual(g2.group_name, 'g22')
172 # we moved out group from t1 to '' so it's full path should be 'g2'
173 # we moved out group from t1 to '' so it's full path should be 'g2'
173 self.assertEqual(g2.full_path, 'g22')
174 self.assertEqual(g2.full_path, 'g22')
174 self.assertFalse(self.__check_path('t11', 't22'))
175 self.assertFalse(self.__check_path('t11', 't22'))
175 self.assertTrue(self.__check_path('g22'))
176 self.assertTrue(self.__check_path('g22'))
176
177
177
178
178 class TestUser(unittest.TestCase):
179 class TestUser(unittest.TestCase):
179 def __init__(self, methodName='runTest'):
180 def __init__(self, methodName='runTest'):
180 Session.remove()
181 Session.remove()
181 super(TestUser, self).__init__(methodName=methodName)
182 super(TestUser, self).__init__(methodName=methodName)
182
183
183 def test_create_and_remove(self):
184 def test_create_and_remove(self):
184 usr = UserModel().create_or_update(username=u'test_user', password=u'qweqwe',
185 usr = UserModel().create_or_update(username=u'test_user', password=u'qweqwe',
185 email=u'u232@rhodecode.org',
186 email=u'u232@rhodecode.org',
186 name=u'u1', lastname=u'u1')
187 name=u'u1', lastname=u'u1')
187 Session.commit()
188 Session.commit()
188 self.assertEqual(User.get_by_username(u'test_user'), usr)
189 self.assertEqual(User.get_by_username(u'test_user'), usr)
189
190
190 # make users group
191 # make users group
191 users_group = UsersGroupModel().create('some_example_group')
192 users_group = UsersGroupModel().create('some_example_group')
192 Session.commit()
193 Session.commit()
193
194
194 UsersGroupModel().add_user_to_group(users_group, usr)
195 UsersGroupModel().add_user_to_group(users_group, usr)
195 Session.commit()
196 Session.commit()
196
197
197 self.assertEqual(UsersGroup.get(users_group.users_group_id), users_group)
198 self.assertEqual(UsersGroup.get(users_group.users_group_id), users_group)
198 self.assertEqual(UsersGroupMember.query().count(), 1)
199 self.assertEqual(UsersGroupMember.query().count(), 1)
199 UserModel().delete(usr.user_id)
200 UserModel().delete(usr.user_id)
200 Session.commit()
201 Session.commit()
201
202
202 self.assertEqual(UsersGroupMember.query().all(), [])
203 self.assertEqual(UsersGroupMember.query().all(), [])
203
204
204
205
205 class TestNotifications(unittest.TestCase):
206 class TestNotifications(unittest.TestCase):
206
207
207 def __init__(self, methodName='runTest'):
208 def __init__(self, methodName='runTest'):
208 Session.remove()
209 Session.remove()
209 self.u1 = UserModel().create_or_update(username=u'u1',
210 self.u1 = UserModel().create_or_update(username=u'u1',
210 password=u'qweqwe',
211 password=u'qweqwe',
211 email=u'u1@rhodecode.org',
212 email=u'u1@rhodecode.org',
212 name=u'u1', lastname=u'u1')
213 name=u'u1', lastname=u'u1')
213 Session.commit()
214 Session.commit()
214 self.u1 = self.u1.user_id
215 self.u1 = self.u1.user_id
215
216
216 self.u2 = UserModel().create_or_update(username=u'u2',
217 self.u2 = UserModel().create_or_update(username=u'u2',
217 password=u'qweqwe',
218 password=u'qweqwe',
218 email=u'u2@rhodecode.org',
219 email=u'u2@rhodecode.org',
219 name=u'u2', lastname=u'u3')
220 name=u'u2', lastname=u'u3')
220 Session.commit()
221 Session.commit()
221 self.u2 = self.u2.user_id
222 self.u2 = self.u2.user_id
222
223
223 self.u3 = UserModel().create_or_update(username=u'u3',
224 self.u3 = UserModel().create_or_update(username=u'u3',
224 password=u'qweqwe',
225 password=u'qweqwe',
225 email=u'u3@rhodecode.org',
226 email=u'u3@rhodecode.org',
226 name=u'u3', lastname=u'u3')
227 name=u'u3', lastname=u'u3')
227 Session.commit()
228 Session.commit()
228 self.u3 = self.u3.user_id
229 self.u3 = self.u3.user_id
229
230
230 super(TestNotifications, self).__init__(methodName=methodName)
231 super(TestNotifications, self).__init__(methodName=methodName)
231
232
232 def _clean_notifications(self):
233 def _clean_notifications(self):
233 for n in Notification.query().all():
234 for n in Notification.query().all():
234 Session.delete(n)
235 Session.delete(n)
235
236
236 Session.commit()
237 Session.commit()
237 self.assertEqual(Notification.query().all(), [])
238 self.assertEqual(Notification.query().all(), [])
238
239
239 def tearDown(self):
240 def tearDown(self):
240 self._clean_notifications()
241 self._clean_notifications()
241
242
242 def test_create_notification(self):
243 def test_create_notification(self):
243 self.assertEqual([], Notification.query().all())
244 self.assertEqual([], Notification.query().all())
244 self.assertEqual([], UserNotification.query().all())
245 self.assertEqual([], UserNotification.query().all())
245
246
246 usrs = [self.u1, self.u2]
247 usrs = [self.u1, self.u2]
247 notification = NotificationModel().create(created_by=self.u1,
248 notification = NotificationModel().create(created_by=self.u1,
248 subject=u'subj', body=u'hi there',
249 subject=u'subj', body=u'hi there',
249 recipients=usrs)
250 recipients=usrs)
250 Session.commit()
251 Session.commit()
251 u1 = User.get(self.u1)
252 u1 = User.get(self.u1)
252 u2 = User.get(self.u2)
253 u2 = User.get(self.u2)
253 u3 = User.get(self.u3)
254 u3 = User.get(self.u3)
254 notifications = Notification.query().all()
255 notifications = Notification.query().all()
255 self.assertEqual(len(notifications), 1)
256 self.assertEqual(len(notifications), 1)
256
257
257 unotification = UserNotification.query()\
258 unotification = UserNotification.query()\
258 .filter(UserNotification.notification == notification).all()
259 .filter(UserNotification.notification == notification).all()
259
260
260 self.assertEqual(notifications[0].recipients, [u1, u2])
261 self.assertEqual(notifications[0].recipients, [u1, u2])
261 self.assertEqual(notification.notification_id,
262 self.assertEqual(notification.notification_id,
262 notifications[0].notification_id)
263 notifications[0].notification_id)
263 self.assertEqual(len(unotification), len(usrs))
264 self.assertEqual(len(unotification), len(usrs))
264 self.assertEqual([x.user.user_id for x in unotification], usrs)
265 self.assertEqual([x.user.user_id for x in unotification], usrs)
265
266
266 def test_user_notifications(self):
267 def test_user_notifications(self):
267 self.assertEqual([], Notification.query().all())
268 self.assertEqual([], Notification.query().all())
268 self.assertEqual([], UserNotification.query().all())
269 self.assertEqual([], UserNotification.query().all())
269
270
270 notification1 = NotificationModel().create(created_by=self.u1,
271 notification1 = NotificationModel().create(created_by=self.u1,
271 subject=u'subj', body=u'hi there1',
272 subject=u'subj', body=u'hi there1',
272 recipients=[self.u3])
273 recipients=[self.u3])
273 Session.commit()
274 Session.commit()
274 notification2 = NotificationModel().create(created_by=self.u1,
275 notification2 = NotificationModel().create(created_by=self.u1,
275 subject=u'subj', body=u'hi there2',
276 subject=u'subj', body=u'hi there2',
276 recipients=[self.u3])
277 recipients=[self.u3])
277 Session.commit()
278 Session.commit()
278 u3 = Session.query(User).get(self.u3)
279 u3 = Session.query(User).get(self.u3)
279
280
280 self.assertEqual(sorted([x.notification for x in u3.notifications]),
281 self.assertEqual(sorted([x.notification for x in u3.notifications]),
281 sorted([notification2, notification1]))
282 sorted([notification2, notification1]))
282
283
283 def test_delete_notifications(self):
284 def test_delete_notifications(self):
284 self.assertEqual([], Notification.query().all())
285 self.assertEqual([], Notification.query().all())
285 self.assertEqual([], UserNotification.query().all())
286 self.assertEqual([], UserNotification.query().all())
286
287
287 notification = NotificationModel().create(created_by=self.u1,
288 notification = NotificationModel().create(created_by=self.u1,
288 subject=u'title', body=u'hi there3',
289 subject=u'title', body=u'hi there3',
289 recipients=[self.u3, self.u1, self.u2])
290 recipients=[self.u3, self.u1, self.u2])
290 Session.commit()
291 Session.commit()
291 notifications = Notification.query().all()
292 notifications = Notification.query().all()
292 self.assertTrue(notification in notifications)
293 self.assertTrue(notification in notifications)
293
294
294 Notification.delete(notification.notification_id)
295 Notification.delete(notification.notification_id)
295 Session.commit()
296 Session.commit()
296
297
297 notifications = Notification.query().all()
298 notifications = Notification.query().all()
298 self.assertFalse(notification in notifications)
299 self.assertFalse(notification in notifications)
299
300
300 un = UserNotification.query().filter(UserNotification.notification
301 un = UserNotification.query().filter(UserNotification.notification
301 == notification).all()
302 == notification).all()
302 self.assertEqual(un, [])
303 self.assertEqual(un, [])
303
304
304 def test_delete_association(self):
305 def test_delete_association(self):
305
306
306 self.assertEqual([], Notification.query().all())
307 self.assertEqual([], Notification.query().all())
307 self.assertEqual([], UserNotification.query().all())
308 self.assertEqual([], UserNotification.query().all())
308
309
309 notification = NotificationModel().create(created_by=self.u1,
310 notification = NotificationModel().create(created_by=self.u1,
310 subject=u'title', body=u'hi there3',
311 subject=u'title', body=u'hi there3',
311 recipients=[self.u3, self.u1, self.u2])
312 recipients=[self.u3, self.u1, self.u2])
312 Session.commit()
313 Session.commit()
313
314
314 unotification = UserNotification.query()\
315 unotification = UserNotification.query()\
315 .filter(UserNotification.notification ==
316 .filter(UserNotification.notification ==
316 notification)\
317 notification)\
317 .filter(UserNotification.user_id == self.u3)\
318 .filter(UserNotification.user_id == self.u3)\
318 .scalar()
319 .scalar()
319
320
320 self.assertEqual(unotification.user_id, self.u3)
321 self.assertEqual(unotification.user_id, self.u3)
321
322
322 NotificationModel().delete(self.u3,
323 NotificationModel().delete(self.u3,
323 notification.notification_id)
324 notification.notification_id)
324 Session.commit()
325 Session.commit()
325
326
326 u3notification = UserNotification.query()\
327 u3notification = UserNotification.query()\
327 .filter(UserNotification.notification ==
328 .filter(UserNotification.notification ==
328 notification)\
329 notification)\
329 .filter(UserNotification.user_id == self.u3)\
330 .filter(UserNotification.user_id == self.u3)\
330 .scalar()
331 .scalar()
331
332
332 self.assertEqual(u3notification, None)
333 self.assertEqual(u3notification, None)
333
334
334 # notification object is still there
335 # notification object is still there
335 self.assertEqual(Notification.query().all(), [notification])
336 self.assertEqual(Notification.query().all(), [notification])
336
337
337 #u1 and u2 still have assignments
338 #u1 and u2 still have assignments
338 u1notification = UserNotification.query()\
339 u1notification = UserNotification.query()\
339 .filter(UserNotification.notification ==
340 .filter(UserNotification.notification ==
340 notification)\
341 notification)\
341 .filter(UserNotification.user_id == self.u1)\
342 .filter(UserNotification.user_id == self.u1)\
342 .scalar()
343 .scalar()
343 self.assertNotEqual(u1notification, None)
344 self.assertNotEqual(u1notification, None)
344 u2notification = UserNotification.query()\
345 u2notification = UserNotification.query()\
345 .filter(UserNotification.notification ==
346 .filter(UserNotification.notification ==
346 notification)\
347 notification)\
347 .filter(UserNotification.user_id == self.u2)\
348 .filter(UserNotification.user_id == self.u2)\
348 .scalar()
349 .scalar()
349 self.assertNotEqual(u2notification, None)
350 self.assertNotEqual(u2notification, None)
350
351
351 def test_notification_counter(self):
352 def test_notification_counter(self):
352 self._clean_notifications()
353 self._clean_notifications()
353 self.assertEqual([], Notification.query().all())
354 self.assertEqual([], Notification.query().all())
354 self.assertEqual([], UserNotification.query().all())
355 self.assertEqual([], UserNotification.query().all())
355
356
356 NotificationModel().create(created_by=self.u1,
357 NotificationModel().create(created_by=self.u1,
357 subject=u'title', body=u'hi there_delete',
358 subject=u'title', body=u'hi there_delete',
358 recipients=[self.u3, self.u1])
359 recipients=[self.u3, self.u1])
359 Session.commit()
360 Session.commit()
360
361
361 self.assertEqual(NotificationModel()
362 self.assertEqual(NotificationModel()
362 .get_unread_cnt_for_user(self.u1), 1)
363 .get_unread_cnt_for_user(self.u1), 1)
363 self.assertEqual(NotificationModel()
364 self.assertEqual(NotificationModel()
364 .get_unread_cnt_for_user(self.u2), 0)
365 .get_unread_cnt_for_user(self.u2), 0)
365 self.assertEqual(NotificationModel()
366 self.assertEqual(NotificationModel()
366 .get_unread_cnt_for_user(self.u3), 1)
367 .get_unread_cnt_for_user(self.u3), 1)
367
368
368 notification = NotificationModel().create(created_by=self.u1,
369 notification = NotificationModel().create(created_by=self.u1,
369 subject=u'title', body=u'hi there3',
370 subject=u'title', body=u'hi there3',
370 recipients=[self.u3, self.u1, self.u2])
371 recipients=[self.u3, self.u1, self.u2])
371 Session.commit()
372 Session.commit()
372
373
373 self.assertEqual(NotificationModel()
374 self.assertEqual(NotificationModel()
374 .get_unread_cnt_for_user(self.u1), 2)
375 .get_unread_cnt_for_user(self.u1), 2)
375 self.assertEqual(NotificationModel()
376 self.assertEqual(NotificationModel()
376 .get_unread_cnt_for_user(self.u2), 1)
377 .get_unread_cnt_for_user(self.u2), 1)
377 self.assertEqual(NotificationModel()
378 self.assertEqual(NotificationModel()
378 .get_unread_cnt_for_user(self.u3), 2)
379 .get_unread_cnt_for_user(self.u3), 2)
379
380
380
381
381 class TestUsers(unittest.TestCase):
382 class TestUsers(unittest.TestCase):
382
383
383 def __init__(self, methodName='runTest'):
384 def __init__(self, methodName='runTest'):
384 super(TestUsers, self).__init__(methodName=methodName)
385 super(TestUsers, self).__init__(methodName=methodName)
385
386
386 def setUp(self):
387 def setUp(self):
387 self.u1 = UserModel().create_or_update(username=u'u1',
388 self.u1 = UserModel().create_or_update(username=u'u1',
388 password=u'qweqwe',
389 password=u'qweqwe',
389 email=u'u1@rhodecode.org',
390 email=u'u1@rhodecode.org',
390 name=u'u1', lastname=u'u1')
391 name=u'u1', lastname=u'u1')
391
392
392 def tearDown(self):
393 def tearDown(self):
393 perm = Permission.query().all()
394 perm = Permission.query().all()
394 for p in perm:
395 for p in perm:
395 UserModel().revoke_perm(self.u1, p)
396 UserModel().revoke_perm(self.u1, p)
396
397
397 UserModel().delete(self.u1)
398 UserModel().delete(self.u1)
398 Session.commit()
399 Session.commit()
399
400
400 def test_add_perm(self):
401 def test_add_perm(self):
401 perm = Permission.query().all()[0]
402 perm = Permission.query().all()[0]
402 UserModel().grant_perm(self.u1, perm)
403 UserModel().grant_perm(self.u1, perm)
403 Session.commit()
404 Session.commit()
404 self.assertEqual(UserModel().has_perm(self.u1, perm), True)
405 self.assertEqual(UserModel().has_perm(self.u1, perm), True)
405
406
406 def test_has_perm(self):
407 def test_has_perm(self):
407 perm = Permission.query().all()
408 perm = Permission.query().all()
408 for p in perm:
409 for p in perm:
409 has_p = UserModel().has_perm(self.u1, p)
410 has_p = UserModel().has_perm(self.u1, p)
410 self.assertEqual(False, has_p)
411 self.assertEqual(False, has_p)
411
412
412 def test_revoke_perm(self):
413 def test_revoke_perm(self):
413 perm = Permission.query().all()[0]
414 perm = Permission.query().all()[0]
414 UserModel().grant_perm(self.u1, perm)
415 UserModel().grant_perm(self.u1, perm)
415 Session.commit()
416 Session.commit()
416 self.assertEqual(UserModel().has_perm(self.u1, perm), True)
417 self.assertEqual(UserModel().has_perm(self.u1, perm), True)
417
418
418 #revoke
419 #revoke
419 UserModel().revoke_perm(self.u1, perm)
420 UserModel().revoke_perm(self.u1, perm)
420 Session.commit()
421 Session.commit()
421 self.assertEqual(UserModel().has_perm(self.u1, perm), False)
422 self.assertEqual(UserModel().has_perm(self.u1, perm), False)
422
423
423
424
424 class TestPermissions(unittest.TestCase):
425 class TestPermissions(unittest.TestCase):
425 def __init__(self, methodName='runTest'):
426 def __init__(self, methodName='runTest'):
426 super(TestPermissions, self).__init__(methodName=methodName)
427 super(TestPermissions, self).__init__(methodName=methodName)
427
428
428 def setUp(self):
429 def setUp(self):
429 self.u1 = UserModel().create_or_update(
430 self.u1 = UserModel().create_or_update(
430 username=u'u1', password=u'qweqwe',
431 username=u'u1', password=u'qweqwe',
431 email=u'u1@rhodecode.org', name=u'u1', lastname=u'u1'
432 email=u'u1@rhodecode.org', name=u'u1', lastname=u'u1'
432 )
433 )
433 self.u2 = UserModel().create_or_update(
434 self.u2 = UserModel().create_or_update(
434 username=u'u2', password=u'qweqwe',
435 username=u'u2', password=u'qweqwe',
435 email=u'u2@rhodecode.org', name=u'u2', lastname=u'u2'
436 email=u'u2@rhodecode.org', name=u'u2', lastname=u'u2'
436 )
437 )
437 self.anon = User.get_by_username('default')
438 self.anon = User.get_by_username('default')
438 self.a1 = UserModel().create_or_update(
439 self.a1 = UserModel().create_or_update(
439 username=u'a1', password=u'qweqwe',
440 username=u'a1', password=u'qweqwe',
440 email=u'a1@rhodecode.org', name=u'a1', lastname=u'a1', admin=True
441 email=u'a1@rhodecode.org', name=u'a1', lastname=u'a1', admin=True
441 )
442 )
442 Session.commit()
443 Session.commit()
443
444
444 def tearDown(self):
445 def tearDown(self):
445 if hasattr(self, 'test_repo'):
446 if hasattr(self, 'test_repo'):
446 RepoModel().delete(repo=self.test_repo)
447 RepoModel().delete(repo=self.test_repo)
447 UserModel().delete(self.u1)
448 UserModel().delete(self.u1)
448 UserModel().delete(self.u2)
449 UserModel().delete(self.u2)
449 UserModel().delete(self.a1)
450 UserModel().delete(self.a1)
450 if hasattr(self, 'g1'):
451 if hasattr(self, 'g1'):
451 ReposGroupModel().delete(self.g1.group_id)
452 ReposGroupModel().delete(self.g1.group_id)
452 if hasattr(self, 'g2'):
453 if hasattr(self, 'g2'):
453 ReposGroupModel().delete(self.g2.group_id)
454 ReposGroupModel().delete(self.g2.group_id)
454
455
455 if hasattr(self, 'ug1'):
456 if hasattr(self, 'ug1'):
456 UsersGroupModel().delete(self.ug1, force=True)
457 UsersGroupModel().delete(self.ug1, force=True)
457
458
458 Session.commit()
459 Session.commit()
459
460
460 def test_default_perms_set(self):
461 def test_default_perms_set(self):
461 u1_auth = AuthUser(user_id=self.u1.user_id)
462 u1_auth = AuthUser(user_id=self.u1.user_id)
462 perms = {
463 perms = {
463 'repositories_groups': {},
464 'repositories_groups': {},
464 'global': set([u'hg.create.repository', u'repository.read',
465 'global': set([u'hg.create.repository', u'repository.read',
465 u'hg.register.manual_activate']),
466 u'hg.register.manual_activate']),
466 'repositories': {u'vcs_test_hg': u'repository.read'}
467 'repositories': {u'vcs_test_hg': u'repository.read'}
467 }
468 }
468 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
469 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
469 perms['repositories'][HG_REPO])
470 perms['repositories'][HG_REPO])
470 new_perm = 'repository.write'
471 new_perm = 'repository.write'
471 RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1, perm=new_perm)
472 RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1, perm=new_perm)
472 Session.commit()
473 Session.commit()
473
474
474 u1_auth = AuthUser(user_id=self.u1.user_id)
475 u1_auth = AuthUser(user_id=self.u1.user_id)
475 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO], new_perm)
476 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO], new_perm)
476
477
477 def test_default_admin_perms_set(self):
478 def test_default_admin_perms_set(self):
478 a1_auth = AuthUser(user_id=self.a1.user_id)
479 a1_auth = AuthUser(user_id=self.a1.user_id)
479 perms = {
480 perms = {
480 'repositories_groups': {},
481 'repositories_groups': {},
481 'global': set([u'hg.admin']),
482 'global': set([u'hg.admin']),
482 'repositories': {u'vcs_test_hg': u'repository.admin'}
483 'repositories': {u'vcs_test_hg': u'repository.admin'}
483 }
484 }
484 self.assertEqual(a1_auth.permissions['repositories'][HG_REPO],
485 self.assertEqual(a1_auth.permissions['repositories'][HG_REPO],
485 perms['repositories'][HG_REPO])
486 perms['repositories'][HG_REPO])
486 new_perm = 'repository.write'
487 new_perm = 'repository.write'
487 RepoModel().grant_user_permission(repo=HG_REPO, user=self.a1, perm=new_perm)
488 RepoModel().grant_user_permission(repo=HG_REPO, user=self.a1, perm=new_perm)
488 Session.commit()
489 Session.commit()
489 # cannot really downgrade admins permissions !? they still get's set as
490 # cannot really downgrade admins permissions !? they still get's set as
490 # admin !
491 # admin !
491 u1_auth = AuthUser(user_id=self.a1.user_id)
492 u1_auth = AuthUser(user_id=self.a1.user_id)
492 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
493 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
493 perms['repositories'][HG_REPO])
494 perms['repositories'][HG_REPO])
494
495
495 def test_default_group_perms(self):
496 def test_default_group_perms(self):
496 self.g1 = _make_group('test1', skip_if_exists=True)
497 self.g1 = _make_group('test1', skip_if_exists=True)
497 self.g2 = _make_group('test2', skip_if_exists=True)
498 self.g2 = _make_group('test2', skip_if_exists=True)
498 u1_auth = AuthUser(user_id=self.u1.user_id)
499 u1_auth = AuthUser(user_id=self.u1.user_id)
499 perms = {
500 perms = {
500 'repositories_groups': {u'test1': 'group.read', u'test2': 'group.read'},
501 'repositories_groups': {u'test1': 'group.read', u'test2': 'group.read'},
501 'global': set([u'hg.create.repository', u'repository.read', u'hg.register.manual_activate']),
502 'global': set([u'hg.create.repository', u'repository.read', u'hg.register.manual_activate']),
502 'repositories': {u'vcs_test_hg': u'repository.read'}
503 'repositories': {u'vcs_test_hg': u'repository.read'}
503 }
504 }
504 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
505 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
505 perms['repositories'][HG_REPO])
506 perms['repositories'][HG_REPO])
506 self.assertEqual(u1_auth.permissions['repositories_groups'],
507 self.assertEqual(u1_auth.permissions['repositories_groups'],
507 perms['repositories_groups'])
508 perms['repositories_groups'])
508
509
509 def test_default_admin_group_perms(self):
510 def test_default_admin_group_perms(self):
510 self.g1 = _make_group('test1', skip_if_exists=True)
511 self.g1 = _make_group('test1', skip_if_exists=True)
511 self.g2 = _make_group('test2', skip_if_exists=True)
512 self.g2 = _make_group('test2', skip_if_exists=True)
512 a1_auth = AuthUser(user_id=self.a1.user_id)
513 a1_auth = AuthUser(user_id=self.a1.user_id)
513 perms = {
514 perms = {
514 'repositories_groups': {u'test1': 'group.admin', u'test2': 'group.admin'},
515 'repositories_groups': {u'test1': 'group.admin', u'test2': 'group.admin'},
515 'global': set(['hg.admin']),
516 'global': set(['hg.admin']),
516 'repositories': {u'vcs_test_hg': 'repository.admin'}
517 'repositories': {u'vcs_test_hg': 'repository.admin'}
517 }
518 }
518
519
519 self.assertEqual(a1_auth.permissions['repositories'][HG_REPO],
520 self.assertEqual(a1_auth.permissions['repositories'][HG_REPO],
520 perms['repositories'][HG_REPO])
521 perms['repositories'][HG_REPO])
521 self.assertEqual(a1_auth.permissions['repositories_groups'],
522 self.assertEqual(a1_auth.permissions['repositories_groups'],
522 perms['repositories_groups'])
523 perms['repositories_groups'])
523
524
524 def test_propagated_permission_from_users_group(self):
525 def test_propagated_permission_from_users_group(self):
525 # make group
526 # make group
526 self.ug1 = UsersGroupModel().create('G1')
527 self.ug1 = UsersGroupModel().create('G1')
527 # add user to group
528 # add user to group
528 UsersGroupModel().add_user_to_group(self.ug1, self.u1)
529 UsersGroupModel().add_user_to_group(self.ug1, self.u1)
529
530
530 # set permission to lower
531 # set permission to lower
531 new_perm = 'repository.none'
532 new_perm = 'repository.none'
532 RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1, perm=new_perm)
533 RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1, perm=new_perm)
533 Session.commit()
534 Session.commit()
534 u1_auth = AuthUser(user_id=self.u1.user_id)
535 u1_auth = AuthUser(user_id=self.u1.user_id)
535 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
536 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
536 new_perm)
537 new_perm)
537
538
538 # grant perm for group this should override permission from user
539 # grant perm for group this should override permission from user
539 new_perm = 'repository.write'
540 new_perm = 'repository.write'
540 RepoModel().grant_users_group_permission(repo=HG_REPO,
541 RepoModel().grant_users_group_permission(repo=HG_REPO,
541 group_name=self.ug1,
542 group_name=self.ug1,
542 perm=new_perm)
543 perm=new_perm)
543 # check perms
544 # check perms
544 u1_auth = AuthUser(user_id=self.u1.user_id)
545 u1_auth = AuthUser(user_id=self.u1.user_id)
545 perms = {
546 perms = {
546 'repositories_groups': {},
547 'repositories_groups': {},
547 'global': set([u'hg.create.repository', u'repository.read',
548 'global': set([u'hg.create.repository', u'repository.read',
548 u'hg.register.manual_activate']),
549 u'hg.register.manual_activate']),
549 'repositories': {u'vcs_test_hg': u'repository.read'}
550 'repositories': {u'vcs_test_hg': u'repository.read'}
550 }
551 }
551 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
552 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
552 new_perm)
553 new_perm)
553 self.assertEqual(u1_auth.permissions['repositories_groups'],
554 self.assertEqual(u1_auth.permissions['repositories_groups'],
554 perms['repositories_groups'])
555 perms['repositories_groups'])
555
556
556 def test_propagated_permission_from_users_group_lower_weight(self):
557 def test_propagated_permission_from_users_group_lower_weight(self):
557 # make group
558 # make group
558 self.ug1 = UsersGroupModel().create('G1')
559 self.ug1 = UsersGroupModel().create('G1')
559 # add user to group
560 # add user to group
560 UsersGroupModel().add_user_to_group(self.ug1, self.u1)
561 UsersGroupModel().add_user_to_group(self.ug1, self.u1)
561
562
562 # set permission to lower
563 # set permission to lower
563 new_perm_h = 'repository.write'
564 new_perm_h = 'repository.write'
564 RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1,
565 RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1,
565 perm=new_perm_h)
566 perm=new_perm_h)
566 Session.commit()
567 Session.commit()
567 u1_auth = AuthUser(user_id=self.u1.user_id)
568 u1_auth = AuthUser(user_id=self.u1.user_id)
568 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
569 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
569 new_perm_h)
570 new_perm_h)
570
571
571 # grant perm for group this should NOT override permission from user
572 # grant perm for group this should NOT override permission from user
572 # since it's lower than granted
573 # since it's lower than granted
573 new_perm_l = 'repository.read'
574 new_perm_l = 'repository.read'
574 RepoModel().grant_users_group_permission(repo=HG_REPO,
575 RepoModel().grant_users_group_permission(repo=HG_REPO,
575 group_name=self.ug1,
576 group_name=self.ug1,
576 perm=new_perm_l)
577 perm=new_perm_l)
577 # check perms
578 # check perms
578 u1_auth = AuthUser(user_id=self.u1.user_id)
579 u1_auth = AuthUser(user_id=self.u1.user_id)
579 perms = {
580 perms = {
580 'repositories_groups': {},
581 'repositories_groups': {},
581 'global': set([u'hg.create.repository', u'repository.read',
582 'global': set([u'hg.create.repository', u'repository.read',
582 u'hg.register.manual_activate']),
583 u'hg.register.manual_activate']),
583 'repositories': {u'vcs_test_hg': u'repository.write'}
584 'repositories': {u'vcs_test_hg': u'repository.write'}
584 }
585 }
585 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
586 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
586 new_perm_h)
587 new_perm_h)
587 self.assertEqual(u1_auth.permissions['repositories_groups'],
588 self.assertEqual(u1_auth.permissions['repositories_groups'],
588 perms['repositories_groups'])
589 perms['repositories_groups'])
589
590
590 def test_repo_in_group_permissions(self):
591 def test_repo_in_group_permissions(self):
591 self.g1 = _make_group('group1', skip_if_exists=True)
592 self.g1 = _make_group('group1', skip_if_exists=True)
592 self.g2 = _make_group('group2', skip_if_exists=True)
593 self.g2 = _make_group('group2', skip_if_exists=True)
593 Session.commit()
594 Session.commit()
594 # both perms should be read !
595 # both perms should be read !
595 u1_auth = AuthUser(user_id=self.u1.user_id)
596 u1_auth = AuthUser(user_id=self.u1.user_id)
596 self.assertEqual(u1_auth.permissions['repositories_groups'],
597 self.assertEqual(u1_auth.permissions['repositories_groups'],
597 {u'group1': u'group.read', u'group2': u'group.read'})
598 {u'group1': u'group.read', u'group2': u'group.read'})
598
599
599 a1_auth = AuthUser(user_id=self.anon.user_id)
600 a1_auth = AuthUser(user_id=self.anon.user_id)
600 self.assertEqual(a1_auth.permissions['repositories_groups'],
601 self.assertEqual(a1_auth.permissions['repositories_groups'],
601 {u'group1': u'group.read', u'group2': u'group.read'})
602 {u'group1': u'group.read', u'group2': u'group.read'})
602
603
603 #Change perms to none for both groups
604 #Change perms to none for both groups
604 ReposGroupModel().grant_user_permission(repos_group=self.g1,
605 ReposGroupModel().grant_user_permission(repos_group=self.g1,
605 user=self.anon,
606 user=self.anon,
606 perm='group.none')
607 perm='group.none')
607 ReposGroupModel().grant_user_permission(repos_group=self.g2,
608 ReposGroupModel().grant_user_permission(repos_group=self.g2,
608 user=self.anon,
609 user=self.anon,
609 perm='group.none')
610 perm='group.none')
610
611
611
612 u1_auth = AuthUser(user_id=self.u1.user_id)
612 u1_auth = AuthUser(user_id=self.u1.user_id)
613 self.assertEqual(u1_auth.permissions['repositories_groups'],
613 self.assertEqual(u1_auth.permissions['repositories_groups'],
614 {u'group1': u'group.none', u'group2': u'group.none'})
614 {u'group1': u'group.none', u'group2': u'group.none'})
615
615
616 a1_auth = AuthUser(user_id=self.anon.user_id)
616 a1_auth = AuthUser(user_id=self.anon.user_id)
617 self.assertEqual(a1_auth.permissions['repositories_groups'],
617 self.assertEqual(a1_auth.permissions['repositories_groups'],
618 {u'group1': u'group.none', u'group2': u'group.none'})
618 {u'group1': u'group.none', u'group2': u'group.none'})
619
619
620 # add repo to group
620 # add repo to group
621 form_data = {
621 form_data = {
622 'repo_name':HG_REPO,
622 'repo_name': HG_REPO,
623 'repo_name_full':RepoGroup.url_sep().join([self.g1.group_name,HG_REPO]),
623 'repo_name_full': RepoGroup.url_sep().join([self.g1.group_name,HG_REPO]),
624 'repo_type':'hg',
624 'repo_type': 'hg',
625 'clone_uri':'',
625 'clone_uri': '',
626 'repo_group':self.g1.group_id,
626 'repo_group': self.g1.group_id,
627 'description':'desc',
627 'description': 'desc',
628 'private':False
628 'private': False,
629 'landing_rev': 'tip'
629 }
630 }
630 self.test_repo = RepoModel().create(form_data, cur_user=self.u1)
631 self.test_repo = RepoModel().create(form_data, cur_user=self.u1)
631 Session.commit()
632 Session.commit()
632
633
633 u1_auth = AuthUser(user_id=self.u1.user_id)
634 u1_auth = AuthUser(user_id=self.u1.user_id)
634 self.assertEqual(u1_auth.permissions['repositories_groups'],
635 self.assertEqual(u1_auth.permissions['repositories_groups'],
635 {u'group1': u'group.none', u'group2': u'group.none'})
636 {u'group1': u'group.none', u'group2': u'group.none'})
636
637
637 a1_auth = AuthUser(user_id=self.anon.user_id)
638 a1_auth = AuthUser(user_id=self.anon.user_id)
638 self.assertEqual(a1_auth.permissions['repositories_groups'],
639 self.assertEqual(a1_auth.permissions['repositories_groups'],
639 {u'group1': u'group.none', u'group2': u'group.none'})
640 {u'group1': u'group.none', u'group2': u'group.none'})
640
641
641 #grant permission for u2 !
642 #grant permission for u2 !
642 ReposGroupModel().grant_user_permission(repos_group=self.g1,
643 ReposGroupModel().grant_user_permission(repos_group=self.g1,
643 user=self.u2,
644 user=self.u2,
644 perm='group.read')
645 perm='group.read')
645 ReposGroupModel().grant_user_permission(repos_group=self.g2,
646 ReposGroupModel().grant_user_permission(repos_group=self.g2,
646 user=self.u2,
647 user=self.u2,
647 perm='group.read')
648 perm='group.read')
648 Session.commit()
649 Session.commit()
649 self.assertNotEqual(self.u1, self.u2)
650 self.assertNotEqual(self.u1, self.u2)
650 #u1 and anon should have not change perms while u2 should !
651 #u1 and anon should have not change perms while u2 should !
651 u1_auth = AuthUser(user_id=self.u1.user_id)
652 u1_auth = AuthUser(user_id=self.u1.user_id)
652 self.assertEqual(u1_auth.permissions['repositories_groups'],
653 self.assertEqual(u1_auth.permissions['repositories_groups'],
653 {u'group1': u'group.none', u'group2': u'group.none'})
654 {u'group1': u'group.none', u'group2': u'group.none'})
654
655
655 u2_auth = AuthUser(user_id=self.u2.user_id)
656 u2_auth = AuthUser(user_id=self.u2.user_id)
656 self.assertEqual(u2_auth.permissions['repositories_groups'],
657 self.assertEqual(u2_auth.permissions['repositories_groups'],
657 {u'group1': u'group.read', u'group2': u'group.read'})
658 {u'group1': u'group.read', u'group2': u'group.read'})
658
659
659 a1_auth = AuthUser(user_id=self.anon.user_id)
660 a1_auth = AuthUser(user_id=self.anon.user_id)
660 self.assertEqual(a1_auth.permissions['repositories_groups'],
661 self.assertEqual(a1_auth.permissions['repositories_groups'],
661 {u'group1': u'group.none', u'group2': u'group.none'})
662 {u'group1': u'group.none', u'group2': u'group.none'})
662
663
663 def test_repo_group_user_as_user_group_member(self):
664 def test_repo_group_user_as_user_group_member(self):
664 # create Group1
665 # create Group1
665 self.g1 = _make_group('group1', skip_if_exists=True)
666 self.g1 = _make_group('group1', skip_if_exists=True)
666 Session.commit()
667 Session.commit()
667 a1_auth = AuthUser(user_id=self.anon.user_id)
668 a1_auth = AuthUser(user_id=self.anon.user_id)
668
669
669 self.assertEqual(a1_auth.permissions['repositories_groups'],
670 self.assertEqual(a1_auth.permissions['repositories_groups'],
670 {u'group1': u'group.read'})
671 {u'group1': u'group.read'})
671
672
672 # set default permission to none
673 # set default permission to none
673 ReposGroupModel().grant_user_permission(repos_group=self.g1,
674 ReposGroupModel().grant_user_permission(repos_group=self.g1,
674 user=self.anon,
675 user=self.anon,
675 perm='group.none')
676 perm='group.none')
676 # make group
677 # make group
677 self.ug1 = UsersGroupModel().create('G1')
678 self.ug1 = UsersGroupModel().create('G1')
678 # add user to group
679 # add user to group
679 UsersGroupModel().add_user_to_group(self.ug1, self.u1)
680 UsersGroupModel().add_user_to_group(self.ug1, self.u1)
680 Session.commit()
681 Session.commit()
681
682
682 # check if user is in the group
683 # check if user is in the group
683 membrs = [x.user_id for x in UsersGroupModel().get(self.ug1.users_group_id).members]
684 membrs = [x.user_id for x in UsersGroupModel().get(self.ug1.users_group_id).members]
684 self.assertEqual(membrs, [self.u1.user_id])
685 self.assertEqual(membrs, [self.u1.user_id])
685 # add some user to that group
686 # add some user to that group
686
687
687 # check his permissions
688 # check his permissions
688 a1_auth = AuthUser(user_id=self.anon.user_id)
689 a1_auth = AuthUser(user_id=self.anon.user_id)
689 self.assertEqual(a1_auth.permissions['repositories_groups'],
690 self.assertEqual(a1_auth.permissions['repositories_groups'],
690 {u'group1': u'group.none'})
691 {u'group1': u'group.none'})
691
692
692 u1_auth = AuthUser(user_id=self.u1.user_id)
693 u1_auth = AuthUser(user_id=self.u1.user_id)
693 self.assertEqual(u1_auth.permissions['repositories_groups'],
694 self.assertEqual(u1_auth.permissions['repositories_groups'],
694 {u'group1': u'group.none'})
695 {u'group1': u'group.none'})
695
696
696 # grant ug1 read permissions for
697 # grant ug1 read permissions for
697 ReposGroupModel().grant_users_group_permission(repos_group=self.g1,
698 ReposGroupModel().grant_users_group_permission(repos_group=self.g1,
698 group_name=self.ug1,
699 group_name=self.ug1,
699 perm='group.read')
700 perm='group.read')
700 Session.commit()
701 Session.commit()
701 # check if the
702 # check if the
702 obj = Session.query(UsersGroupRepoGroupToPerm)\
703 obj = Session.query(UsersGroupRepoGroupToPerm)\
703 .filter(UsersGroupRepoGroupToPerm.group == self.g1)\
704 .filter(UsersGroupRepoGroupToPerm.group == self.g1)\
704 .filter(UsersGroupRepoGroupToPerm.users_group == self.ug1)\
705 .filter(UsersGroupRepoGroupToPerm.users_group == self.ug1)\
705 .scalar()
706 .scalar()
706 self.assertEqual(obj.permission.permission_name, 'group.read')
707 self.assertEqual(obj.permission.permission_name, 'group.read')
707
708
708 a1_auth = AuthUser(user_id=self.anon.user_id)
709 a1_auth = AuthUser(user_id=self.anon.user_id)
709
710
710 self.assertEqual(a1_auth.permissions['repositories_groups'],
711 self.assertEqual(a1_auth.permissions['repositories_groups'],
711 {u'group1': u'group.none'})
712 {u'group1': u'group.none'})
712
713
713 u1_auth = AuthUser(user_id=self.u1.user_id)
714 u1_auth = AuthUser(user_id=self.u1.user_id)
714 self.assertEqual(u1_auth.permissions['repositories_groups'],
715 self.assertEqual(u1_auth.permissions['repositories_groups'],
715 {u'group1': u'group.read'})
716 {u'group1': u'group.read'})
@@ -1,56 +1,56 b''
1 """
1 """
2 Unit tests for vcs_ library.
2 Unit tests for vcs_ library.
3
3
4 In order to run tests we need to prepare our environment first. Tests would be
4 In order to run tests we need to prepare our environment first. Tests would be
5 run for each engine listed at ``conf.SCM_TESTS`` - keys are aliases from
5 run for each engine listed at ``conf.SCM_TESTS`` - keys are aliases from
6 ``vcs.backends.BACKENDS``.
6 ``vcs.backends.BACKENDS``.
7
7
8 For each SCM we run tests for, we need some repository. We would use
8 For each SCM we run tests for, we need some repository. We would use
9 repositories location from system environment variables or test suite defaults
9 repositories location from system environment variables or test suite defaults
10 - see ``conf`` module for more detail. We simply try to check if repository at
10 - see ``conf`` module for more detail. We simply try to check if repository at
11 certain location exists, if not we would try to fetch them. At ``test_vcs`` or
11 certain location exists, if not we would try to fetch them. At ``test_vcs`` or
12 ``test_common`` we run unit tests common for each repository type and for
12 ``test_common`` we run unit tests common for each repository type and for
13 example specific mercurial tests are located at ``test_hg`` module.
13 example specific mercurial tests are located at ``test_hg`` module.
14
14
15 Oh, and tests are run with ``unittest.collector`` wrapped by ``collector``
15 Oh, and tests are run with ``unittest.collector`` wrapped by ``collector``
16 function at ``tests/__init__.py``.
16 function at ``tests/__init__.py``.
17
17
18 .. _vcs: http://bitbucket.org/marcinkuzminski/vcs
18 .. _vcs: http://bitbucket.org/marcinkuzminski/vcs
19 .. _unittest: http://pypi.python.org/pypi/unittest
19 .. _unittest: http://pypi.python.org/pypi/unittest
20
20
21 """
21 """
22 import os
22 import os
23 from rhodecode.lib import vcs
23 from rhodecode.lib import vcs
24 from rhodecode.lib.vcs.utils.compat import unittest
24 from rhodecode.lib.vcs.utils.compat import unittest
25 from rhodecode.tests import *
25 from rhodecode.tests import *
26 from utils import VCSTestError, SCMFetcher
26 from utils import VCSTestError, SCMFetcher
27
27
28
28
29 def setup_package():
29 def setup_package():
30 """
30 """
31 Prepares whole package for tests which mainly means it would try to fetch
31 Prepares whole package for tests which mainly means it would try to fetch
32 test repositories or use already existing ones.
32 test repositories or use already existing ones.
33 """
33 """
34 fetchers = {
34 fetchers = {
35 'hg': {
35 'hg': {
36 'alias': 'hg',
36 'alias': 'hg',
37 'test_repo_path': TEST_HG_REPO,
37 'test_repo_path': TEST_HG_REPO,
38 'remote_repo': HG_REMOTE_REPO,
38 'remote_repo': HG_REMOTE_REPO,
39 'clone_cmd': 'hg clone',
39 'clone_cmd': 'hg clone',
40 },
40 },
41 'git': {
41 'git': {
42 'alias': 'git',
42 'alias': 'git',
43 'test_repo_path': TEST_GIT_REPO,
43 'test_repo_path': TEST_GIT_REPO,
44 'remote_repo': GIT_REMOTE_REPO,
44 'remote_repo': GIT_REMOTE_REPO,
45 'clone_cmd': 'git clone --bare',
45 'clone_cmd': 'git clone --bare',
46 },
46 },
47 }
47 }
48 try:
48 try:
49 for scm, fetcher_info in fetchers.items():
49 for scm, fetcher_info in fetchers.items():
50 fetcher = SCMFetcher(**fetcher_info)
50 fetcher = SCMFetcher(**fetcher_info)
51 fetcher.setup()
51 fetcher.setup()
52 except VCSTestError, err:
52 except VCSTestError, err:
53 raise RuntimeError(str(err))
53 raise RuntimeError(str(err))
54
54
55 start_dir = os.path.abspath(os.path.dirname(__file__))
55 #start_dir = os.path.abspath(os.path.dirname(__file__))
56 unittest.defaultTestLoader.discover(start_dir)
56 #unittest.defaultTestLoader.discover(start_dir)
@@ -1,61 +1,62 b''
1 """
1 """
2 Unit tests configuration module for vcs.
2 Unit tests configuration module for vcs.
3 """
3 """
4
4 import os
5 import os
5 import time
6 import time
6 import hashlib
7 import hashlib
7 import tempfile
8 import tempfile
8 import datetime
9 import datetime
9
10 from rhodecode.tests import *
10 from utils import get_normalized_path
11 from utils import get_normalized_path
11 from os.path import join as jn
12 from os.path import join as jn
12
13
13 __all__ = (
14 TEST_TMP_PATH = TESTS_TMP_PATH
14 'TEST_HG_REPO', 'TEST_GIT_REPO', 'HG_REMOTE_REPO', 'GIT_REMOTE_REPO',
15 #__all__ = (
15 'SCM_TESTS',
16 # 'TEST_HG_REPO', 'TEST_GIT_REPO', 'HG_REMOTE_REPO', 'GIT_REMOTE_REPO',
16 )
17 # 'SCM_TESTS',
17
18 #)
18 SCM_TESTS = ['hg', 'git']
19 #
19 uniq_suffix = str(int(time.mktime(datetime.datetime.now().timetuple())))
20 #SCM_TESTS = ['hg', 'git']
20
21 #uniq_suffix = str(int(time.mktime(datetime.datetime.now().timetuple())))
22 #
21 THIS = os.path.abspath(os.path.dirname(__file__))
23 THIS = os.path.abspath(os.path.dirname(__file__))
22
24 #
23 GIT_REMOTE_REPO = 'git://github.com/codeinn/vcs.git'
25 #GIT_REMOTE_REPO = 'git://github.com/codeinn/vcs.git'
24
26 #
25 TEST_TMP_PATH = os.environ.get('VCS_TEST_ROOT', '/tmp')
27 #TEST_TMP_PATH = os.environ.get('VCS_TEST_ROOT', '/tmp')
26 TEST_GIT_REPO = os.environ.get('VCS_TEST_GIT_REPO',
28 #TEST_GIT_REPO = os.environ.get('VCS_TEST_GIT_REPO',
27 jn(TEST_TMP_PATH, 'vcs-git'))
29 # jn(TEST_TMP_PATH, 'vcs-git'))
28 TEST_GIT_REPO_CLONE = os.environ.get('VCS_TEST_GIT_REPO_CLONE',
30 #TEST_GIT_REPO_CLONE = os.environ.get('VCS_TEST_GIT_REPO_CLONE',
29 jn(TEST_TMP_PATH, 'vcsgitclone%s' % uniq_suffix))
31 # jn(TEST_TMP_PATH, 'vcsgitclone%s' % uniq_suffix))
30 TEST_GIT_REPO_PULL = os.environ.get('VCS_TEST_GIT_REPO_PULL',
32 #TEST_GIT_REPO_PULL = os.environ.get('VCS_TEST_GIT_REPO_PULL',
31 jn(TEST_TMP_PATH, 'vcsgitpull%s' % uniq_suffix))
33 # jn(TEST_TMP_PATH, 'vcsgitpull%s' % uniq_suffix))
32
34 #
33 HG_REMOTE_REPO = 'http://bitbucket.org/marcinkuzminski/vcs'
35 #HG_REMOTE_REPO = 'http://bitbucket.org/marcinkuzminski/vcs'
34 TEST_HG_REPO = os.environ.get('VCS_TEST_HG_REPO',
36 #TEST_HG_REPO = os.environ.get('VCS_TEST_HG_REPO',
35 jn(TEST_TMP_PATH, 'vcs-hg'))
37 # jn(TEST_TMP_PATH, 'vcs-hg'))
36 TEST_HG_REPO_CLONE = os.environ.get('VCS_TEST_HG_REPO_CLONE',
38 #TEST_HG_REPO_CLONE = os.environ.get('VCS_TEST_HG_REPO_CLONE',
37 jn(TEST_TMP_PATH, 'vcshgclone%s' % uniq_suffix))
39 # jn(TEST_TMP_PATH, 'vcshgclone%s' % uniq_suffix))
38 TEST_HG_REPO_PULL = os.environ.get('VCS_TEST_HG_REPO_PULL',
40 #TEST_HG_REPO_PULL = os.environ.get('VCS_TEST_HG_REPO_PULL',
39 jn(TEST_TMP_PATH, 'vcshgpull%s' % uniq_suffix))
41 # jn(TEST_TMP_PATH, 'vcshgpull%s' % uniq_suffix))
40
42 #
41 TEST_DIR = os.environ.get('VCS_TEST_ROOT', tempfile.gettempdir())
43 #TEST_DIR = os.environ.get('VCS_TEST_ROOT', tempfile.gettempdir())
42 TEST_REPO_PREFIX = 'vcs-test'
44 #TEST_REPO_PREFIX = 'vcs-test'
43
45 #
44
46 #
45 def get_new_dir(title):
47 #def get_new_dir(title):
46 """
48 # """
47 Returns always new directory path.
49 # Returns always new directory path.
48 """
50 # """
49 name = TEST_REPO_PREFIX
51 # name = TEST_REPO_PREFIX
50 if title:
52 # if title:
51 name = '-'.join((name, title))
53 # name = '-'.join((name, title))
52 hex = hashlib.sha1(str(time.time())).hexdigest()
54 # hex = hashlib.sha1(str(time.time())).hexdigest()
53 name = '-'.join((name, hex))
55 # name = '-'.join((name, hex))
54 path = os.path.join(TEST_DIR, name)
56 # path = os.path.join(TEST_DIR, name)
55 return get_normalized_path(path)
57 # return get_normalized_path(path)
56
57
58
58 PACKAGE_DIR = os.path.abspath(os.path.join(
59 PACKAGE_DIR = os.path.abspath(os.path.join(
59 os.path.dirname(__file__), '..'))
60 os.path.dirname(__file__), '..'))
60
61
61 TEST_USER_CONFIG_FILE = jn(THIS, 'aconfig')
62 TEST_USER_CONFIG_FILE = jn(THIS, 'aconfig')
@@ -1,561 +1,557 b''
1 from __future__ import with_statement
1 from __future__ import with_statement
2
2
3 import os
3 import os
4 from rhodecode.lib.vcs.backends.hg import MercurialRepository, MercurialChangeset
4 from rhodecode.lib.vcs.backends.hg import MercurialRepository, MercurialChangeset
5 from rhodecode.lib.vcs.exceptions import RepositoryError, VCSError, NodeDoesNotExistError
5 from rhodecode.lib.vcs.exceptions import RepositoryError, VCSError, NodeDoesNotExistError
6 from rhodecode.lib.vcs.nodes import NodeKind, NodeState
6 from rhodecode.lib.vcs.nodes import NodeKind, NodeState
7 from conf import PACKAGE_DIR, TEST_HG_REPO, TEST_HG_REPO_CLONE, \
7 from conf import PACKAGE_DIR, TEST_HG_REPO, TEST_HG_REPO_CLONE, \
8 TEST_HG_REPO_PULL
8 TEST_HG_REPO_PULL
9 from rhodecode.lib.vcs.utils.compat import unittest
9 from rhodecode.lib.vcs.utils.compat import unittest
10
10
11
11
12 # Use only clean mercurial's ui
12 # Use only clean mercurial's ui
13 import mercurial.scmutil
13 import mercurial.scmutil
14 mercurial.scmutil.rcpath()
14 mercurial.scmutil.rcpath()
15 if mercurial.scmutil._rcpath:
15 if mercurial.scmutil._rcpath:
16 mercurial.scmutil._rcpath = mercurial.scmutil._rcpath[:1]
16 mercurial.scmutil._rcpath = mercurial.scmutil._rcpath[:1]
17
17
18
18
19 class MercurialRepositoryTest(unittest.TestCase):
19 class MercurialRepositoryTest(unittest.TestCase):
20
20
21 def __check_for_existing_repo(self):
21 def __check_for_existing_repo(self):
22 if os.path.exists(TEST_HG_REPO_CLONE):
22 if os.path.exists(TEST_HG_REPO_CLONE):
23 self.fail('Cannot test mercurial clone repo as location %s already '
23 self.fail('Cannot test mercurial clone repo as location %s already '
24 'exists. You should manually remove it first.'
24 'exists. You should manually remove it first.'
25 % TEST_HG_REPO_CLONE)
25 % TEST_HG_REPO_CLONE)
26
26
27 def setUp(self):
27 def setUp(self):
28 self.repo = MercurialRepository(TEST_HG_REPO)
28 self.repo = MercurialRepository(TEST_HG_REPO)
29
29
30 def test_wrong_repo_path(self):
30 def test_wrong_repo_path(self):
31 wrong_repo_path = '/tmp/errorrepo'
31 wrong_repo_path = '/tmp/errorrepo'
32 self.assertRaises(RepositoryError, MercurialRepository, wrong_repo_path)
32 self.assertRaises(RepositoryError, MercurialRepository, wrong_repo_path)
33
33
34 def test_unicode_path_repo(self):
34 def test_unicode_path_repo(self):
35 self.assertRaises(VCSError,lambda:MercurialRepository(u'iShouldFail'))
35 self.assertRaises(VCSError,lambda:MercurialRepository(u'iShouldFail'))
36
36
37 def test_repo_clone(self):
37 def test_repo_clone(self):
38 self.__check_for_existing_repo()
38 self.__check_for_existing_repo()
39 repo = MercurialRepository(TEST_HG_REPO)
39 repo = MercurialRepository(TEST_HG_REPO)
40 repo_clone = MercurialRepository(TEST_HG_REPO_CLONE,
40 repo_clone = MercurialRepository(TEST_HG_REPO_CLONE,
41 src_url=TEST_HG_REPO, update_after_clone=True)
41 src_url=TEST_HG_REPO, update_after_clone=True)
42 self.assertEqual(len(repo.revisions), len(repo_clone.revisions))
42 self.assertEqual(len(repo.revisions), len(repo_clone.revisions))
43 # Checking hashes of changesets should be enough
43 # Checking hashes of changesets should be enough
44 for changeset in repo.get_changesets():
44 for changeset in repo.get_changesets():
45 raw_id = changeset.raw_id
45 raw_id = changeset.raw_id
46 self.assertEqual(raw_id, repo_clone.get_changeset(raw_id).raw_id)
46 self.assertEqual(raw_id, repo_clone.get_changeset(raw_id).raw_id)
47
47
48 def test_repo_clone_with_update(self):
48 def test_repo_clone_with_update(self):
49 repo = MercurialRepository(TEST_HG_REPO)
49 repo = MercurialRepository(TEST_HG_REPO)
50 repo_clone = MercurialRepository(TEST_HG_REPO_CLONE + '_w_update',
50 repo_clone = MercurialRepository(TEST_HG_REPO_CLONE + '_w_update',
51 src_url=TEST_HG_REPO, update_after_clone=True)
51 src_url=TEST_HG_REPO, update_after_clone=True)
52 self.assertEqual(len(repo.revisions), len(repo_clone.revisions))
52 self.assertEqual(len(repo.revisions), len(repo_clone.revisions))
53
53
54 #check if current workdir was updated
54 #check if current workdir was updated
55 self.assertEqual(os.path.isfile(os.path.join(TEST_HG_REPO_CLONE \
55 self.assertEqual(os.path.isfile(os.path.join(TEST_HG_REPO_CLONE \
56 + '_w_update',
56 + '_w_update',
57 'MANIFEST.in')), True,)
57 'MANIFEST.in')), True,)
58
58
59 def test_repo_clone_without_update(self):
59 def test_repo_clone_without_update(self):
60 repo = MercurialRepository(TEST_HG_REPO)
60 repo = MercurialRepository(TEST_HG_REPO)
61 repo_clone = MercurialRepository(TEST_HG_REPO_CLONE + '_wo_update',
61 repo_clone = MercurialRepository(TEST_HG_REPO_CLONE + '_wo_update',
62 src_url=TEST_HG_REPO, update_after_clone=False)
62 src_url=TEST_HG_REPO, update_after_clone=False)
63 self.assertEqual(len(repo.revisions), len(repo_clone.revisions))
63 self.assertEqual(len(repo.revisions), len(repo_clone.revisions))
64 self.assertEqual(os.path.isfile(os.path.join(TEST_HG_REPO_CLONE \
64 self.assertEqual(os.path.isfile(os.path.join(TEST_HG_REPO_CLONE \
65 + '_wo_update',
65 + '_wo_update',
66 'MANIFEST.in')), False,)
66 'MANIFEST.in')), False,)
67
67
68 def test_pull(self):
68 def test_pull(self):
69 if os.path.exists(TEST_HG_REPO_PULL):
69 if os.path.exists(TEST_HG_REPO_PULL):
70 self.fail('Cannot test mercurial pull command as location %s '
70 self.fail('Cannot test mercurial pull command as location %s '
71 'already exists. You should manually remove it first'
71 'already exists. You should manually remove it first'
72 % TEST_HG_REPO_PULL)
72 % TEST_HG_REPO_PULL)
73 repo_new = MercurialRepository(TEST_HG_REPO_PULL, create=True)
73 repo_new = MercurialRepository(TEST_HG_REPO_PULL, create=True)
74 self.assertTrue(len(self.repo.revisions) > len(repo_new.revisions))
74 self.assertTrue(len(self.repo.revisions) > len(repo_new.revisions))
75
75
76 repo_new.pull(self.repo.path)
76 repo_new.pull(self.repo.path)
77 repo_new = MercurialRepository(TEST_HG_REPO_PULL)
77 repo_new = MercurialRepository(TEST_HG_REPO_PULL)
78 self.assertTrue(len(self.repo.revisions) == len(repo_new.revisions))
78 self.assertTrue(len(self.repo.revisions) == len(repo_new.revisions))
79
79
80 def test_revisions(self):
80 def test_revisions(self):
81 # there are 21 revisions at bitbucket now
81 # there are 21 revisions at bitbucket now
82 # so we can assume they would be available from now on
82 # so we can assume they would be available from now on
83 subset = set(['b986218ba1c9b0d6a259fac9b050b1724ed8e545',
83 subset = set(['b986218ba1c9b0d6a259fac9b050b1724ed8e545',
84 '3d8f361e72ab303da48d799ff1ac40d5ac37c67e',
84 '3d8f361e72ab303da48d799ff1ac40d5ac37c67e',
85 '6cba7170863a2411822803fa77a0a264f1310b35',
85 '6cba7170863a2411822803fa77a0a264f1310b35',
86 '56349e29c2af3ac913b28bde9a2c6154436e615b',
86 '56349e29c2af3ac913b28bde9a2c6154436e615b',
87 '2dda4e345facb0ccff1a191052dd1606dba6781d',
87 '2dda4e345facb0ccff1a191052dd1606dba6781d',
88 '6fff84722075f1607a30f436523403845f84cd9e',
88 '6fff84722075f1607a30f436523403845f84cd9e',
89 '7d4bc8ec6be56c0f10425afb40b6fc315a4c25e7',
89 '7d4bc8ec6be56c0f10425afb40b6fc315a4c25e7',
90 '3803844fdbd3b711175fc3da9bdacfcd6d29a6fb',
90 '3803844fdbd3b711175fc3da9bdacfcd6d29a6fb',
91 'dc5d2c0661b61928834a785d3e64a3f80d3aad9c',
91 'dc5d2c0661b61928834a785d3e64a3f80d3aad9c',
92 'be90031137367893f1c406e0a8683010fd115b79',
92 'be90031137367893f1c406e0a8683010fd115b79',
93 'db8e58be770518cbb2b1cdfa69146e47cd481481',
93 'db8e58be770518cbb2b1cdfa69146e47cd481481',
94 '84478366594b424af694a6c784cb991a16b87c21',
94 '84478366594b424af694a6c784cb991a16b87c21',
95 '17f8e105dddb9f339600389c6dc7175d395a535c',
95 '17f8e105dddb9f339600389c6dc7175d395a535c',
96 '20a662e756499bde3095ffc9bc0643d1def2d0eb',
96 '20a662e756499bde3095ffc9bc0643d1def2d0eb',
97 '2e319b85e70a707bba0beff866d9f9de032aa4f9',
97 '2e319b85e70a707bba0beff866d9f9de032aa4f9',
98 '786facd2c61deb9cf91e9534735124fb8fc11842',
98 '786facd2c61deb9cf91e9534735124fb8fc11842',
99 '94593d2128d38210a2fcd1aabff6dda0d6d9edf8',
99 '94593d2128d38210a2fcd1aabff6dda0d6d9edf8',
100 'aa6a0de05b7612707db567078e130a6cd114a9a7',
100 'aa6a0de05b7612707db567078e130a6cd114a9a7',
101 'eada5a770da98ab0dd7325e29d00e0714f228d09'
101 'eada5a770da98ab0dd7325e29d00e0714f228d09'
102 ])
102 ])
103 self.assertTrue(subset.issubset(set(self.repo.revisions)))
103 self.assertTrue(subset.issubset(set(self.repo.revisions)))
104
104
105
105
106 # check if we have the proper order of revisions
106 # check if we have the proper order of revisions
107 org = ['b986218ba1c9b0d6a259fac9b050b1724ed8e545',
107 org = ['b986218ba1c9b0d6a259fac9b050b1724ed8e545',
108 '3d8f361e72ab303da48d799ff1ac40d5ac37c67e',
108 '3d8f361e72ab303da48d799ff1ac40d5ac37c67e',
109 '6cba7170863a2411822803fa77a0a264f1310b35',
109 '6cba7170863a2411822803fa77a0a264f1310b35',
110 '56349e29c2af3ac913b28bde9a2c6154436e615b',
110 '56349e29c2af3ac913b28bde9a2c6154436e615b',
111 '2dda4e345facb0ccff1a191052dd1606dba6781d',
111 '2dda4e345facb0ccff1a191052dd1606dba6781d',
112 '6fff84722075f1607a30f436523403845f84cd9e',
112 '6fff84722075f1607a30f436523403845f84cd9e',
113 '7d4bc8ec6be56c0f10425afb40b6fc315a4c25e7',
113 '7d4bc8ec6be56c0f10425afb40b6fc315a4c25e7',
114 '3803844fdbd3b711175fc3da9bdacfcd6d29a6fb',
114 '3803844fdbd3b711175fc3da9bdacfcd6d29a6fb',
115 'dc5d2c0661b61928834a785d3e64a3f80d3aad9c',
115 'dc5d2c0661b61928834a785d3e64a3f80d3aad9c',
116 'be90031137367893f1c406e0a8683010fd115b79',
116 'be90031137367893f1c406e0a8683010fd115b79',
117 'db8e58be770518cbb2b1cdfa69146e47cd481481',
117 'db8e58be770518cbb2b1cdfa69146e47cd481481',
118 '84478366594b424af694a6c784cb991a16b87c21',
118 '84478366594b424af694a6c784cb991a16b87c21',
119 '17f8e105dddb9f339600389c6dc7175d395a535c',
119 '17f8e105dddb9f339600389c6dc7175d395a535c',
120 '20a662e756499bde3095ffc9bc0643d1def2d0eb',
120 '20a662e756499bde3095ffc9bc0643d1def2d0eb',
121 '2e319b85e70a707bba0beff866d9f9de032aa4f9',
121 '2e319b85e70a707bba0beff866d9f9de032aa4f9',
122 '786facd2c61deb9cf91e9534735124fb8fc11842',
122 '786facd2c61deb9cf91e9534735124fb8fc11842',
123 '94593d2128d38210a2fcd1aabff6dda0d6d9edf8',
123 '94593d2128d38210a2fcd1aabff6dda0d6d9edf8',
124 'aa6a0de05b7612707db567078e130a6cd114a9a7',
124 'aa6a0de05b7612707db567078e130a6cd114a9a7',
125 'eada5a770da98ab0dd7325e29d00e0714f228d09',
125 'eada5a770da98ab0dd7325e29d00e0714f228d09',
126 '2c1885c735575ca478bf9e17b0029dca68824458',
126 '2c1885c735575ca478bf9e17b0029dca68824458',
127 'd9bcd465040bf869799b09ad732c04e0eea99fe9',
127 'd9bcd465040bf869799b09ad732c04e0eea99fe9',
128 '469e9c847fe1f6f7a697b8b25b4bc5b48780c1a7',
128 '469e9c847fe1f6f7a697b8b25b4bc5b48780c1a7',
129 '4fb8326d78e5120da2c7468dcf7098997be385da',
129 '4fb8326d78e5120da2c7468dcf7098997be385da',
130 '62b4a097164940bd66030c4db51687f3ec035eed',
130 '62b4a097164940bd66030c4db51687f3ec035eed',
131 '536c1a19428381cfea92ac44985304f6a8049569',
131 '536c1a19428381cfea92ac44985304f6a8049569',
132 '965e8ab3c44b070cdaa5bf727ddef0ada980ecc4',
132 '965e8ab3c44b070cdaa5bf727ddef0ada980ecc4',
133 '9bb326a04ae5d98d437dece54be04f830cf1edd9',
133 '9bb326a04ae5d98d437dece54be04f830cf1edd9',
134 'f8940bcb890a98c4702319fbe36db75ea309b475',
134 'f8940bcb890a98c4702319fbe36db75ea309b475',
135 'ff5ab059786ebc7411e559a2cc309dfae3625a3b',
135 'ff5ab059786ebc7411e559a2cc309dfae3625a3b',
136 '6b6ad5f82ad5bb6190037671bd254bd4e1f4bf08',
136 '6b6ad5f82ad5bb6190037671bd254bd4e1f4bf08',
137 'ee87846a61c12153b51543bf860e1026c6d3dcba', ]
137 'ee87846a61c12153b51543bf860e1026c6d3dcba', ]
138 self.assertEqual(org, self.repo.revisions[:31])
138 self.assertEqual(org, self.repo.revisions[:31])
139
139
140 def test_iter_slice(self):
140 def test_iter_slice(self):
141 sliced = list(self.repo[:10])
141 sliced = list(self.repo[:10])
142 itered = list(self.repo)[:10]
142 itered = list(self.repo)[:10]
143 self.assertEqual(sliced, itered)
143 self.assertEqual(sliced, itered)
144
144
145 def test_slicing(self):
145 def test_slicing(self):
146 #4 1 5 10 95
146 #4 1 5 10 95
147 for sfrom, sto, size in [(0, 4, 4), (1, 2, 1), (10, 15, 5),
147 for sfrom, sto, size in [(0, 4, 4), (1, 2, 1), (10, 15, 5),
148 (10, 20, 10), (5, 100, 95)]:
148 (10, 20, 10), (5, 100, 95)]:
149 revs = list(self.repo[sfrom:sto])
149 revs = list(self.repo[sfrom:sto])
150 self.assertEqual(len(revs), size)
150 self.assertEqual(len(revs), size)
151 self.assertEqual(revs[0], self.repo.get_changeset(sfrom))
151 self.assertEqual(revs[0], self.repo.get_changeset(sfrom))
152 self.assertEqual(revs[-1], self.repo.get_changeset(sto - 1))
152 self.assertEqual(revs[-1], self.repo.get_changeset(sto - 1))
153
153
154 def test_branches(self):
154 def test_branches(self):
155 # TODO: Need more tests here
155 # TODO: Need more tests here
156
156
157 #active branches
157 #active branches
158 self.assertTrue('default' in self.repo.branches)
158 self.assertTrue('default' in self.repo.branches)
159
159 self.assertTrue('git' in self.repo.branches)
160 #closed branches
161 self.assertFalse('web' in self.repo.branches)
162 self.assertFalse('git' in self.repo.branches)
163
160
164 # closed
161 # closed
165 self.assertTrue('workdir' in self.repo._get_branches(closed=True))
162 self.assertTrue('web' in self.repo._get_branches(closed=True))
166 self.assertTrue('webvcs' in self.repo._get_branches(closed=True))
167
163
168 for name, id in self.repo.branches.items():
164 for name, id in self.repo.branches.items():
169 self.assertTrue(isinstance(
165 self.assertTrue(isinstance(
170 self.repo.get_changeset(id), MercurialChangeset))
166 self.repo.get_changeset(id), MercurialChangeset))
171
167
172 def test_tip_in_tags(self):
168 def test_tip_in_tags(self):
173 # tip is always a tag
169 # tip is always a tag
174 self.assertIn('tip', self.repo.tags)
170 self.assertIn('tip', self.repo.tags)
175
171
176 def test_tip_changeset_in_tags(self):
172 def test_tip_changeset_in_tags(self):
177 tip = self.repo.get_changeset()
173 tip = self.repo.get_changeset()
178 self.assertEqual(self.repo.tags['tip'], tip.raw_id)
174 self.assertEqual(self.repo.tags['tip'], tip.raw_id)
179
175
180 def test_initial_changeset(self):
176 def test_initial_changeset(self):
181
177
182 init_chset = self.repo.get_changeset(0)
178 init_chset = self.repo.get_changeset(0)
183 self.assertEqual(init_chset.message, 'initial import')
179 self.assertEqual(init_chset.message, 'initial import')
184 self.assertEqual(init_chset.author,
180 self.assertEqual(init_chset.author,
185 'Marcin Kuzminski <marcin@python-blog.com>')
181 'Marcin Kuzminski <marcin@python-blog.com>')
186 self.assertEqual(sorted(init_chset._file_paths),
182 self.assertEqual(sorted(init_chset._file_paths),
187 sorted([
183 sorted([
188 'vcs/__init__.py',
184 'vcs/__init__.py',
189 'vcs/backends/BaseRepository.py',
185 'vcs/backends/BaseRepository.py',
190 'vcs/backends/__init__.py',
186 'vcs/backends/__init__.py',
191 ])
187 ])
192 )
188 )
193 self.assertEqual(sorted(init_chset._dir_paths),
189 self.assertEqual(sorted(init_chset._dir_paths),
194 sorted(['', 'vcs', 'vcs/backends']))
190 sorted(['', 'vcs', 'vcs/backends']))
195
191
196 self.assertRaises(NodeDoesNotExistError, init_chset.get_node, path='foobar')
192 self.assertRaises(NodeDoesNotExistError, init_chset.get_node, path='foobar')
197
193
198 node = init_chset.get_node('vcs/')
194 node = init_chset.get_node('vcs/')
199 self.assertTrue(hasattr(node, 'kind'))
195 self.assertTrue(hasattr(node, 'kind'))
200 self.assertEqual(node.kind, NodeKind.DIR)
196 self.assertEqual(node.kind, NodeKind.DIR)
201
197
202 node = init_chset.get_node('vcs')
198 node = init_chset.get_node('vcs')
203 self.assertTrue(hasattr(node, 'kind'))
199 self.assertTrue(hasattr(node, 'kind'))
204 self.assertEqual(node.kind, NodeKind.DIR)
200 self.assertEqual(node.kind, NodeKind.DIR)
205
201
206 node = init_chset.get_node('vcs/__init__.py')
202 node = init_chset.get_node('vcs/__init__.py')
207 self.assertTrue(hasattr(node, 'kind'))
203 self.assertTrue(hasattr(node, 'kind'))
208 self.assertEqual(node.kind, NodeKind.FILE)
204 self.assertEqual(node.kind, NodeKind.FILE)
209
205
210 def test_not_existing_changeset(self):
206 def test_not_existing_changeset(self):
211 #rawid
207 #rawid
212 self.assertRaises(RepositoryError, self.repo.get_changeset,
208 self.assertRaises(RepositoryError, self.repo.get_changeset,
213 'abcd' * 10)
209 'abcd' * 10)
214 #shortid
210 #shortid
215 self.assertRaises(RepositoryError, self.repo.get_changeset,
211 self.assertRaises(RepositoryError, self.repo.get_changeset,
216 'erro' * 4)
212 'erro' * 4)
217 #numeric
213 #numeric
218 self.assertRaises(RepositoryError, self.repo.get_changeset,
214 self.assertRaises(RepositoryError, self.repo.get_changeset,
219 self.repo.count() + 1)
215 self.repo.count() + 1)
220
216
221
217
222 # Small chance we ever get to this one
218 # Small chance we ever get to this one
223 revision = pow(2, 30)
219 revision = pow(2, 30)
224 self.assertRaises(RepositoryError, self.repo.get_changeset, revision)
220 self.assertRaises(RepositoryError, self.repo.get_changeset, revision)
225
221
226 def test_changeset10(self):
222 def test_changeset10(self):
227
223
228 chset10 = self.repo.get_changeset(10)
224 chset10 = self.repo.get_changeset(10)
229 README = """===
225 README = """===
230 VCS
226 VCS
231 ===
227 ===
232
228
233 Various Version Control System management abstraction layer for Python.
229 Various Version Control System management abstraction layer for Python.
234
230
235 Introduction
231 Introduction
236 ------------
232 ------------
237
233
238 TODO: To be written...
234 TODO: To be written...
239
235
240 """
236 """
241 node = chset10.get_node('README.rst')
237 node = chset10.get_node('README.rst')
242 self.assertEqual(node.kind, NodeKind.FILE)
238 self.assertEqual(node.kind, NodeKind.FILE)
243 self.assertEqual(node.content, README)
239 self.assertEqual(node.content, README)
244
240
245
241
246 class MercurialChangesetTest(unittest.TestCase):
242 class MercurialChangesetTest(unittest.TestCase):
247
243
248 def setUp(self):
244 def setUp(self):
249 self.repo = MercurialRepository(TEST_HG_REPO)
245 self.repo = MercurialRepository(TEST_HG_REPO)
250
246
251 def _test_equality(self, changeset):
247 def _test_equality(self, changeset):
252 revision = changeset.revision
248 revision = changeset.revision
253 self.assertEqual(changeset, self.repo.get_changeset(revision))
249 self.assertEqual(changeset, self.repo.get_changeset(revision))
254
250
255 def test_equality(self):
251 def test_equality(self):
256 self.setUp()
252 self.setUp()
257 revs = [0, 10, 20]
253 revs = [0, 10, 20]
258 changesets = [self.repo.get_changeset(rev) for rev in revs]
254 changesets = [self.repo.get_changeset(rev) for rev in revs]
259 for changeset in changesets:
255 for changeset in changesets:
260 self._test_equality(changeset)
256 self._test_equality(changeset)
261
257
262 def test_default_changeset(self):
258 def test_default_changeset(self):
263 tip = self.repo.get_changeset('tip')
259 tip = self.repo.get_changeset('tip')
264 self.assertEqual(tip, self.repo.get_changeset())
260 self.assertEqual(tip, self.repo.get_changeset())
265 self.assertEqual(tip, self.repo.get_changeset(revision=None))
261 self.assertEqual(tip, self.repo.get_changeset(revision=None))
266 self.assertEqual(tip, list(self.repo[-1:])[0])
262 self.assertEqual(tip, list(self.repo[-1:])[0])
267
263
268 def test_root_node(self):
264 def test_root_node(self):
269 tip = self.repo.get_changeset('tip')
265 tip = self.repo.get_changeset('tip')
270 self.assertTrue(tip.root is tip.get_node(''))
266 self.assertTrue(tip.root is tip.get_node(''))
271
267
272 def test_lazy_fetch(self):
268 def test_lazy_fetch(self):
273 """
269 """
274 Test if changeset's nodes expands and are cached as we walk through
270 Test if changeset's nodes expands and are cached as we walk through
275 the revision. This test is somewhat hard to write as order of tests
271 the revision. This test is somewhat hard to write as order of tests
276 is a key here. Written by running command after command in a shell.
272 is a key here. Written by running command after command in a shell.
277 """
273 """
278 self.setUp()
274 self.setUp()
279 chset = self.repo.get_changeset(45)
275 chset = self.repo.get_changeset(45)
280 self.assertTrue(len(chset.nodes) == 0)
276 self.assertTrue(len(chset.nodes) == 0)
281 root = chset.root
277 root = chset.root
282 self.assertTrue(len(chset.nodes) == 1)
278 self.assertTrue(len(chset.nodes) == 1)
283 self.assertTrue(len(root.nodes) == 8)
279 self.assertTrue(len(root.nodes) == 8)
284 # accessing root.nodes updates chset.nodes
280 # accessing root.nodes updates chset.nodes
285 self.assertTrue(len(chset.nodes) == 9)
281 self.assertTrue(len(chset.nodes) == 9)
286
282
287 docs = root.get_node('docs')
283 docs = root.get_node('docs')
288 # we haven't yet accessed anything new as docs dir was already cached
284 # we haven't yet accessed anything new as docs dir was already cached
289 self.assertTrue(len(chset.nodes) == 9)
285 self.assertTrue(len(chset.nodes) == 9)
290 self.assertTrue(len(docs.nodes) == 8)
286 self.assertTrue(len(docs.nodes) == 8)
291 # accessing docs.nodes updates chset.nodes
287 # accessing docs.nodes updates chset.nodes
292 self.assertTrue(len(chset.nodes) == 17)
288 self.assertTrue(len(chset.nodes) == 17)
293
289
294 self.assertTrue(docs is chset.get_node('docs'))
290 self.assertTrue(docs is chset.get_node('docs'))
295 self.assertTrue(docs is root.nodes[0])
291 self.assertTrue(docs is root.nodes[0])
296 self.assertTrue(docs is root.dirs[0])
292 self.assertTrue(docs is root.dirs[0])
297 self.assertTrue(docs is chset.get_node('docs'))
293 self.assertTrue(docs is chset.get_node('docs'))
298
294
299 def test_nodes_with_changeset(self):
295 def test_nodes_with_changeset(self):
300 self.setUp()
296 self.setUp()
301 chset = self.repo.get_changeset(45)
297 chset = self.repo.get_changeset(45)
302 root = chset.root
298 root = chset.root
303 docs = root.get_node('docs')
299 docs = root.get_node('docs')
304 self.assertTrue(docs is chset.get_node('docs'))
300 self.assertTrue(docs is chset.get_node('docs'))
305 api = docs.get_node('api')
301 api = docs.get_node('api')
306 self.assertTrue(api is chset.get_node('docs/api'))
302 self.assertTrue(api is chset.get_node('docs/api'))
307 index = api.get_node('index.rst')
303 index = api.get_node('index.rst')
308 self.assertTrue(index is chset.get_node('docs/api/index.rst'))
304 self.assertTrue(index is chset.get_node('docs/api/index.rst'))
309 self.assertTrue(index is chset.get_node('docs')\
305 self.assertTrue(index is chset.get_node('docs')\
310 .get_node('api')\
306 .get_node('api')\
311 .get_node('index.rst'))
307 .get_node('index.rst'))
312
308
313 def test_branch_and_tags(self):
309 def test_branch_and_tags(self):
314 chset0 = self.repo.get_changeset(0)
310 chset0 = self.repo.get_changeset(0)
315 self.assertEqual(chset0.branch, 'default')
311 self.assertEqual(chset0.branch, 'default')
316 self.assertEqual(chset0.tags, [])
312 self.assertEqual(chset0.tags, [])
317
313
318 chset10 = self.repo.get_changeset(10)
314 chset10 = self.repo.get_changeset(10)
319 self.assertEqual(chset10.branch, 'default')
315 self.assertEqual(chset10.branch, 'default')
320 self.assertEqual(chset10.tags, [])
316 self.assertEqual(chset10.tags, [])
321
317
322 chset44 = self.repo.get_changeset(44)
318 chset44 = self.repo.get_changeset(44)
323 self.assertEqual(chset44.branch, 'web')
319 self.assertEqual(chset44.branch, 'web')
324
320
325 tip = self.repo.get_changeset('tip')
321 tip = self.repo.get_changeset('tip')
326 self.assertTrue('tip' in tip.tags)
322 self.assertTrue('tip' in tip.tags)
327
323
328 def _test_file_size(self, revision, path, size):
324 def _test_file_size(self, revision, path, size):
329 node = self.repo.get_changeset(revision).get_node(path)
325 node = self.repo.get_changeset(revision).get_node(path)
330 self.assertTrue(node.is_file())
326 self.assertTrue(node.is_file())
331 self.assertEqual(node.size, size)
327 self.assertEqual(node.size, size)
332
328
333 def test_file_size(self):
329 def test_file_size(self):
334 to_check = (
330 to_check = (
335 (10, 'setup.py', 1068),
331 (10, 'setup.py', 1068),
336 (20, 'setup.py', 1106),
332 (20, 'setup.py', 1106),
337 (60, 'setup.py', 1074),
333 (60, 'setup.py', 1074),
338
334
339 (10, 'vcs/backends/base.py', 2921),
335 (10, 'vcs/backends/base.py', 2921),
340 (20, 'vcs/backends/base.py', 3936),
336 (20, 'vcs/backends/base.py', 3936),
341 (60, 'vcs/backends/base.py', 6189),
337 (60, 'vcs/backends/base.py', 6189),
342 )
338 )
343 for revision, path, size in to_check:
339 for revision, path, size in to_check:
344 self._test_file_size(revision, path, size)
340 self._test_file_size(revision, path, size)
345
341
346 def test_file_history(self):
342 def test_file_history(self):
347 # we can only check if those revisions are present in the history
343 # we can only check if those revisions are present in the history
348 # as we cannot update this test every time file is changed
344 # as we cannot update this test every time file is changed
349 files = {
345 files = {
350 'setup.py': [7, 18, 45, 46, 47, 69, 77],
346 'setup.py': [7, 18, 45, 46, 47, 69, 77],
351 'vcs/nodes.py': [7, 8, 24, 26, 30, 45, 47, 49, 56, 57, 58, 59, 60,
347 'vcs/nodes.py': [7, 8, 24, 26, 30, 45, 47, 49, 56, 57, 58, 59, 60,
352 61, 73, 76],
348 61, 73, 76],
353 'vcs/backends/hg.py': [4, 5, 6, 11, 12, 13, 14, 15, 16, 21, 22, 23,
349 'vcs/backends/hg.py': [4, 5, 6, 11, 12, 13, 14, 15, 16, 21, 22, 23,
354 26, 27, 28, 30, 31, 33, 35, 36, 37, 38, 39, 40, 41, 44, 45, 47,
350 26, 27, 28, 30, 31, 33, 35, 36, 37, 38, 39, 40, 41, 44, 45, 47,
355 48, 49, 53, 54, 55, 58, 60, 61, 67, 68, 69, 70, 73, 77, 78, 79,
351 48, 49, 53, 54, 55, 58, 60, 61, 67, 68, 69, 70, 73, 77, 78, 79,
356 82],
352 82],
357 }
353 }
358 for path, revs in files.items():
354 for path, revs in files.items():
359 tip = self.repo.get_changeset(revs[-1])
355 tip = self.repo.get_changeset(revs[-1])
360 node = tip.get_node(path)
356 node = tip.get_node(path)
361 node_revs = [chset.revision for chset in node.history]
357 node_revs = [chset.revision for chset in node.history]
362 self.assertTrue(set(revs).issubset(set(node_revs)),
358 self.assertTrue(set(revs).issubset(set(node_revs)),
363 "We assumed that %s is subset of revisions for which file %s "
359 "We assumed that %s is subset of revisions for which file %s "
364 "has been changed, and history of that node returned: %s"
360 "has been changed, and history of that node returned: %s"
365 % (revs, path, node_revs))
361 % (revs, path, node_revs))
366
362
367 def test_file_annotate(self):
363 def test_file_annotate(self):
368 files = {
364 files = {
369 'vcs/backends/__init__.py':
365 'vcs/backends/__init__.py':
370 {89: {'lines_no': 31,
366 {89: {'lines_no': 31,
371 'changesets': [32, 32, 61, 32, 32, 37, 32, 32, 32, 44,
367 'changesets': [32, 32, 61, 32, 32, 37, 32, 32, 32, 44,
372 37, 37, 37, 37, 45, 37, 44, 37, 37, 37,
368 37, 37, 37, 37, 45, 37, 44, 37, 37, 37,
373 32, 32, 32, 32, 37, 32, 37, 37, 32,
369 32, 32, 32, 32, 37, 32, 37, 37, 32,
374 32, 32]},
370 32, 32]},
375 20: {'lines_no': 1,
371 20: {'lines_no': 1,
376 'changesets': [4]},
372 'changesets': [4]},
377 55: {'lines_no': 31,
373 55: {'lines_no': 31,
378 'changesets': [32, 32, 45, 32, 32, 37, 32, 32, 32, 44,
374 'changesets': [32, 32, 45, 32, 32, 37, 32, 32, 32, 44,
379 37, 37, 37, 37, 45, 37, 44, 37, 37, 37,
375 37, 37, 37, 37, 45, 37, 44, 37, 37, 37,
380 32, 32, 32, 32, 37, 32, 37, 37, 32,
376 32, 32, 32, 32, 37, 32, 37, 37, 32,
381 32, 32]}},
377 32, 32]}},
382 'vcs/exceptions.py':
378 'vcs/exceptions.py':
383 {89: {'lines_no': 18,
379 {89: {'lines_no': 18,
384 'changesets': [16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
380 'changesets': [16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
385 16, 16, 17, 16, 16, 18, 18, 18]},
381 16, 16, 17, 16, 16, 18, 18, 18]},
386 20: {'lines_no': 18,
382 20: {'lines_no': 18,
387 'changesets': [16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
383 'changesets': [16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
388 16, 16, 17, 16, 16, 18, 18, 18]},
384 16, 16, 17, 16, 16, 18, 18, 18]},
389 55: {'lines_no': 18, 'changesets': [16, 16, 16, 16, 16, 16,
385 55: {'lines_no': 18, 'changesets': [16, 16, 16, 16, 16, 16,
390 16, 16, 16, 16, 16, 16,
386 16, 16, 16, 16, 16, 16,
391 17, 16, 16, 18, 18, 18]}},
387 17, 16, 16, 18, 18, 18]}},
392 'MANIFEST.in': {89: {'lines_no': 5,
388 'MANIFEST.in': {89: {'lines_no': 5,
393 'changesets': [7, 7, 7, 71, 71]},
389 'changesets': [7, 7, 7, 71, 71]},
394 20: {'lines_no': 3,
390 20: {'lines_no': 3,
395 'changesets': [7, 7, 7]},
391 'changesets': [7, 7, 7]},
396 55: {'lines_no': 3,
392 55: {'lines_no': 3,
397 'changesets': [7, 7, 7]}}}
393 'changesets': [7, 7, 7]}}}
398
394
399
395
400 for fname, revision_dict in files.items():
396 for fname, revision_dict in files.items():
401 for rev, data in revision_dict.items():
397 for rev, data in revision_dict.items():
402 cs = self.repo.get_changeset(rev)
398 cs = self.repo.get_changeset(rev)
403 ann = cs.get_file_annotate(fname)
399 ann = cs.get_file_annotate(fname)
404
400
405 l1 = [x[1].revision for x in ann]
401 l1 = [x[1].revision for x in ann]
406 l2 = files[fname][rev]['changesets']
402 l2 = files[fname][rev]['changesets']
407 self.assertTrue(l1 == l2 , "The lists of revision for %s@rev%s"
403 self.assertTrue(l1 == l2 , "The lists of revision for %s@rev%s"
408 "from annotation list should match each other,"
404 "from annotation list should match each other,"
409 "got \n%s \nvs \n%s " % (fname, rev, l1, l2))
405 "got \n%s \nvs \n%s " % (fname, rev, l1, l2))
410
406
411 def test_changeset_state(self):
407 def test_changeset_state(self):
412 """
408 """
413 Tests which files have been added/changed/removed at particular revision
409 Tests which files have been added/changed/removed at particular revision
414 """
410 """
415
411
416 # rev 46ad32a4f974:
412 # rev 46ad32a4f974:
417 # hg st --rev 46ad32a4f974
413 # hg st --rev 46ad32a4f974
418 # changed: 13
414 # changed: 13
419 # added: 20
415 # added: 20
420 # removed: 1
416 # removed: 1
421 changed = set(['.hgignore'
417 changed = set(['.hgignore'
422 , 'README.rst' , 'docs/conf.py' , 'docs/index.rst' , 'setup.py'
418 , 'README.rst' , 'docs/conf.py' , 'docs/index.rst' , 'setup.py'
423 , 'tests/test_hg.py' , 'tests/test_nodes.py' , 'vcs/__init__.py'
419 , 'tests/test_hg.py' , 'tests/test_nodes.py' , 'vcs/__init__.py'
424 , 'vcs/backends/__init__.py' , 'vcs/backends/base.py'
420 , 'vcs/backends/__init__.py' , 'vcs/backends/base.py'
425 , 'vcs/backends/hg.py' , 'vcs/nodes.py' , 'vcs/utils/__init__.py'])
421 , 'vcs/backends/hg.py' , 'vcs/nodes.py' , 'vcs/utils/__init__.py'])
426
422
427 added = set(['docs/api/backends/hg.rst'
423 added = set(['docs/api/backends/hg.rst'
428 , 'docs/api/backends/index.rst' , 'docs/api/index.rst'
424 , 'docs/api/backends/index.rst' , 'docs/api/index.rst'
429 , 'docs/api/nodes.rst' , 'docs/api/web/index.rst'
425 , 'docs/api/nodes.rst' , 'docs/api/web/index.rst'
430 , 'docs/api/web/simplevcs.rst' , 'docs/installation.rst'
426 , 'docs/api/web/simplevcs.rst' , 'docs/installation.rst'
431 , 'docs/quickstart.rst' , 'setup.cfg' , 'vcs/utils/baseui_config.py'
427 , 'docs/quickstart.rst' , 'setup.cfg' , 'vcs/utils/baseui_config.py'
432 , 'vcs/utils/web.py' , 'vcs/web/__init__.py' , 'vcs/web/exceptions.py'
428 , 'vcs/utils/web.py' , 'vcs/web/__init__.py' , 'vcs/web/exceptions.py'
433 , 'vcs/web/simplevcs/__init__.py' , 'vcs/web/simplevcs/exceptions.py'
429 , 'vcs/web/simplevcs/__init__.py' , 'vcs/web/simplevcs/exceptions.py'
434 , 'vcs/web/simplevcs/middleware.py' , 'vcs/web/simplevcs/models.py'
430 , 'vcs/web/simplevcs/middleware.py' , 'vcs/web/simplevcs/models.py'
435 , 'vcs/web/simplevcs/settings.py' , 'vcs/web/simplevcs/utils.py'
431 , 'vcs/web/simplevcs/settings.py' , 'vcs/web/simplevcs/utils.py'
436 , 'vcs/web/simplevcs/views.py'])
432 , 'vcs/web/simplevcs/views.py'])
437
433
438 removed = set(['docs/api.rst'])
434 removed = set(['docs/api.rst'])
439
435
440 chset64 = self.repo.get_changeset('46ad32a4f974')
436 chset64 = self.repo.get_changeset('46ad32a4f974')
441 self.assertEqual(set((node.path for node in chset64.added)), added)
437 self.assertEqual(set((node.path for node in chset64.added)), added)
442 self.assertEqual(set((node.path for node in chset64.changed)), changed)
438 self.assertEqual(set((node.path for node in chset64.changed)), changed)
443 self.assertEqual(set((node.path for node in chset64.removed)), removed)
439 self.assertEqual(set((node.path for node in chset64.removed)), removed)
444
440
445 # rev b090f22d27d6:
441 # rev b090f22d27d6:
446 # hg st --rev b090f22d27d6
442 # hg st --rev b090f22d27d6
447 # changed: 13
443 # changed: 13
448 # added: 20
444 # added: 20
449 # removed: 1
445 # removed: 1
450 chset88 = self.repo.get_changeset('b090f22d27d6')
446 chset88 = self.repo.get_changeset('b090f22d27d6')
451 self.assertEqual(set((node.path for node in chset88.added)), set())
447 self.assertEqual(set((node.path for node in chset88.added)), set())
452 self.assertEqual(set((node.path for node in chset88.changed)),
448 self.assertEqual(set((node.path for node in chset88.changed)),
453 set(['.hgignore']))
449 set(['.hgignore']))
454 self.assertEqual(set((node.path for node in chset88.removed)), set())
450 self.assertEqual(set((node.path for node in chset88.removed)), set())
455 #
451 #
456 # 85:
452 # 85:
457 # added: 2 ['vcs/utils/diffs.py', 'vcs/web/simplevcs/views/diffs.py']
453 # added: 2 ['vcs/utils/diffs.py', 'vcs/web/simplevcs/views/diffs.py']
458 # changed: 4 ['vcs/web/simplevcs/models.py', ...]
454 # changed: 4 ['vcs/web/simplevcs/models.py', ...]
459 # removed: 1 ['vcs/utils/web.py']
455 # removed: 1 ['vcs/utils/web.py']
460 chset85 = self.repo.get_changeset(85)
456 chset85 = self.repo.get_changeset(85)
461 self.assertEqual(set((node.path for node in chset85.added)), set([
457 self.assertEqual(set((node.path for node in chset85.added)), set([
462 'vcs/utils/diffs.py',
458 'vcs/utils/diffs.py',
463 'vcs/web/simplevcs/views/diffs.py']))
459 'vcs/web/simplevcs/views/diffs.py']))
464 self.assertEqual(set((node.path for node in chset85.changed)), set([
460 self.assertEqual(set((node.path for node in chset85.changed)), set([
465 'vcs/web/simplevcs/models.py',
461 'vcs/web/simplevcs/models.py',
466 'vcs/web/simplevcs/utils.py',
462 'vcs/web/simplevcs/utils.py',
467 'vcs/web/simplevcs/views/__init__.py',
463 'vcs/web/simplevcs/views/__init__.py',
468 'vcs/web/simplevcs/views/repository.py',
464 'vcs/web/simplevcs/views/repository.py',
469 ]))
465 ]))
470 self.assertEqual(set((node.path for node in chset85.removed)),
466 self.assertEqual(set((node.path for node in chset85.removed)),
471 set(['vcs/utils/web.py']))
467 set(['vcs/utils/web.py']))
472
468
473
469
474 def test_files_state(self):
470 def test_files_state(self):
475 """
471 """
476 Tests state of FileNodes.
472 Tests state of FileNodes.
477 """
473 """
478 chset = self.repo.get_changeset(85)
474 chset = self.repo.get_changeset(85)
479 node = chset.get_node('vcs/utils/diffs.py')
475 node = chset.get_node('vcs/utils/diffs.py')
480 self.assertTrue(node.state, NodeState.ADDED)
476 self.assertTrue(node.state, NodeState.ADDED)
481 self.assertTrue(node.added)
477 self.assertTrue(node.added)
482 self.assertFalse(node.changed)
478 self.assertFalse(node.changed)
483 self.assertFalse(node.not_changed)
479 self.assertFalse(node.not_changed)
484 self.assertFalse(node.removed)
480 self.assertFalse(node.removed)
485
481
486 chset = self.repo.get_changeset(88)
482 chset = self.repo.get_changeset(88)
487 node = chset.get_node('.hgignore')
483 node = chset.get_node('.hgignore')
488 self.assertTrue(node.state, NodeState.CHANGED)
484 self.assertTrue(node.state, NodeState.CHANGED)
489 self.assertFalse(node.added)
485 self.assertFalse(node.added)
490 self.assertTrue(node.changed)
486 self.assertTrue(node.changed)
491 self.assertFalse(node.not_changed)
487 self.assertFalse(node.not_changed)
492 self.assertFalse(node.removed)
488 self.assertFalse(node.removed)
493
489
494 chset = self.repo.get_changeset(85)
490 chset = self.repo.get_changeset(85)
495 node = chset.get_node('setup.py')
491 node = chset.get_node('setup.py')
496 self.assertTrue(node.state, NodeState.NOT_CHANGED)
492 self.assertTrue(node.state, NodeState.NOT_CHANGED)
497 self.assertFalse(node.added)
493 self.assertFalse(node.added)
498 self.assertFalse(node.changed)
494 self.assertFalse(node.changed)
499 self.assertTrue(node.not_changed)
495 self.assertTrue(node.not_changed)
500 self.assertFalse(node.removed)
496 self.assertFalse(node.removed)
501
497
502 # If node has REMOVED state then trying to fetch it would raise
498 # If node has REMOVED state then trying to fetch it would raise
503 # ChangesetError exception
499 # ChangesetError exception
504 chset = self.repo.get_changeset(2)
500 chset = self.repo.get_changeset(2)
505 path = 'vcs/backends/BaseRepository.py'
501 path = 'vcs/backends/BaseRepository.py'
506 self.assertRaises(NodeDoesNotExistError, chset.get_node, path)
502 self.assertRaises(NodeDoesNotExistError, chset.get_node, path)
507 # but it would be one of ``removed`` (changeset's attribute)
503 # but it would be one of ``removed`` (changeset's attribute)
508 self.assertTrue(path in [rf.path for rf in chset.removed])
504 self.assertTrue(path in [rf.path for rf in chset.removed])
509
505
510 def test_commit_message_is_unicode(self):
506 def test_commit_message_is_unicode(self):
511 for cm in self.repo:
507 for cm in self.repo:
512 self.assertEqual(type(cm.message), unicode)
508 self.assertEqual(type(cm.message), unicode)
513
509
514 def test_changeset_author_is_unicode(self):
510 def test_changeset_author_is_unicode(self):
515 for cm in self.repo:
511 for cm in self.repo:
516 self.assertEqual(type(cm.author), unicode)
512 self.assertEqual(type(cm.author), unicode)
517
513
518 def test_repo_files_content_is_unicode(self):
514 def test_repo_files_content_is_unicode(self):
519 test_changeset = self.repo.get_changeset(100)
515 test_changeset = self.repo.get_changeset(100)
520 for node in test_changeset.get_node('/'):
516 for node in test_changeset.get_node('/'):
521 if node.is_file():
517 if node.is_file():
522 self.assertEqual(type(node.content), unicode)
518 self.assertEqual(type(node.content), unicode)
523
519
524 def test_wrong_path(self):
520 def test_wrong_path(self):
525 # There is 'setup.py' in the root dir but not there:
521 # There is 'setup.py' in the root dir but not there:
526 path = 'foo/bar/setup.py'
522 path = 'foo/bar/setup.py'
527 self.assertRaises(VCSError, self.repo.get_changeset().get_node, path)
523 self.assertRaises(VCSError, self.repo.get_changeset().get_node, path)
528
524
529
525
530 def test_archival_file(self):
526 def test_archival_file(self):
531 #TODO:
527 #TODO:
532 pass
528 pass
533
529
534 def test_archival_as_generator(self):
530 def test_archival_as_generator(self):
535 #TODO:
531 #TODO:
536 pass
532 pass
537
533
538 def test_archival_wrong_kind(self):
534 def test_archival_wrong_kind(self):
539 tip = self.repo.get_changeset()
535 tip = self.repo.get_changeset()
540 self.assertRaises(VCSError, tip.fill_archive, kind='error')
536 self.assertRaises(VCSError, tip.fill_archive, kind='error')
541
537
542 def test_archival_empty_prefix(self):
538 def test_archival_empty_prefix(self):
543 #TODO:
539 #TODO:
544 pass
540 pass
545
541
546
542
547 def test_author_email(self):
543 def test_author_email(self):
548 self.assertEqual('marcin@python-blog.com',
544 self.assertEqual('marcin@python-blog.com',
549 self.repo.get_changeset('b986218ba1c9').author_email)
545 self.repo.get_changeset('b986218ba1c9').author_email)
550 self.assertEqual('lukasz.balcerzak@python-center.pl',
546 self.assertEqual('lukasz.balcerzak@python-center.pl',
551 self.repo.get_changeset('3803844fdbd3').author_email)
547 self.repo.get_changeset('3803844fdbd3').author_email)
552 self.assertEqual('',
548 self.assertEqual('',
553 self.repo.get_changeset('84478366594b').author_email)
549 self.repo.get_changeset('84478366594b').author_email)
554
550
555 def test_author_username(self):
551 def test_author_username(self):
556 self.assertEqual('Marcin Kuzminski',
552 self.assertEqual('Marcin Kuzminski',
557 self.repo.get_changeset('b986218ba1c9').author_name)
553 self.repo.get_changeset('b986218ba1c9').author_name)
558 self.assertEqual('Lukasz Balcerzak',
554 self.assertEqual('Lukasz Balcerzak',
559 self.repo.get_changeset('3803844fdbd3').author_name)
555 self.repo.get_changeset('3803844fdbd3').author_name)
560 self.assertEqual('marcink',
556 self.assertEqual('marcink',
561 self.repo.get_changeset('84478366594b').author_name)
557 self.repo.get_changeset('84478366594b').author_name)
1 NO CONTENT: modified file, binary diff hidden
NO CONTENT: modified file, binary diff hidden
General Comments 0
You need to be logged in to leave comments. Login now