##// END OF EJS Templates
extended admin rescan to show what repositories was added and what removed...
marcink -
r1039:51b70569 beta
parent child Browse files
Show More
@@ -1,342 +1,342 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.repos
3 rhodecode.controllers.admin.repos
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Admin controller for RhodeCode
6 Admin controller for RhodeCode
7
7
8 :created_on: Apr 7, 2010
8 :created_on: Apr 7, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
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, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import logging
28 import logging
29 import traceback
29 import traceback
30 import formencode
30 import formencode
31 from operator import itemgetter
31 from operator import itemgetter
32 from formencode import htmlfill
32 from formencode import htmlfill
33
33
34 from paste.httpexceptions import HTTPInternalServerError
34 from paste.httpexceptions import HTTPInternalServerError
35 from pylons import request, response, session, tmpl_context as c, url
35 from pylons import request, response, session, tmpl_context as c, url
36 from pylons.controllers.util import abort, redirect
36 from pylons.controllers.util import abort, redirect
37 from pylons.i18n.translation import _
37 from pylons.i18n.translation import _
38
38
39 from rhodecode.lib import helpers as h
39 from rhodecode.lib import helpers as h
40 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
40 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
41 HasPermissionAnyDecorator
41 HasPermissionAnyDecorator
42 from rhodecode.lib.base import BaseController, render
42 from rhodecode.lib.base import BaseController, render
43 from rhodecode.lib.utils import invalidate_cache, action_logger, repo_name_slug
43 from rhodecode.lib.utils import invalidate_cache, action_logger, repo_name_slug
44 from rhodecode.model.db import User
44 from rhodecode.model.db import User
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
49
50 log = logging.getLogger(__name__)
50 log = logging.getLogger(__name__)
51
51
52 class ReposController(BaseController):
52 class ReposController(BaseController):
53 """REST Controller styled on the Atom Publishing Protocol"""
53 """REST Controller styled on the Atom Publishing Protocol"""
54 # To properly map this controller, ensure your config/routing.py
54 # To properly map this controller, ensure your config/routing.py
55 # file has a resource setup:
55 # file has a resource setup:
56 # map.resource('repo', 'repos')
56 # map.resource('repo', 'repos')
57
57
58 @LoginRequired()
58 @LoginRequired()
59 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
59 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
60 def __before__(self):
60 def __before__(self):
61 c.admin_user = session.get('admin_user')
61 c.admin_user = session.get('admin_user')
62 c.admin_username = session.get('admin_username')
62 c.admin_username = session.get('admin_username')
63 super(ReposController, self).__before__()
63 super(ReposController, self).__before__()
64
64
65 @HasPermissionAllDecorator('hg.admin')
65 @HasPermissionAllDecorator('hg.admin')
66 def index(self, format='html'):
66 def index(self, format='html'):
67 """GET /repos: All items in the collection"""
67 """GET /repos: All items in the collection"""
68 # url('repos')
68 # url('repos')
69 cached_repo_list = ScmModel().get_repos()
69 cached_repo_list = ScmModel().get_repos()
70 c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
70 c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
71 return render('admin/repos/repos.html')
71 return render('admin/repos/repos.html')
72
72
73 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
73 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
74 def create(self):
74 def create(self):
75 """POST /repos: Create a new item"""
75 """POST /repos: Create a new item"""
76 # url('repos')
76 # url('repos')
77 repo_model = RepoModel()
77 repo_model = RepoModel()
78 _form = RepoForm()()
78 _form = RepoForm()()
79 form_result = {}
79 form_result = {}
80 try:
80 try:
81 form_result = _form.to_python(dict(request.POST))
81 form_result = _form.to_python(dict(request.POST))
82 repo_model.create(form_result, c.rhodecode_user)
82 repo_model.create(form_result, c.rhodecode_user)
83 h.flash(_('created repository %s') % form_result['repo_name'],
83 h.flash(_('created repository %s') % form_result['repo_name'],
84 category='success')
84 category='success')
85
85
86 if request.POST.get('user_created'):
86 if request.POST.get('user_created'):
87 action_logger(self.rhodecode_user, 'user_created_repo',
87 action_logger(self.rhodecode_user, 'user_created_repo',
88 form_result['repo_name'], '', self.sa)
88 form_result['repo_name'], '', self.sa)
89 else:
89 else:
90 action_logger(self.rhodecode_user, 'admin_created_repo',
90 action_logger(self.rhodecode_user, 'admin_created_repo',
91 form_result['repo_name'], '', self.sa)
91 form_result['repo_name'], '', self.sa)
92
92
93 except formencode.Invalid, errors:
93 except formencode.Invalid, errors:
94 c.new_repo = errors.value['repo_name']
94 c.new_repo = errors.value['repo_name']
95
95
96 if request.POST.get('user_created'):
96 if request.POST.get('user_created'):
97 r = render('admin/repos/repo_add_create_repository.html')
97 r = render('admin/repos/repo_add_create_repository.html')
98 else:
98 else:
99 r = render('admin/repos/repo_add.html')
99 r = render('admin/repos/repo_add.html')
100
100
101 return htmlfill.render(
101 return htmlfill.render(
102 r,
102 r,
103 defaults=errors.value,
103 defaults=errors.value,
104 errors=errors.error_dict or {},
104 errors=errors.error_dict or {},
105 prefix_error=False,
105 prefix_error=False,
106 encoding="UTF-8")
106 encoding="UTF-8")
107
107
108 except Exception:
108 except Exception:
109 log.error(traceback.format_exc())
109 log.error(traceback.format_exc())
110 msg = _('error occurred during creation of repository %s') \
110 msg = _('error occurred during creation of repository %s') \
111 % form_result.get('repo_name')
111 % form_result.get('repo_name')
112 h.flash(msg, category='error')
112 h.flash(msg, category='error')
113 if request.POST.get('user_created'):
113 if request.POST.get('user_created'):
114 return redirect(url('home'))
114 return redirect(url('home'))
115 return redirect(url('repos'))
115 return redirect(url('repos'))
116
116
117 @HasPermissionAllDecorator('hg.admin')
117 @HasPermissionAllDecorator('hg.admin')
118 def new(self, format='html'):
118 def new(self, format='html'):
119 """GET /repos/new: Form to create a new item"""
119 """GET /repos/new: Form to create a new item"""
120 new_repo = request.GET.get('repo', '')
120 new_repo = request.GET.get('repo', '')
121 c.new_repo = repo_name_slug(new_repo)
121 c.new_repo = repo_name_slug(new_repo)
122
122
123 return render('admin/repos/repo_add.html')
123 return render('admin/repos/repo_add.html')
124
124
125 @HasPermissionAllDecorator('hg.admin')
125 @HasPermissionAllDecorator('hg.admin')
126 def update(self, repo_name):
126 def update(self, repo_name):
127 """PUT /repos/repo_name: Update an existing item"""
127 """PUT /repos/repo_name: Update an existing item"""
128 # Forms posted to this method should contain a hidden field:
128 # Forms posted to this method should contain a hidden field:
129 # <input type="hidden" name="_method" value="PUT" />
129 # <input type="hidden" name="_method" value="PUT" />
130 # Or using helpers:
130 # Or using helpers:
131 # h.form(url('repo', repo_name=ID),
131 # h.form(url('repo', repo_name=ID),
132 # method='put')
132 # method='put')
133 # url('repo', repo_name=ID)
133 # url('repo', repo_name=ID)
134 repo_model = RepoModel()
134 repo_model = RepoModel()
135 changed_name = repo_name
135 changed_name = repo_name
136 _form = RepoForm(edit=True, old_data={'repo_name':repo_name})()
136 _form = RepoForm(edit=True, old_data={'repo_name':repo_name})()
137
137
138 try:
138 try:
139 form_result = _form.to_python(dict(request.POST))
139 form_result = _form.to_python(dict(request.POST))
140 repo_model.update(repo_name, form_result)
140 repo_model.update(repo_name, form_result)
141 invalidate_cache('get_repo_cached_%s' % repo_name)
141 invalidate_cache('get_repo_cached_%s' % repo_name)
142 h.flash(_('Repository %s updated successfully' % repo_name),
142 h.flash(_('Repository %s updated successfully' % repo_name),
143 category='success')
143 category='success')
144 changed_name = form_result['repo_name']
144 changed_name = form_result['repo_name']
145 action_logger(self.rhodecode_user, 'admin_updated_repo',
145 action_logger(self.rhodecode_user, 'admin_updated_repo',
146 changed_name, '', self.sa)
146 changed_name, '', self.sa)
147
147
148 except formencode.Invalid, errors:
148 except formencode.Invalid, errors:
149 c.repo_info = repo_model.get_by_repo_name(repo_name)
149 c.repo_info = repo_model.get_by_repo_name(repo_name)
150
150
151 if c.repo_info.stats:
151 if c.repo_info.stats:
152 last_rev = c.repo_info.stats.stat_on_revision
152 last_rev = c.repo_info.stats.stat_on_revision
153 else:
153 else:
154 last_rev = 0
154 last_rev = 0
155 c.stats_revision = last_rev
155 c.stats_revision = last_rev
156 r = ScmModel().get(repo_name)
156 repo, dbrepo = ScmModel().get(repo_name, retval='repo')
157 c.repo_last_rev = r.revisions[-1] if r.revisions else 0
157 c.repo_last_rev = repo.revisions[-1] if repo.revisions else 0
158
158
159 if last_rev == 0:
159 if last_rev == 0:
160 c.stats_percentage = 0
160 c.stats_percentage = 0
161 else:
161 else:
162 c.stats_percentage = '%.2f' % ((float((last_rev)) /
162 c.stats_percentage = '%.2f' % ((float((last_rev)) /
163 c.repo_last_rev) * 100)
163 c.repo_last_rev) * 100)
164
164
165 c.users_array = repo_model.get_users_js()
165 c.users_array = repo_model.get_users_js()
166 c.users_groups_array = repo_model.get_users_groups_js()
166 c.users_groups_array = repo_model.get_users_groups_js()
167
167
168 errors.value.update({'user':c.repo_info.user.username})
168 errors.value.update({'user':c.repo_info.user.username})
169 return htmlfill.render(
169 return htmlfill.render(
170 render('admin/repos/repo_edit.html'),
170 render('admin/repos/repo_edit.html'),
171 defaults=errors.value,
171 defaults=errors.value,
172 errors=errors.error_dict or {},
172 errors=errors.error_dict or {},
173 prefix_error=False,
173 prefix_error=False,
174 encoding="UTF-8")
174 encoding="UTF-8")
175
175
176 except Exception:
176 except Exception:
177 log.error(traceback.format_exc())
177 log.error(traceback.format_exc())
178 h.flash(_('error occurred during update of repository %s') \
178 h.flash(_('error occurred during update of repository %s') \
179 % repo_name, category='error')
179 % repo_name, category='error')
180
180
181 return redirect(url('edit_repo', repo_name=changed_name))
181 return redirect(url('edit_repo', repo_name=changed_name))
182
182
183 @HasPermissionAllDecorator('hg.admin')
183 @HasPermissionAllDecorator('hg.admin')
184 def delete(self, repo_name):
184 def delete(self, repo_name):
185 """DELETE /repos/repo_name: Delete an existing item"""
185 """DELETE /repos/repo_name: Delete an existing item"""
186 # Forms posted to this method should contain a hidden field:
186 # Forms posted to this method should contain a hidden field:
187 # <input type="hidden" name="_method" value="DELETE" />
187 # <input type="hidden" name="_method" value="DELETE" />
188 # Or using helpers:
188 # Or using helpers:
189 # h.form(url('repo', repo_name=ID),
189 # h.form(url('repo', repo_name=ID),
190 # method='delete')
190 # method='delete')
191 # url('repo', repo_name=ID)
191 # url('repo', repo_name=ID)
192
192
193 repo_model = RepoModel()
193 repo_model = RepoModel()
194 repo = repo_model.get_by_repo_name(repo_name)
194 repo = repo_model.get_by_repo_name(repo_name)
195 if not repo:
195 if not repo:
196 h.flash(_('%s repository is not mapped to db perhaps'
196 h.flash(_('%s repository is not mapped to db perhaps'
197 ' it was moved or renamed from the filesystem'
197 ' it was moved or renamed from the filesystem'
198 ' please run the application again'
198 ' please run the application again'
199 ' in order to rescan repositories') % repo_name,
199 ' in order to rescan repositories') % repo_name,
200 category='error')
200 category='error')
201
201
202 return redirect(url('repos'))
202 return redirect(url('repos'))
203 try:
203 try:
204 action_logger(self.rhodecode_user, 'admin_deleted_repo',
204 action_logger(self.rhodecode_user, 'admin_deleted_repo',
205 repo_name, '', self.sa)
205 repo_name, '', self.sa)
206 repo_model.delete(repo)
206 repo_model.delete(repo)
207 invalidate_cache('get_repo_cached_%s' % repo_name)
207 invalidate_cache('get_repo_cached_%s' % repo_name)
208 h.flash(_('deleted repository %s') % repo_name, category='success')
208 h.flash(_('deleted repository %s') % repo_name, category='success')
209
209
210 except Exception, e:
210 except Exception, e:
211 log.error(traceback.format_exc())
211 log.error(traceback.format_exc())
212 h.flash(_('An error occurred during deletion of %s') % repo_name,
212 h.flash(_('An error occurred during deletion of %s') % repo_name,
213 category='error')
213 category='error')
214
214
215 return redirect(url('repos'))
215 return redirect(url('repos'))
216
216
217 @HasPermissionAllDecorator('hg.admin')
217 @HasPermissionAllDecorator('hg.admin')
218 def delete_perm_user(self, repo_name):
218 def delete_perm_user(self, repo_name):
219 """DELETE an existing repository permission user
219 """DELETE an existing repository permission user
220
220
221 :param repo_name:
221 :param repo_name:
222 """
222 """
223
223
224 try:
224 try:
225 repo_model = RepoModel()
225 repo_model = RepoModel()
226 repo_model.delete_perm_user(request.POST, repo_name)
226 repo_model.delete_perm_user(request.POST, repo_name)
227 except Exception, e:
227 except Exception, e:
228 h.flash(_('An error occurred during deletion of repository user'),
228 h.flash(_('An error occurred during deletion of repository user'),
229 category='error')
229 category='error')
230 raise HTTPInternalServerError()
230 raise HTTPInternalServerError()
231
231
232 @HasPermissionAllDecorator('hg.admin')
232 @HasPermissionAllDecorator('hg.admin')
233 def delete_perm_users_group(self, repo_name):
233 def delete_perm_users_group(self, repo_name):
234 """DELETE an existing repository permission users group
234 """DELETE an existing repository permission users group
235
235
236 :param repo_name:
236 :param repo_name:
237 """
237 """
238 try:
238 try:
239 repo_model = RepoModel()
239 repo_model = RepoModel()
240 repo_model.delete_perm_users_group(request.POST, repo_name)
240 repo_model.delete_perm_users_group(request.POST, repo_name)
241 except Exception, e:
241 except Exception, e:
242 h.flash(_('An error occurred during deletion of repository'
242 h.flash(_('An error occurred during deletion of repository'
243 ' users groups'),
243 ' users groups'),
244 category='error')
244 category='error')
245 raise HTTPInternalServerError()
245 raise HTTPInternalServerError()
246
246
247 @HasPermissionAllDecorator('hg.admin')
247 @HasPermissionAllDecorator('hg.admin')
248 def repo_stats(self, repo_name):
248 def repo_stats(self, repo_name):
249 """DELETE an existing repository statistics
249 """DELETE an existing repository statistics
250
250
251 :param repo_name:
251 :param repo_name:
252 """
252 """
253
253
254 try:
254 try:
255 repo_model = RepoModel()
255 repo_model = RepoModel()
256 repo_model.delete_stats(repo_name)
256 repo_model.delete_stats(repo_name)
257 except Exception, e:
257 except Exception, e:
258 h.flash(_('An error occurred during deletion of repository stats'),
258 h.flash(_('An error occurred during deletion of repository stats'),
259 category='error')
259 category='error')
260 return redirect(url('edit_repo', repo_name=repo_name))
260 return redirect(url('edit_repo', repo_name=repo_name))
261
261
262 @HasPermissionAllDecorator('hg.admin')
262 @HasPermissionAllDecorator('hg.admin')
263 def repo_cache(self, repo_name):
263 def repo_cache(self, repo_name):
264 """INVALIDATE existing repository cache
264 """INVALIDATE existing repository cache
265
265
266 :param repo_name:
266 :param repo_name:
267 """
267 """
268
268
269 try:
269 try:
270 ScmModel().mark_for_invalidation(repo_name)
270 ScmModel().mark_for_invalidation(repo_name)
271 except Exception, e:
271 except Exception, e:
272 h.flash(_('An error occurred during cache invalidation'),
272 h.flash(_('An error occurred during cache invalidation'),
273 category='error')
273 category='error')
274 return redirect(url('edit_repo', repo_name=repo_name))
274 return redirect(url('edit_repo', repo_name=repo_name))
275
275
276 @HasPermissionAllDecorator('hg.admin')
276 @HasPermissionAllDecorator('hg.admin')
277 def show(self, repo_name, format='html'):
277 def show(self, repo_name, format='html'):
278 """GET /repos/repo_name: Show a specific item"""
278 """GET /repos/repo_name: Show a specific item"""
279 # url('repo', repo_name=ID)
279 # url('repo', repo_name=ID)
280
280
281 @HasPermissionAllDecorator('hg.admin')
281 @HasPermissionAllDecorator('hg.admin')
282 def edit(self, repo_name, format='html'):
282 def edit(self, repo_name, format='html'):
283 """GET /repos/repo_name/edit: Form to edit an existing item"""
283 """GET /repos/repo_name/edit: Form to edit an existing item"""
284 # url('edit_repo', repo_name=ID)
284 # url('edit_repo', repo_name=ID)
285 r = ScmModel().get(repo_name)[0]
285 repo, dbrepo = ScmModel().get(repo_name, retval='repo')
286
286
287 repo_model = RepoModel()
287 repo_model = RepoModel()
288 c.repo_info = repo_model.get_by_repo_name(repo_name)
288 c.repo_info = repo_model.get_by_repo_name(repo_name)
289
289
290 if c.repo_info is None:
290 if c.repo_info is None:
291 h.flash(_('%s repository is not mapped to db perhaps'
291 h.flash(_('%s repository is not mapped to db perhaps'
292 ' it was created or renamed from the filesystem'
292 ' it was created or renamed from the filesystem'
293 ' please run the application again'
293 ' please run the application again'
294 ' in order to rescan repositories') % repo_name,
294 ' in order to rescan repositories') % repo_name,
295 category='error')
295 category='error')
296
296
297 return redirect(url('repos'))
297 return redirect(url('repos'))
298
298
299 if c.repo_info.stats:
299 if c.repo_info.stats:
300 last_rev = c.repo_info.stats.stat_on_revision
300 last_rev = c.repo_info.stats.stat_on_revision
301 else:
301 else:
302 last_rev = 0
302 last_rev = 0
303 c.stats_revision = last_rev
303 c.stats_revision = last_rev
304
304
305 c.repo_last_rev = r.revisions[-1] if r.revisions else 0
305 c.repo_last_rev = repo.revisions[-1] if repo.revisions else 0
306
306
307 if last_rev == 0 or c.repo_last_rev == 0:
307 if last_rev == 0 or c.repo_last_rev == 0:
308 c.stats_percentage = 0
308 c.stats_percentage = 0
309 else:
309 else:
310 c.stats_percentage = '%.2f' % ((float((last_rev)) /
310 c.stats_percentage = '%.2f' % ((float((last_rev)) /
311 c.repo_last_rev) * 100)
311 c.repo_last_rev) * 100)
312
312
313 c.users_array = repo_model.get_users_js()
313 c.users_array = repo_model.get_users_js()
314 c.users_groups_array = repo_model.get_users_groups_js()
314 c.users_groups_array = repo_model.get_users_groups_js()
315
315
316 defaults = c.repo_info.get_dict()
316 defaults = c.repo_info.get_dict()
317
317
318 #fill owner
318 #fill owner
319 if c.repo_info.user:
319 if c.repo_info.user:
320 defaults.update({'user':c.repo_info.user.username})
320 defaults.update({'user':c.repo_info.user.username})
321 else:
321 else:
322 replacement_user = self.sa.query(User)\
322 replacement_user = self.sa.query(User)\
323 .filter(User.admin == True).first().username
323 .filter(User.admin == True).first().username
324 defaults.update({'user':replacement_user})
324 defaults.update({'user':replacement_user})
325
325
326
326
327 #fill repository users
327 #fill repository users
328 for p in c.repo_info.repo_to_perm:
328 for p in c.repo_info.repo_to_perm:
329 defaults.update({'u_perm_%s' % p.user.username:
329 defaults.update({'u_perm_%s' % p.user.username:
330 p.permission.permission_name})
330 p.permission.permission_name})
331
331
332 #fill repository groups
332 #fill repository groups
333 for p in c.repo_info.users_group_to_perm:
333 for p in c.repo_info.users_group_to_perm:
334 defaults.update({'g_perm_%s' % p.users_group.users_group_name:
334 defaults.update({'g_perm_%s' % p.users_group.users_group_name:
335 p.permission.permission_name})
335 p.permission.permission_name})
336
336
337 return htmlfill.render(
337 return htmlfill.render(
338 render('admin/repos/repo_edit.html'),
338 render('admin/repos/repo_edit.html'),
339 defaults=defaults,
339 defaults=defaults,
340 encoding="UTF-8",
340 encoding="UTF-8",
341 force_defaults=False
341 force_defaults=False
342 )
342 )
@@ -1,347 +1,350 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) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
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, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import logging
28 import logging
29 import traceback
29 import traceback
30 import formencode
30 import formencode
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
45 from rhodecode.model.db import RhodeCodeUi, Repository
46 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
46 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
47 ApplicationUiSettingsForm
47 ApplicationUiSettingsForm
48 from rhodecode.model.scm import ScmModel
48 from rhodecode.model.scm import ScmModel
49 from rhodecode.model.settings import SettingsModel
49 from rhodecode.model.settings import SettingsModel
50 from rhodecode.model.user import UserModel
50 from rhodecode.model.user import UserModel
51 from rhodecode.model.repo import RepoModel
51 from rhodecode.model.repo import RepoModel
52
52
53 log = logging.getLogger(__name__)
53 log = logging.getLogger(__name__)
54
54
55
55
56 class SettingsController(BaseController):
56 class SettingsController(BaseController):
57 """REST Controller styled on the Atom Publishing Protocol"""
57 """REST Controller styled on the Atom Publishing Protocol"""
58 # To properly map this controller, ensure your config/routing.py
58 # To properly map this controller, ensure your config/routing.py
59 # file has a resource setup:
59 # file has a resource setup:
60 # map.resource('setting', 'settings', controller='admin/settings',
60 # map.resource('setting', 'settings', controller='admin/settings',
61 # path_prefix='/admin', name_prefix='admin_')
61 # path_prefix='/admin', name_prefix='admin_')
62
62
63
63
64 @LoginRequired()
64 @LoginRequired()
65 def __before__(self):
65 def __before__(self):
66 c.admin_user = session.get('admin_user')
66 c.admin_user = session.get('admin_user')
67 c.admin_username = session.get('admin_username')
67 c.admin_username = session.get('admin_username')
68 super(SettingsController, self).__before__()
68 super(SettingsController, self).__before__()
69
69
70
70
71 @HasPermissionAllDecorator('hg.admin')
71 @HasPermissionAllDecorator('hg.admin')
72 def index(self, format='html'):
72 def index(self, format='html'):
73 """GET /admin/settings: All items in the collection"""
73 """GET /admin/settings: All items in the collection"""
74 # url('admin_settings')
74 # url('admin_settings')
75
75
76 defaults = SettingsModel().get_app_settings()
76 defaults = SettingsModel().get_app_settings()
77 defaults.update(self.get_hg_ui_settings())
77 defaults.update(self.get_hg_ui_settings())
78 return htmlfill.render(
78 return htmlfill.render(
79 render('admin/settings/settings.html'),
79 render('admin/settings/settings.html'),
80 defaults=defaults,
80 defaults=defaults,
81 encoding="UTF-8",
81 encoding="UTF-8",
82 force_defaults=False
82 force_defaults=False
83 )
83 )
84
84
85 @HasPermissionAllDecorator('hg.admin')
85 @HasPermissionAllDecorator('hg.admin')
86 def create(self):
86 def create(self):
87 """POST /admin/settings: Create a new item"""
87 """POST /admin/settings: Create a new item"""
88 # url('admin_settings')
88 # url('admin_settings')
89
89
90 @HasPermissionAllDecorator('hg.admin')
90 @HasPermissionAllDecorator('hg.admin')
91 def new(self, format='html'):
91 def new(self, format='html'):
92 """GET /admin/settings/new: Form to create a new item"""
92 """GET /admin/settings/new: Form to create a new item"""
93 # url('admin_new_setting')
93 # url('admin_new_setting')
94
94
95 @HasPermissionAllDecorator('hg.admin')
95 @HasPermissionAllDecorator('hg.admin')
96 def update(self, setting_id):
96 def update(self, setting_id):
97 """PUT /admin/settings/setting_id: Update an existing item"""
97 """PUT /admin/settings/setting_id: Update an existing item"""
98 # Forms posted to this method should contain a hidden field:
98 # Forms posted to this method should contain a hidden field:
99 # <input type="hidden" name="_method" value="PUT" />
99 # <input type="hidden" name="_method" value="PUT" />
100 # Or using helpers:
100 # Or using helpers:
101 # h.form(url('admin_setting', setting_id=ID),
101 # h.form(url('admin_setting', setting_id=ID),
102 # method='put')
102 # method='put')
103 # url('admin_setting', setting_id=ID)
103 # url('admin_setting', setting_id=ID)
104 if setting_id == 'mapping':
104 if setting_id == 'mapping':
105 rm_obsolete = request.POST.get('destroy', False)
105 rm_obsolete = request.POST.get('destroy', False)
106 log.debug('Rescanning directories with destroy=%s', rm_obsolete)
106 log.debug('Rescanning directories with destroy=%s', rm_obsolete)
107 initial = ScmModel().repo_scan()
107 initial = ScmModel().repo_scan()
108 log.debug('invalidating all repositories')
108 for repo_name in initial.keys():
109 for repo_name in initial.keys():
109 invalidate_cache('get_repo_cached_%s' % repo_name)
110 invalidate_cache('get_repo_cached_%s' % repo_name)
110
111
111 repo2db_mapper(initial, rm_obsolete)
112 added, removed = repo2db_mapper(initial, rm_obsolete)
112
113
113 h.flash(_('Repositories successfully rescanned'), category='success')
114 h.flash(_('Repositories successfully'
115 ' rescanned added: %s,removed: %s') % (added, removed)
116 , category='success')
114
117
115 if setting_id == 'whoosh':
118 if setting_id == 'whoosh':
116 repo_location = self.get_hg_ui_settings()['paths_root_path']
119 repo_location = self.get_hg_ui_settings()['paths_root_path']
117 full_index = request.POST.get('full_index', False)
120 full_index = request.POST.get('full_index', False)
118 task = run_task(tasks.whoosh_index, repo_location, full_index)
121 task = run_task(tasks.whoosh_index, repo_location, full_index)
119
122
120 h.flash(_('Whoosh reindex task scheduled'), category='success')
123 h.flash(_('Whoosh reindex task scheduled'), category='success')
121 if setting_id == 'global':
124 if setting_id == 'global':
122
125
123 application_form = ApplicationSettingsForm()()
126 application_form = ApplicationSettingsForm()()
124 try:
127 try:
125 form_result = application_form.to_python(dict(request.POST))
128 form_result = application_form.to_python(dict(request.POST))
126 settings_model = SettingsModel()
129 settings_model = SettingsModel()
127
130
128 try:
131 try:
129 hgsettings1 = settings_model.get('title')
132 hgsettings1 = settings_model.get('title')
130 hgsettings1.app_settings_value = form_result['rhodecode_title']
133 hgsettings1.app_settings_value = form_result['rhodecode_title']
131
134
132 hgsettings2 = settings_model.get('realm')
135 hgsettings2 = settings_model.get('realm')
133 hgsettings2.app_settings_value = form_result['rhodecode_realm']
136 hgsettings2.app_settings_value = form_result['rhodecode_realm']
134
137
135 hgsettings3 = settings_model.get('ga_code')
138 hgsettings3 = settings_model.get('ga_code')
136 hgsettings3.app_settings_value = form_result['rhodecode_ga_code']
139 hgsettings3.app_settings_value = form_result['rhodecode_ga_code']
137
140
138
141
139
142
140 self.sa.add(hgsettings1)
143 self.sa.add(hgsettings1)
141 self.sa.add(hgsettings2)
144 self.sa.add(hgsettings2)
142 self.sa.add(hgsettings3)
145 self.sa.add(hgsettings3)
143 self.sa.commit()
146 self.sa.commit()
144 set_rhodecode_config(config)
147 set_rhodecode_config(config)
145 h.flash(_('Updated application settings'),
148 h.flash(_('Updated application settings'),
146 category='success')
149 category='success')
147
150
148 except:
151 except:
149 log.error(traceback.format_exc())
152 log.error(traceback.format_exc())
150 h.flash(_('error occurred during updating application settings'),
153 h.flash(_('error occurred during updating application settings'),
151 category='error')
154 category='error')
152
155
153 self.sa.rollback()
156 self.sa.rollback()
154
157
155
158
156 except formencode.Invalid, errors:
159 except formencode.Invalid, errors:
157 return htmlfill.render(
160 return htmlfill.render(
158 render('admin/settings/settings.html'),
161 render('admin/settings/settings.html'),
159 defaults=errors.value,
162 defaults=errors.value,
160 errors=errors.error_dict or {},
163 errors=errors.error_dict or {},
161 prefix_error=False,
164 prefix_error=False,
162 encoding="UTF-8")
165 encoding="UTF-8")
163
166
164 if setting_id == 'mercurial':
167 if setting_id == 'mercurial':
165 application_form = ApplicationUiSettingsForm()()
168 application_form = ApplicationUiSettingsForm()()
166 try:
169 try:
167 form_result = application_form.to_python(dict(request.POST))
170 form_result = application_form.to_python(dict(request.POST))
168
171
169 try:
172 try:
170
173
171 hgsettings1 = self.sa.query(RhodeCodeUi)\
174 hgsettings1 = self.sa.query(RhodeCodeUi)\
172 .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
175 .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
173 hgsettings1.ui_value = form_result['web_push_ssl']
176 hgsettings1.ui_value = form_result['web_push_ssl']
174
177
175 hgsettings2 = self.sa.query(RhodeCodeUi)\
178 hgsettings2 = self.sa.query(RhodeCodeUi)\
176 .filter(RhodeCodeUi.ui_key == '/').one()
179 .filter(RhodeCodeUi.ui_key == '/').one()
177 hgsettings2.ui_value = form_result['paths_root_path']
180 hgsettings2.ui_value = form_result['paths_root_path']
178
181
179
182
180 #HOOKS
183 #HOOKS
181 hgsettings3 = self.sa.query(RhodeCodeUi)\
184 hgsettings3 = self.sa.query(RhodeCodeUi)\
182 .filter(RhodeCodeUi.ui_key == 'changegroup.update').one()
185 .filter(RhodeCodeUi.ui_key == 'changegroup.update').one()
183 hgsettings3.ui_active = bool(form_result['hooks_changegroup_update'])
186 hgsettings3.ui_active = bool(form_result['hooks_changegroup_update'])
184
187
185 hgsettings4 = self.sa.query(RhodeCodeUi)\
188 hgsettings4 = self.sa.query(RhodeCodeUi)\
186 .filter(RhodeCodeUi.ui_key == 'changegroup.repo_size').one()
189 .filter(RhodeCodeUi.ui_key == 'changegroup.repo_size').one()
187 hgsettings4.ui_active = bool(form_result['hooks_changegroup_repo_size'])
190 hgsettings4.ui_active = bool(form_result['hooks_changegroup_repo_size'])
188
191
189 hgsettings5 = self.sa.query(RhodeCodeUi)\
192 hgsettings5 = self.sa.query(RhodeCodeUi)\
190 .filter(RhodeCodeUi.ui_key == 'pretxnchangegroup.push_logger').one()
193 .filter(RhodeCodeUi.ui_key == 'pretxnchangegroup.push_logger').one()
191 hgsettings5.ui_active = bool(form_result['hooks_pretxnchangegroup_push_logger'])
194 hgsettings5.ui_active = bool(form_result['hooks_pretxnchangegroup_push_logger'])
192
195
193 hgsettings6 = self.sa.query(RhodeCodeUi)\
196 hgsettings6 = self.sa.query(RhodeCodeUi)\
194 .filter(RhodeCodeUi.ui_key == 'preoutgoing.pull_logger').one()
197 .filter(RhodeCodeUi.ui_key == 'preoutgoing.pull_logger').one()
195 hgsettings6.ui_active = bool(form_result['hooks_preoutgoing_pull_logger'])
198 hgsettings6.ui_active = bool(form_result['hooks_preoutgoing_pull_logger'])
196
199
197
200
198 self.sa.add(hgsettings1)
201 self.sa.add(hgsettings1)
199 self.sa.add(hgsettings2)
202 self.sa.add(hgsettings2)
200 self.sa.add(hgsettings3)
203 self.sa.add(hgsettings3)
201 self.sa.add(hgsettings4)
204 self.sa.add(hgsettings4)
202 self.sa.add(hgsettings5)
205 self.sa.add(hgsettings5)
203 self.sa.add(hgsettings6)
206 self.sa.add(hgsettings6)
204 self.sa.commit()
207 self.sa.commit()
205
208
206 h.flash(_('Updated mercurial settings'),
209 h.flash(_('Updated mercurial settings'),
207 category='success')
210 category='success')
208
211
209 except:
212 except:
210 log.error(traceback.format_exc())
213 log.error(traceback.format_exc())
211 h.flash(_('error occurred during updating application settings'),
214 h.flash(_('error occurred during updating application settings'),
212 category='error')
215 category='error')
213
216
214 self.sa.rollback()
217 self.sa.rollback()
215
218
216
219
217 except formencode.Invalid, errors:
220 except formencode.Invalid, errors:
218 return htmlfill.render(
221 return htmlfill.render(
219 render('admin/settings/settings.html'),
222 render('admin/settings/settings.html'),
220 defaults=errors.value,
223 defaults=errors.value,
221 errors=errors.error_dict or {},
224 errors=errors.error_dict or {},
222 prefix_error=False,
225 prefix_error=False,
223 encoding="UTF-8")
226 encoding="UTF-8")
224
227
225
228
226
229
227 return redirect(url('admin_settings'))
230 return redirect(url('admin_settings'))
228
231
229 @HasPermissionAllDecorator('hg.admin')
232 @HasPermissionAllDecorator('hg.admin')
230 def delete(self, setting_id):
233 def delete(self, setting_id):
231 """DELETE /admin/settings/setting_id: Delete an existing item"""
234 """DELETE /admin/settings/setting_id: Delete an existing item"""
232 # Forms posted to this method should contain a hidden field:
235 # Forms posted to this method should contain a hidden field:
233 # <input type="hidden" name="_method" value="DELETE" />
236 # <input type="hidden" name="_method" value="DELETE" />
234 # Or using helpers:
237 # Or using helpers:
235 # h.form(url('admin_setting', setting_id=ID),
238 # h.form(url('admin_setting', setting_id=ID),
236 # method='delete')
239 # method='delete')
237 # url('admin_setting', setting_id=ID)
240 # url('admin_setting', setting_id=ID)
238
241
239 @HasPermissionAllDecorator('hg.admin')
242 @HasPermissionAllDecorator('hg.admin')
240 def show(self, setting_id, format='html'):
243 def show(self, setting_id, format='html'):
241 """GET /admin/settings/setting_id: Show a specific item"""
244 """GET /admin/settings/setting_id: Show a specific item"""
242 # url('admin_setting', setting_id=ID)
245 # url('admin_setting', setting_id=ID)
243
246
244 @HasPermissionAllDecorator('hg.admin')
247 @HasPermissionAllDecorator('hg.admin')
245 def edit(self, setting_id, format='html'):
248 def edit(self, setting_id, format='html'):
246 """GET /admin/settings/setting_id/edit: Form to edit an existing item"""
249 """GET /admin/settings/setting_id/edit: Form to edit an existing item"""
247 # url('admin_edit_setting', setting_id=ID)
250 # url('admin_edit_setting', setting_id=ID)
248
251
249 @NotAnonymous()
252 @NotAnonymous()
250 def my_account(self):
253 def my_account(self):
251 """
254 """
252 GET /_admin/my_account Displays info about my account
255 GET /_admin/my_account Displays info about my account
253 """
256 """
254 # url('admin_settings_my_account')
257 # url('admin_settings_my_account')
255
258
256 c.user = UserModel().get(c.rhodecode_user.user_id, cache=False)
259 c.user = UserModel().get(c.rhodecode_user.user_id, cache=False)
257 all_repos = self.sa.query(Repository)\
260 all_repos = self.sa.query(Repository)\
258 .filter(Repository.user_id == c.user.user_id)\
261 .filter(Repository.user_id == c.user.user_id)\
259 .order_by(func.lower(Repository.repo_name))\
262 .order_by(func.lower(Repository.repo_name))\
260 .all()
263 .all()
261
264
262 c.user_repos = ScmModel().get_repos(all_repos)
265 c.user_repos = ScmModel().get_repos(all_repos)
263
266
264 if c.user.username == 'default':
267 if c.user.username == 'default':
265 h.flash(_("You can't edit this user since it's"
268 h.flash(_("You can't edit this user since it's"
266 " crucial for entire application"), category='warning')
269 " crucial for entire application"), category='warning')
267 return redirect(url('users'))
270 return redirect(url('users'))
268
271
269 defaults = c.user.get_dict()
272 defaults = c.user.get_dict()
270 return htmlfill.render(
273 return htmlfill.render(
271 render('admin/users/user_edit_my_account.html'),
274 render('admin/users/user_edit_my_account.html'),
272 defaults=defaults,
275 defaults=defaults,
273 encoding="UTF-8",
276 encoding="UTF-8",
274 force_defaults=False
277 force_defaults=False
275 )
278 )
276
279
277 def my_account_update(self):
280 def my_account_update(self):
278 """PUT /_admin/my_account_update: Update an existing item"""
281 """PUT /_admin/my_account_update: Update an existing item"""
279 # Forms posted to this method should contain a hidden field:
282 # Forms posted to this method should contain a hidden field:
280 # <input type="hidden" name="_method" value="PUT" />
283 # <input type="hidden" name="_method" value="PUT" />
281 # Or using helpers:
284 # Or using helpers:
282 # h.form(url('admin_settings_my_account_update'),
285 # h.form(url('admin_settings_my_account_update'),
283 # method='put')
286 # method='put')
284 # url('admin_settings_my_account_update', id=ID)
287 # url('admin_settings_my_account_update', id=ID)
285 user_model = UserModel()
288 user_model = UserModel()
286 uid = c.rhodecode_user.user_id
289 uid = c.rhodecode_user.user_id
287 _form = UserForm(edit=True, old_data={'user_id':uid,
290 _form = UserForm(edit=True, old_data={'user_id':uid,
288 'email':c.rhodecode_user.email})()
291 'email':c.rhodecode_user.email})()
289 form_result = {}
292 form_result = {}
290 try:
293 try:
291 form_result = _form.to_python(dict(request.POST))
294 form_result = _form.to_python(dict(request.POST))
292 user_model.update_my_account(uid, form_result)
295 user_model.update_my_account(uid, form_result)
293 h.flash(_('Your account was updated successfully'),
296 h.flash(_('Your account was updated successfully'),
294 category='success')
297 category='success')
295
298
296 except formencode.Invalid, errors:
299 except formencode.Invalid, errors:
297 c.user = user_model.get(c.rhodecode_user.user_id, cache=False)
300 c.user = user_model.get(c.rhodecode_user.user_id, cache=False)
298 c.user = UserModel().get(c.rhodecode_user.user_id, cache=False)
301 c.user = UserModel().get(c.rhodecode_user.user_id, cache=False)
299 all_repos = self.sa.query(Repository)\
302 all_repos = self.sa.query(Repository)\
300 .filter(Repository.user_id == c.user.user_id)\
303 .filter(Repository.user_id == c.user.user_id)\
301 .order_by(func.lower(Repository.repo_name))\
304 .order_by(func.lower(Repository.repo_name))\
302 .all()
305 .all()
303 c.user_repos = ScmModel().get_repos(all_repos)
306 c.user_repos = ScmModel().get_repos(all_repos)
304
307
305 return htmlfill.render(
308 return htmlfill.render(
306 render('admin/users/user_edit_my_account.html'),
309 render('admin/users/user_edit_my_account.html'),
307 defaults=errors.value,
310 defaults=errors.value,
308 errors=errors.error_dict or {},
311 errors=errors.error_dict or {},
309 prefix_error=False,
312 prefix_error=False,
310 encoding="UTF-8")
313 encoding="UTF-8")
311 except Exception:
314 except Exception:
312 log.error(traceback.format_exc())
315 log.error(traceback.format_exc())
313 h.flash(_('error occurred during update of user %s') \
316 h.flash(_('error occurred during update of user %s') \
314 % form_result.get('username'), category='error')
317 % form_result.get('username'), category='error')
315
318
316 return redirect(url('my_account'))
319 return redirect(url('my_account'))
317
320
318 @NotAnonymous()
321 @NotAnonymous()
319 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
322 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
320 def create_repository(self):
323 def create_repository(self):
321 """GET /_admin/create_repository: Form to create a new item"""
324 """GET /_admin/create_repository: Form to create a new item"""
322 new_repo = request.GET.get('repo', '')
325 new_repo = request.GET.get('repo', '')
323 c.new_repo = repo_name_slug(new_repo)
326 c.new_repo = repo_name_slug(new_repo)
324
327
325 return render('admin/repos/repo_add_create_repository.html')
328 return render('admin/repos/repo_add_create_repository.html')
326
329
327 def get_hg_ui_settings(self):
330 def get_hg_ui_settings(self):
328 ret = self.sa.query(RhodeCodeUi).all()
331 ret = self.sa.query(RhodeCodeUi).all()
329
332
330 if not ret:
333 if not ret:
331 raise Exception('Could not get application ui settings !')
334 raise Exception('Could not get application ui settings !')
332 settings = {}
335 settings = {}
333 for each in ret:
336 for each in ret:
334 k = each.ui_key
337 k = each.ui_key
335 v = each.ui_value
338 v = each.ui_value
336 if k == '/':
339 if k == '/':
337 k = 'root_path'
340 k = 'root_path'
338
341
339 if k.find('.') != -1:
342 if k.find('.') != -1:
340 k = k.replace('.', '_')
343 k = k.replace('.', '_')
341
344
342 if each.ui_section == 'hooks':
345 if each.ui_section == 'hooks':
343 v = each.ui_active
346 v = each.ui_active
344
347
345 settings[each.ui_section + '_' + k] = v
348 settings[each.ui_section + '_' + k] = v
346
349
347 return settings
350 return settings
@@ -1,685 +1,688 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.utils
3 rhodecode.lib.utils
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Utilities library for RhodeCode
6 Utilities library for RhodeCode
7
7
8 :created_on: Apr 18, 2010
8 :created_on: Apr 18, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
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, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import os
28 import os
29 import logging
29 import logging
30 import datetime
30 import datetime
31 import traceback
31 import traceback
32 import paste
32 import paste
33 import beaker
33 import beaker
34
34
35 from paste.script.command import Command, BadCommand
35 from paste.script.command import Command, BadCommand
36
36
37 from UserDict import DictMixin
37 from UserDict import DictMixin
38
38
39 from mercurial import ui, config, hg
39 from mercurial import ui, config, hg
40 from mercurial.error import RepoError
40 from mercurial.error import RepoError
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 vcs.backends.base import BaseChangeset
44 from vcs.backends.base import BaseChangeset
45 from vcs.utils.lazy import LazyProperty
45 from vcs.utils.lazy import LazyProperty
46
46
47 from rhodecode.model import meta
47 from rhodecode.model import meta
48 from rhodecode.model.caching_query import FromCache
48 from rhodecode.model.caching_query import FromCache
49 from rhodecode.model.db import Repository, User, RhodeCodeUi, UserLog, Group
49 from rhodecode.model.db import Repository, User, RhodeCodeUi, UserLog, Group
50 from rhodecode.model.repo import RepoModel
50 from rhodecode.model.repo import RepoModel
51 from rhodecode.model.user import UserModel
51 from rhodecode.model.user import UserModel
52
52
53 log = logging.getLogger(__name__)
53 log = logging.getLogger(__name__)
54
54
55
55
56 def recursive_replace(str, replace=' '):
56 def recursive_replace(str, replace=' '):
57 """Recursive replace of given sign to just one instance
57 """Recursive replace of given sign to just one instance
58
58
59 :param str: given string
59 :param str: given string
60 :param replace: char to find and replace multiple instances
60 :param replace: char to find and replace multiple instances
61
61
62 Examples::
62 Examples::
63 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
63 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
64 'Mighty-Mighty-Bo-sstones'
64 'Mighty-Mighty-Bo-sstones'
65 """
65 """
66
66
67 if str.find(replace * 2) == -1:
67 if str.find(replace * 2) == -1:
68 return str
68 return str
69 else:
69 else:
70 str = str.replace(replace * 2, replace)
70 str = str.replace(replace * 2, replace)
71 return recursive_replace(str, replace)
71 return recursive_replace(str, replace)
72
72
73 def repo_name_slug(value):
73 def repo_name_slug(value):
74 """Return slug of name of repository
74 """Return slug of name of repository
75 This function is called on each creation/modification
75 This function is called on each creation/modification
76 of repository to prevent bad names in repo
76 of repository to prevent bad names in repo
77 """
77 """
78
78
79 slug = remove_formatting(value)
79 slug = remove_formatting(value)
80 slug = strip_tags(slug)
80 slug = strip_tags(slug)
81
81
82 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
82 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
83 slug = slug.replace(c, '-')
83 slug = slug.replace(c, '-')
84 slug = recursive_replace(slug, '-')
84 slug = recursive_replace(slug, '-')
85 slug = collapse(slug, '-')
85 slug = collapse(slug, '-')
86 return slug
86 return slug
87
87
88 def get_repo_slug(request):
88 def get_repo_slug(request):
89 return request.environ['pylons.routes_dict'].get('repo_name')
89 return request.environ['pylons.routes_dict'].get('repo_name')
90
90
91 def action_logger(user, action, repo, ipaddr='', sa=None):
91 def action_logger(user, action, repo, ipaddr='', sa=None):
92 """
92 """
93 Action logger for various actions made by users
93 Action logger for various actions made by users
94
94
95 :param user: user that made this action, can be a unique username string or
95 :param user: user that made this action, can be a unique username string or
96 object containing user_id attribute
96 object containing user_id attribute
97 :param action: action to log, should be on of predefined unique actions for
97 :param action: action to log, should be on of predefined unique actions for
98 easy translations
98 easy translations
99 :param repo: string name of repository or object containing repo_id,
99 :param repo: string name of repository or object containing repo_id,
100 that action was made on
100 that action was made on
101 :param ipaddr: optional ip address from what the action was made
101 :param ipaddr: optional ip address from what the action was made
102 :param sa: optional sqlalchemy session
102 :param sa: optional sqlalchemy session
103
103
104 """
104 """
105
105
106 if not sa:
106 if not sa:
107 sa = meta.Session()
107 sa = meta.Session()
108
108
109 try:
109 try:
110 um = UserModel()
110 um = UserModel()
111 if hasattr(user, 'user_id'):
111 if hasattr(user, 'user_id'):
112 user_obj = user
112 user_obj = user
113 elif isinstance(user, basestring):
113 elif isinstance(user, basestring):
114 user_obj = um.get_by_username(user, cache=False)
114 user_obj = um.get_by_username(user, cache=False)
115 else:
115 else:
116 raise Exception('You have to provide user object or username')
116 raise Exception('You have to provide user object or username')
117
117
118
118
119 rm = RepoModel()
119 rm = RepoModel()
120 if hasattr(repo, 'repo_id'):
120 if hasattr(repo, 'repo_id'):
121 repo_obj = rm.get(repo.repo_id, cache=False)
121 repo_obj = rm.get(repo.repo_id, cache=False)
122 repo_name = repo_obj.repo_name
122 repo_name = repo_obj.repo_name
123 elif isinstance(repo, basestring):
123 elif isinstance(repo, basestring):
124 repo_name = repo.lstrip('/')
124 repo_name = repo.lstrip('/')
125 repo_obj = rm.get_by_repo_name(repo_name, cache=False)
125 repo_obj = rm.get_by_repo_name(repo_name, cache=False)
126 else:
126 else:
127 raise Exception('You have to provide repository to action logger')
127 raise Exception('You have to provide repository to action logger')
128
128
129
129
130 user_log = UserLog()
130 user_log = UserLog()
131 user_log.user_id = user_obj.user_id
131 user_log.user_id = user_obj.user_id
132 user_log.action = action
132 user_log.action = action
133
133
134 user_log.repository_id = repo_obj.repo_id
134 user_log.repository_id = repo_obj.repo_id
135 user_log.repository_name = repo_name
135 user_log.repository_name = repo_name
136
136
137 user_log.action_date = datetime.datetime.now()
137 user_log.action_date = datetime.datetime.now()
138 user_log.user_ip = ipaddr
138 user_log.user_ip = ipaddr
139 sa.add(user_log)
139 sa.add(user_log)
140 sa.commit()
140 sa.commit()
141
141
142 log.info('Adding user %s, action %s on %s', user_obj, action, repo)
142 log.info('Adding user %s, action %s on %s', user_obj, action, repo)
143 except:
143 except:
144 log.error(traceback.format_exc())
144 log.error(traceback.format_exc())
145 sa.rollback()
145 sa.rollback()
146
146
147 def get_repos(path, recursive=False):
147 def get_repos(path, recursive=False):
148 """
148 """
149 Scans given path for repos and return (name,(type,path)) tuple
149 Scans given path for repos and return (name,(type,path)) tuple
150
150
151 :param path: path to scann for repositories
151 :param path: path to scann for repositories
152 :param recursive: recursive search and return names with subdirs in front
152 :param recursive: recursive search and return names with subdirs in front
153 """
153 """
154 from vcs.utils.helpers import get_scm
154 from vcs.utils.helpers import get_scm
155 from vcs.exceptions import VCSError
155 from vcs.exceptions import VCSError
156
156
157 if path.endswith('/'):
157 if path.endswith('/'):
158 #add ending slash for better results
158 #add ending slash for better results
159 path = path[:-1]
159 path = path[:-1]
160
160
161 def _get_repos(p):
161 def _get_repos(p):
162 for dirpath in os.listdir(p):
162 for dirpath in os.listdir(p):
163 if os.path.isfile(os.path.join(p, dirpath)):
163 if os.path.isfile(os.path.join(p, dirpath)):
164 continue
164 continue
165 cur_path = os.path.join(p, dirpath)
165 cur_path = os.path.join(p, dirpath)
166 try:
166 try:
167 scm_info = get_scm(cur_path)
167 scm_info = get_scm(cur_path)
168 yield scm_info[1].split(path)[-1].lstrip('/'), scm_info
168 yield scm_info[1].split(path)[-1].lstrip('/'), scm_info
169 except VCSError:
169 except VCSError:
170 if not recursive:
170 if not recursive:
171 continue
171 continue
172 #check if this dir containts other repos for recursive scan
172 #check if this dir containts other repos for recursive scan
173 rec_path = os.path.join(p, dirpath)
173 rec_path = os.path.join(p, dirpath)
174 if os.path.isdir(rec_path):
174 if os.path.isdir(rec_path):
175 for inner_scm in _get_repos(rec_path):
175 for inner_scm in _get_repos(rec_path):
176 yield inner_scm
176 yield inner_scm
177
177
178 return _get_repos(path)
178 return _get_repos(path)
179
179
180 def check_repo_fast(repo_name, base_path):
180 def check_repo_fast(repo_name, base_path):
181 """
181 """
182 Check given path for existence of directory
182 Check given path for existence of directory
183 :param repo_name:
183 :param repo_name:
184 :param base_path:
184 :param base_path:
185
185
186 :return False: if this directory is present
186 :return False: if this directory is present
187 """
187 """
188 if os.path.isdir(os.path.join(base_path, repo_name)):return False
188 if os.path.isdir(os.path.join(base_path, repo_name)):return False
189 return True
189 return True
190
190
191 def check_repo(repo_name, base_path, verify=True):
191 def check_repo(repo_name, base_path, verify=True):
192
192
193 repo_path = os.path.join(base_path, repo_name)
193 repo_path = os.path.join(base_path, repo_name)
194
194
195 try:
195 try:
196 if not check_repo_fast(repo_name, base_path):
196 if not check_repo_fast(repo_name, base_path):
197 return False
197 return False
198 r = hg.repository(ui.ui(), repo_path)
198 r = hg.repository(ui.ui(), repo_path)
199 if verify:
199 if verify:
200 hg.verify(r)
200 hg.verify(r)
201 #here we hnow that repo exists it was verified
201 #here we hnow that repo exists it was verified
202 log.info('%s repo is already created', repo_name)
202 log.info('%s repo is already created', repo_name)
203 return False
203 return False
204 except RepoError:
204 except RepoError:
205 #it means that there is no valid repo there...
205 #it means that there is no valid repo there...
206 log.info('%s repo is free for creation', repo_name)
206 log.info('%s repo is free for creation', repo_name)
207 return True
207 return True
208
208
209 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
209 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
210 while True:
210 while True:
211 ok = raw_input(prompt)
211 ok = raw_input(prompt)
212 if ok in ('y', 'ye', 'yes'): return True
212 if ok in ('y', 'ye', 'yes'): return True
213 if ok in ('n', 'no', 'nop', 'nope'): return False
213 if ok in ('n', 'no', 'nop', 'nope'): return False
214 retries = retries - 1
214 retries = retries - 1
215 if retries < 0: raise IOError
215 if retries < 0: raise IOError
216 print complaint
216 print complaint
217
217
218 #propagated from mercurial documentation
218 #propagated from mercurial documentation
219 ui_sections = ['alias', 'auth',
219 ui_sections = ['alias', 'auth',
220 'decode/encode', 'defaults',
220 'decode/encode', 'defaults',
221 'diff', 'email',
221 'diff', 'email',
222 'extensions', 'format',
222 'extensions', 'format',
223 'merge-patterns', 'merge-tools',
223 'merge-patterns', 'merge-tools',
224 'hooks', 'http_proxy',
224 'hooks', 'http_proxy',
225 'smtp', 'patch',
225 'smtp', 'patch',
226 'paths', 'profiling',
226 'paths', 'profiling',
227 'server', 'trusted',
227 'server', 'trusted',
228 'ui', 'web', ]
228 'ui', 'web', ]
229
229
230 def make_ui(read_from='file', path=None, checkpaths=True):
230 def make_ui(read_from='file', path=None, checkpaths=True):
231 """A function that will read python rc files or database
231 """A function that will read python rc files or database
232 and make an mercurial ui object from read options
232 and make an mercurial ui object from read options
233
233
234 :param path: path to mercurial config file
234 :param path: path to mercurial config file
235 :param checkpaths: check the path
235 :param checkpaths: check the path
236 :param read_from: read from 'file' or 'db'
236 :param read_from: read from 'file' or 'db'
237 """
237 """
238
238
239 baseui = ui.ui()
239 baseui = ui.ui()
240
240
241 #clean the baseui object
241 #clean the baseui object
242 baseui._ocfg = config.config()
242 baseui._ocfg = config.config()
243 baseui._ucfg = config.config()
243 baseui._ucfg = config.config()
244 baseui._tcfg = config.config()
244 baseui._tcfg = config.config()
245
245
246 if read_from == 'file':
246 if read_from == 'file':
247 if not os.path.isfile(path):
247 if not os.path.isfile(path):
248 log.warning('Unable to read config file %s' % path)
248 log.warning('Unable to read config file %s' % path)
249 return False
249 return False
250 log.debug('reading hgrc from %s', path)
250 log.debug('reading hgrc from %s', path)
251 cfg = config.config()
251 cfg = config.config()
252 cfg.read(path)
252 cfg.read(path)
253 for section in ui_sections:
253 for section in ui_sections:
254 for k, v in cfg.items(section):
254 for k, v in cfg.items(section):
255 log.debug('settings ui from file[%s]%s:%s', section, k, v)
255 log.debug('settings ui from file[%s]%s:%s', section, k, v)
256 baseui.setconfig(section, k, v)
256 baseui.setconfig(section, k, v)
257
257
258
258
259 elif read_from == 'db':
259 elif read_from == 'db':
260 sa = meta.Session()
260 sa = meta.Session()
261 ret = sa.query(RhodeCodeUi)\
261 ret = sa.query(RhodeCodeUi)\
262 .options(FromCache("sql_cache_short",
262 .options(FromCache("sql_cache_short",
263 "get_hg_ui_settings")).all()
263 "get_hg_ui_settings")).all()
264
264
265 hg_ui = ret
265 hg_ui = ret
266 for ui_ in hg_ui:
266 for ui_ in hg_ui:
267 if ui_.ui_active:
267 if ui_.ui_active:
268 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
268 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
269 ui_.ui_key, ui_.ui_value)
269 ui_.ui_key, ui_.ui_value)
270 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
270 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
271
271
272 meta.Session.remove()
272 meta.Session.remove()
273 return baseui
273 return baseui
274
274
275
275
276 def set_rhodecode_config(config):
276 def set_rhodecode_config(config):
277 """Updates pylons config with new settings from database
277 """Updates pylons config with new settings from database
278
278
279 :param config:
279 :param config:
280 """
280 """
281 from rhodecode.model.settings import SettingsModel
281 from rhodecode.model.settings import SettingsModel
282 hgsettings = SettingsModel().get_app_settings()
282 hgsettings = SettingsModel().get_app_settings()
283
283
284 for k, v in hgsettings.items():
284 for k, v in hgsettings.items():
285 config[k] = v
285 config[k] = v
286
286
287 def invalidate_cache(cache_key, *args):
287 def invalidate_cache(cache_key, *args):
288 """Puts cache invalidation task into db for
288 """Puts cache invalidation task into db for
289 further global cache invalidation
289 further global cache invalidation
290 """
290 """
291
291
292 from rhodecode.model.scm import ScmModel
292 from rhodecode.model.scm import ScmModel
293
293
294 if cache_key.startswith('get_repo_cached_'):
294 if cache_key.startswith('get_repo_cached_'):
295 name = cache_key.split('get_repo_cached_')[-1]
295 name = cache_key.split('get_repo_cached_')[-1]
296 ScmModel().mark_for_invalidation(name)
296 ScmModel().mark_for_invalidation(name)
297
297
298 class EmptyChangeset(BaseChangeset):
298 class EmptyChangeset(BaseChangeset):
299 """
299 """
300 An dummy empty changeset. It's possible to pass hash when creating
300 An dummy empty changeset. It's possible to pass hash when creating
301 an EmptyChangeset
301 an EmptyChangeset
302 """
302 """
303
303
304 def __init__(self, cs='0' * 40):
304 def __init__(self, cs='0' * 40):
305 self._empty_cs = cs
305 self._empty_cs = cs
306 self.revision = -1
306 self.revision = -1
307 self.message = ''
307 self.message = ''
308 self.author = ''
308 self.author = ''
309 self.date = ''
309 self.date = ''
310
310
311 @LazyProperty
311 @LazyProperty
312 def raw_id(self):
312 def raw_id(self):
313 """Returns raw string identifying this changeset, useful for web
313 """Returns raw string identifying this changeset, useful for web
314 representation.
314 representation.
315 """
315 """
316
316
317 return self._empty_cs
317 return self._empty_cs
318
318
319 @LazyProperty
319 @LazyProperty
320 def short_id(self):
320 def short_id(self):
321 return self.raw_id[:12]
321 return self.raw_id[:12]
322
322
323 def get_file_changeset(self, path):
323 def get_file_changeset(self, path):
324 return self
324 return self
325
325
326 def get_file_content(self, path):
326 def get_file_content(self, path):
327 return u''
327 return u''
328
328
329 def get_file_size(self, path):
329 def get_file_size(self, path):
330 return 0
330 return 0
331
331
332 def map_groups(groups):
332 def map_groups(groups):
333 """Checks for groups existence, and creates groups structures.
333 """Checks for groups existence, and creates groups structures.
334 It returns last group in structure
334 It returns last group in structure
335
335
336 :param groups: list of groups structure
336 :param groups: list of groups structure
337 """
337 """
338 sa = meta.Session()
338 sa = meta.Session()
339
339
340 parent = None
340 parent = None
341 group = None
341 group = None
342 for lvl, group_name in enumerate(groups[:-1]):
342 for lvl, group_name in enumerate(groups[:-1]):
343 group = sa.query(Group).filter(Group.group_name == group_name).scalar()
343 group = sa.query(Group).filter(Group.group_name == group_name).scalar()
344
344
345 if group is None:
345 if group is None:
346 group = Group(group_name, parent)
346 group = Group(group_name, parent)
347 sa.add(group)
347 sa.add(group)
348 sa.commit()
348 sa.commit()
349
349
350 parent = group
350 parent = group
351
351
352 return group
352 return group
353
353
354 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
354 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
355 """maps all repos given in initial_repo_list, non existing repositories
355 """maps all repos given in initial_repo_list, non existing repositories
356 are created, if remove_obsolete is True it also check for db entries
356 are created, if remove_obsolete is True it also check for db entries
357 that are not in initial_repo_list and removes them.
357 that are not in initial_repo_list and removes them.
358
358
359 :param initial_repo_list: list of repositories found by scanning methods
359 :param initial_repo_list: list of repositories found by scanning methods
360 :param remove_obsolete: check for obsolete entries in database
360 :param remove_obsolete: check for obsolete entries in database
361 """
361 """
362
362
363 sa = meta.Session()
363 sa = meta.Session()
364 rm = RepoModel()
364 rm = RepoModel()
365 user = sa.query(User).filter(User.admin == True).first()
365 user = sa.query(User).filter(User.admin == True).first()
366
366 added = []
367 for name, repo in initial_repo_list.items():
367 for name, repo in initial_repo_list.items():
368 group = map_groups(name.split('/'))
368 group = map_groups(name.split('/'))
369 if not rm.get_by_repo_name(name, cache=False):
369 if not rm.get_by_repo_name(name, cache=False):
370 log.info('repository %s not found creating default', name)
370 log.info('repository %s not found creating default', name)
371
371 added.append(name)
372 form_data = {
372 form_data = {
373 'repo_name':name,
373 'repo_name':name,
374 'repo_type':repo.alias,
374 'repo_type':repo.alias,
375 'description':repo.description \
375 'description':repo.description \
376 if repo.description != 'unknown' else \
376 if repo.description != 'unknown' else \
377 '%s repository' % name,
377 '%s repository' % name,
378 'private':False,
378 'private':False,
379 'group_id':getattr(group, 'group_id', None)
379 'group_id':getattr(group, 'group_id', None)
380 }
380 }
381 rm.create(form_data, user, just_db=True)
381 rm.create(form_data, user, just_db=True)
382
382
383 removed = []
383 if remove_obsolete:
384 if remove_obsolete:
384 #remove from database those repositories that are not in the filesystem
385 #remove from database those repositories that are not in the filesystem
385 for repo in sa.query(Repository).all():
386 for repo in sa.query(Repository).all():
386 if repo.repo_name not in initial_repo_list.keys():
387 if repo.repo_name not in initial_repo_list.keys():
388 removed.append(repo.repo_name)
387 sa.delete(repo)
389 sa.delete(repo)
388 sa.commit()
390 sa.commit()
389
391
392 return added, removed
390 class OrderedDict(dict, DictMixin):
393 class OrderedDict(dict, DictMixin):
391
394
392 def __init__(self, *args, **kwds):
395 def __init__(self, *args, **kwds):
393 if len(args) > 1:
396 if len(args) > 1:
394 raise TypeError('expected at most 1 arguments, got %d' % len(args))
397 raise TypeError('expected at most 1 arguments, got %d' % len(args))
395 try:
398 try:
396 self.__end
399 self.__end
397 except AttributeError:
400 except AttributeError:
398 self.clear()
401 self.clear()
399 self.update(*args, **kwds)
402 self.update(*args, **kwds)
400
403
401 def clear(self):
404 def clear(self):
402 self.__end = end = []
405 self.__end = end = []
403 end += [None, end, end] # sentinel node for doubly linked list
406 end += [None, end, end] # sentinel node for doubly linked list
404 self.__map = {} # key --> [key, prev, next]
407 self.__map = {} # key --> [key, prev, next]
405 dict.clear(self)
408 dict.clear(self)
406
409
407 def __setitem__(self, key, value):
410 def __setitem__(self, key, value):
408 if key not in self:
411 if key not in self:
409 end = self.__end
412 end = self.__end
410 curr = end[1]
413 curr = end[1]
411 curr[2] = end[1] = self.__map[key] = [key, curr, end]
414 curr[2] = end[1] = self.__map[key] = [key, curr, end]
412 dict.__setitem__(self, key, value)
415 dict.__setitem__(self, key, value)
413
416
414 def __delitem__(self, key):
417 def __delitem__(self, key):
415 dict.__delitem__(self, key)
418 dict.__delitem__(self, key)
416 key, prev, next = self.__map.pop(key)
419 key, prev, next = self.__map.pop(key)
417 prev[2] = next
420 prev[2] = next
418 next[1] = prev
421 next[1] = prev
419
422
420 def __iter__(self):
423 def __iter__(self):
421 end = self.__end
424 end = self.__end
422 curr = end[2]
425 curr = end[2]
423 while curr is not end:
426 while curr is not end:
424 yield curr[0]
427 yield curr[0]
425 curr = curr[2]
428 curr = curr[2]
426
429
427 def __reversed__(self):
430 def __reversed__(self):
428 end = self.__end
431 end = self.__end
429 curr = end[1]
432 curr = end[1]
430 while curr is not end:
433 while curr is not end:
431 yield curr[0]
434 yield curr[0]
432 curr = curr[1]
435 curr = curr[1]
433
436
434 def popitem(self, last=True):
437 def popitem(self, last=True):
435 if not self:
438 if not self:
436 raise KeyError('dictionary is empty')
439 raise KeyError('dictionary is empty')
437 if last:
440 if last:
438 key = reversed(self).next()
441 key = reversed(self).next()
439 else:
442 else:
440 key = iter(self).next()
443 key = iter(self).next()
441 value = self.pop(key)
444 value = self.pop(key)
442 return key, value
445 return key, value
443
446
444 def __reduce__(self):
447 def __reduce__(self):
445 items = [[k, self[k]] for k in self]
448 items = [[k, self[k]] for k in self]
446 tmp = self.__map, self.__end
449 tmp = self.__map, self.__end
447 del self.__map, self.__end
450 del self.__map, self.__end
448 inst_dict = vars(self).copy()
451 inst_dict = vars(self).copy()
449 self.__map, self.__end = tmp
452 self.__map, self.__end = tmp
450 if inst_dict:
453 if inst_dict:
451 return (self.__class__, (items,), inst_dict)
454 return (self.__class__, (items,), inst_dict)
452 return self.__class__, (items,)
455 return self.__class__, (items,)
453
456
454 def keys(self):
457 def keys(self):
455 return list(self)
458 return list(self)
456
459
457 setdefault = DictMixin.setdefault
460 setdefault = DictMixin.setdefault
458 update = DictMixin.update
461 update = DictMixin.update
459 pop = DictMixin.pop
462 pop = DictMixin.pop
460 values = DictMixin.values
463 values = DictMixin.values
461 items = DictMixin.items
464 items = DictMixin.items
462 iterkeys = DictMixin.iterkeys
465 iterkeys = DictMixin.iterkeys
463 itervalues = DictMixin.itervalues
466 itervalues = DictMixin.itervalues
464 iteritems = DictMixin.iteritems
467 iteritems = DictMixin.iteritems
465
468
466 def __repr__(self):
469 def __repr__(self):
467 if not self:
470 if not self:
468 return '%s()' % (self.__class__.__name__,)
471 return '%s()' % (self.__class__.__name__,)
469 return '%s(%r)' % (self.__class__.__name__, self.items())
472 return '%s(%r)' % (self.__class__.__name__, self.items())
470
473
471 def copy(self):
474 def copy(self):
472 return self.__class__(self)
475 return self.__class__(self)
473
476
474 @classmethod
477 @classmethod
475 def fromkeys(cls, iterable, value=None):
478 def fromkeys(cls, iterable, value=None):
476 d = cls()
479 d = cls()
477 for key in iterable:
480 for key in iterable:
478 d[key] = value
481 d[key] = value
479 return d
482 return d
480
483
481 def __eq__(self, other):
484 def __eq__(self, other):
482 if isinstance(other, OrderedDict):
485 if isinstance(other, OrderedDict):
483 return len(self) == len(other) and self.items() == other.items()
486 return len(self) == len(other) and self.items() == other.items()
484 return dict.__eq__(self, other)
487 return dict.__eq__(self, other)
485
488
486 def __ne__(self, other):
489 def __ne__(self, other):
487 return not self == other
490 return not self == other
488
491
489
492
490 #set cache regions for beaker so celery can utilise it
493 #set cache regions for beaker so celery can utilise it
491 def add_cache(settings):
494 def add_cache(settings):
492 cache_settings = {'regions':None}
495 cache_settings = {'regions':None}
493 for key in settings.keys():
496 for key in settings.keys():
494 for prefix in ['beaker.cache.', 'cache.']:
497 for prefix in ['beaker.cache.', 'cache.']:
495 if key.startswith(prefix):
498 if key.startswith(prefix):
496 name = key.split(prefix)[1].strip()
499 name = key.split(prefix)[1].strip()
497 cache_settings[name] = settings[key].strip()
500 cache_settings[name] = settings[key].strip()
498 if cache_settings['regions']:
501 if cache_settings['regions']:
499 for region in cache_settings['regions'].split(','):
502 for region in cache_settings['regions'].split(','):
500 region = region.strip()
503 region = region.strip()
501 region_settings = {}
504 region_settings = {}
502 for key, value in cache_settings.items():
505 for key, value in cache_settings.items():
503 if key.startswith(region):
506 if key.startswith(region):
504 region_settings[key.split('.')[1]] = value
507 region_settings[key.split('.')[1]] = value
505 region_settings['expire'] = int(region_settings.get('expire',
508 region_settings['expire'] = int(region_settings.get('expire',
506 60))
509 60))
507 region_settings.setdefault('lock_dir',
510 region_settings.setdefault('lock_dir',
508 cache_settings.get('lock_dir'))
511 cache_settings.get('lock_dir'))
509 region_settings.setdefault('data_dir',
512 region_settings.setdefault('data_dir',
510 cache_settings.get('data_dir'))
513 cache_settings.get('data_dir'))
511
514
512 if 'type' not in region_settings:
515 if 'type' not in region_settings:
513 region_settings['type'] = cache_settings.get('type',
516 region_settings['type'] = cache_settings.get('type',
514 'memory')
517 'memory')
515 beaker.cache.cache_regions[region] = region_settings
518 beaker.cache.cache_regions[region] = region_settings
516
519
517 def get_current_revision():
520 def get_current_revision():
518 """Returns tuple of (number, id) from repository containing this package
521 """Returns tuple of (number, id) from repository containing this package
519 or None if repository could not be found.
522 or None if repository could not be found.
520 """
523 """
521
524
522 try:
525 try:
523 from vcs import get_repo
526 from vcs import get_repo
524 from vcs.utils.helpers import get_scm
527 from vcs.utils.helpers import get_scm
525 from vcs.exceptions import RepositoryError, VCSError
528 from vcs.exceptions import RepositoryError, VCSError
526 repopath = os.path.join(os.path.dirname(__file__), '..', '..')
529 repopath = os.path.join(os.path.dirname(__file__), '..', '..')
527 scm = get_scm(repopath)[0]
530 scm = get_scm(repopath)[0]
528 repo = get_repo(path=repopath, alias=scm)
531 repo = get_repo(path=repopath, alias=scm)
529 tip = repo.get_changeset()
532 tip = repo.get_changeset()
530 return (tip.revision, tip.short_id)
533 return (tip.revision, tip.short_id)
531 except (ImportError, RepositoryError, VCSError), err:
534 except (ImportError, RepositoryError, VCSError), err:
532 logging.debug("Cannot retrieve rhodecode's revision. Original error "
535 logging.debug("Cannot retrieve rhodecode's revision. Original error "
533 "was: %s" % err)
536 "was: %s" % err)
534 return None
537 return None
535
538
536 #===============================================================================
539 #===============================================================================
537 # TEST FUNCTIONS AND CREATORS
540 # TEST FUNCTIONS AND CREATORS
538 #===============================================================================
541 #===============================================================================
539 def create_test_index(repo_location, full_index):
542 def create_test_index(repo_location, full_index):
540 """Makes default test index
543 """Makes default test index
541 :param repo_location:
544 :param repo_location:
542 :param full_index:
545 :param full_index:
543 """
546 """
544 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
547 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
545 from rhodecode.lib.pidlock import DaemonLock, LockHeld
548 from rhodecode.lib.pidlock import DaemonLock, LockHeld
546 import shutil
549 import shutil
547
550
548 index_location = os.path.join(repo_location, 'index')
551 index_location = os.path.join(repo_location, 'index')
549 if os.path.exists(index_location):
552 if os.path.exists(index_location):
550 shutil.rmtree(index_location)
553 shutil.rmtree(index_location)
551
554
552 try:
555 try:
553 l = DaemonLock()
556 l = DaemonLock()
554 WhooshIndexingDaemon(index_location=index_location,
557 WhooshIndexingDaemon(index_location=index_location,
555 repo_location=repo_location)\
558 repo_location=repo_location)\
556 .run(full_index=full_index)
559 .run(full_index=full_index)
557 l.release()
560 l.release()
558 except LockHeld:
561 except LockHeld:
559 pass
562 pass
560
563
561 def create_test_env(repos_test_path, config):
564 def create_test_env(repos_test_path, config):
562 """Makes a fresh database and
565 """Makes a fresh database and
563 install test repository into tmp dir
566 install test repository into tmp dir
564 """
567 """
565 from rhodecode.lib.db_manage import DbManage
568 from rhodecode.lib.db_manage import DbManage
566 from rhodecode.tests import HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, \
569 from rhodecode.tests import HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, \
567 HG_FORK, GIT_FORK, TESTS_TMP_PATH
570 HG_FORK, GIT_FORK, TESTS_TMP_PATH
568 import tarfile
571 import tarfile
569 import shutil
572 import shutil
570 from os.path import dirname as dn, join as jn, abspath
573 from os.path import dirname as dn, join as jn, abspath
571
574
572 log = logging.getLogger('TestEnvCreator')
575 log = logging.getLogger('TestEnvCreator')
573 # create logger
576 # create logger
574 log.setLevel(logging.DEBUG)
577 log.setLevel(logging.DEBUG)
575 log.propagate = True
578 log.propagate = True
576 # create console handler and set level to debug
579 # create console handler and set level to debug
577 ch = logging.StreamHandler()
580 ch = logging.StreamHandler()
578 ch.setLevel(logging.DEBUG)
581 ch.setLevel(logging.DEBUG)
579
582
580 # create formatter
583 # create formatter
581 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
584 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
582
585
583 # add formatter to ch
586 # add formatter to ch
584 ch.setFormatter(formatter)
587 ch.setFormatter(formatter)
585
588
586 # add ch to logger
589 # add ch to logger
587 log.addHandler(ch)
590 log.addHandler(ch)
588
591
589 #PART ONE create db
592 #PART ONE create db
590 dbconf = config['sqlalchemy.db1.url']
593 dbconf = config['sqlalchemy.db1.url']
591 log.debug('making test db %s', dbconf)
594 log.debug('making test db %s', dbconf)
592
595
593 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
596 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
594 tests=True)
597 tests=True)
595 dbmanage.create_tables(override=True)
598 dbmanage.create_tables(override=True)
596 dbmanage.config_prompt(repos_test_path)
599 dbmanage.config_prompt(repos_test_path)
597 dbmanage.create_default_user()
600 dbmanage.create_default_user()
598 dbmanage.admin_prompt()
601 dbmanage.admin_prompt()
599 dbmanage.create_permissions()
602 dbmanage.create_permissions()
600 dbmanage.populate_default_permissions()
603 dbmanage.populate_default_permissions()
601
604
602 #PART TWO make test repo
605 #PART TWO make test repo
603 log.debug('making test vcs repositories')
606 log.debug('making test vcs repositories')
604
607
605 #remove old one from previos tests
608 #remove old one from previos tests
606 for r in [HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, HG_FORK, GIT_FORK]:
609 for r in [HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, HG_FORK, GIT_FORK]:
607
610
608 if os.path.isdir(jn(TESTS_TMP_PATH, r)):
611 if os.path.isdir(jn(TESTS_TMP_PATH, r)):
609 log.debug('removing %s', r)
612 log.debug('removing %s', r)
610 shutil.rmtree(jn(TESTS_TMP_PATH, r))
613 shutil.rmtree(jn(TESTS_TMP_PATH, r))
611
614
612 #CREATE DEFAULT HG REPOSITORY
615 #CREATE DEFAULT HG REPOSITORY
613 cur_dir = dn(dn(abspath(__file__)))
616 cur_dir = dn(dn(abspath(__file__)))
614 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
617 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
615 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
618 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
616 tar.close()
619 tar.close()
617
620
618
621
619 #==============================================================================
622 #==============================================================================
620 # PASTER COMMANDS
623 # PASTER COMMANDS
621 #==============================================================================
624 #==============================================================================
622
625
623 class BasePasterCommand(Command):
626 class BasePasterCommand(Command):
624 """
627 """
625 Abstract Base Class for paster commands.
628 Abstract Base Class for paster commands.
626
629
627 The celery commands are somewhat aggressive about loading
630 The celery commands are somewhat aggressive about loading
628 celery.conf, and since our module sets the `CELERY_LOADER`
631 celery.conf, and since our module sets the `CELERY_LOADER`
629 environment variable to our loader, we have to bootstrap a bit and
632 environment variable to our loader, we have to bootstrap a bit and
630 make sure we've had a chance to load the pylons config off of the
633 make sure we've had a chance to load the pylons config off of the
631 command line, otherwise everything fails.
634 command line, otherwise everything fails.
632 """
635 """
633 min_args = 1
636 min_args = 1
634 min_args_error = "Please provide a paster config file as an argument."
637 min_args_error = "Please provide a paster config file as an argument."
635 takes_config_file = 1
638 takes_config_file = 1
636 requires_config_file = True
639 requires_config_file = True
637
640
638 def notify_msg(self, msg, log=False):
641 def notify_msg(self, msg, log=False):
639 """Make a notification to user, additionally if logger is passed
642 """Make a notification to user, additionally if logger is passed
640 it logs this action using given logger
643 it logs this action using given logger
641
644
642 :param msg: message that will be printed to user
645 :param msg: message that will be printed to user
643 :param log: logging instance, to use to additionally log this message
646 :param log: logging instance, to use to additionally log this message
644
647
645 """
648 """
646 if log and isinstance(log, logging):
649 if log and isinstance(log, logging):
647 log(msg)
650 log(msg)
648
651
649
652
650 def run(self, args):
653 def run(self, args):
651 """
654 """
652 Overrides Command.run
655 Overrides Command.run
653
656
654 Checks for a config file argument and loads it.
657 Checks for a config file argument and loads it.
655 """
658 """
656 if len(args) < self.min_args:
659 if len(args) < self.min_args:
657 raise BadCommand(
660 raise BadCommand(
658 self.min_args_error % {'min_args': self.min_args,
661 self.min_args_error % {'min_args': self.min_args,
659 'actual_args': len(args)})
662 'actual_args': len(args)})
660
663
661 # Decrement because we're going to lob off the first argument.
664 # Decrement because we're going to lob off the first argument.
662 # @@ This is hacky
665 # @@ This is hacky
663 self.min_args -= 1
666 self.min_args -= 1
664 self.bootstrap_config(args[0])
667 self.bootstrap_config(args[0])
665 self.update_parser()
668 self.update_parser()
666 return super(BasePasterCommand, self).run(args[1:])
669 return super(BasePasterCommand, self).run(args[1:])
667
670
668 def update_parser(self):
671 def update_parser(self):
669 """
672 """
670 Abstract method. Allows for the class's parser to be updated
673 Abstract method. Allows for the class's parser to be updated
671 before the superclass's `run` method is called. Necessary to
674 before the superclass's `run` method is called. Necessary to
672 allow options/arguments to be passed through to the underlying
675 allow options/arguments to be passed through to the underlying
673 celery command.
676 celery command.
674 """
677 """
675 raise NotImplementedError("Abstract Method.")
678 raise NotImplementedError("Abstract Method.")
676
679
677 def bootstrap_config(self, conf):
680 def bootstrap_config(self, conf):
678 """
681 """
679 Loads the pylons configuration.
682 Loads the pylons configuration.
680 """
683 """
681 from pylons import config as pylonsconfig
684 from pylons import config as pylonsconfig
682
685
683 path_to_ini_file = os.path.realpath(conf)
686 path_to_ini_file = os.path.realpath(conf)
684 conf = paste.deploy.appconfig('config:' + path_to_ini_file)
687 conf = paste.deploy.appconfig('config:' + path_to_ini_file)
685 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
688 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
@@ -1,346 +1,347 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) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
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, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27 import logging
27 import logging
28 import datetime
28 import datetime
29 from datetime import date
29 from datetime import date
30
30
31 from sqlalchemy import *
31 from sqlalchemy import *
32 from sqlalchemy.exc import DatabaseError
32 from sqlalchemy.exc import DatabaseError
33 from sqlalchemy.orm import relationship, backref, class_mapper
33 from sqlalchemy.orm import relationship, backref, class_mapper
34 from sqlalchemy.orm.session import Session
34 from sqlalchemy.orm.session import Session
35
35
36 from rhodecode.model.meta import Base
36 from rhodecode.model.meta import Base
37
37
38 log = logging.getLogger(__name__)
38 log = logging.getLogger(__name__)
39
39
40 class BaseModel(object):
40 class BaseModel(object):
41
41
42 @classmethod
42 @classmethod
43 def _get_keys(cls):
43 def _get_keys(cls):
44 """return column names for this model """
44 """return column names for this model """
45 return class_mapper(cls).c.keys()
45 return class_mapper(cls).c.keys()
46
46
47 def get_dict(self):
47 def get_dict(self):
48 """return dict with keys and values corresponding
48 """return dict with keys and values corresponding
49 to this model data """
49 to this model data """
50
50
51 d = {}
51 d = {}
52 for k in self._get_keys():
52 for k in self._get_keys():
53 d[k] = getattr(self, k)
53 d[k] = getattr(self, k)
54 return d
54 return d
55
55
56 def get_appstruct(self):
56 def get_appstruct(self):
57 """return list with keys and values tupples corresponding
57 """return list with keys and values tupples corresponding
58 to this model data """
58 to this model data """
59
59
60 l = []
60 l = []
61 for k in self._get_keys():
61 for k in self._get_keys():
62 l.append((k, getattr(self, k),))
62 l.append((k, getattr(self, k),))
63 return l
63 return l
64
64
65 def populate_obj(self, populate_dict):
65 def populate_obj(self, populate_dict):
66 """populate model with data from given populate_dict"""
66 """populate model with data from given populate_dict"""
67
67
68 for k in self._get_keys():
68 for k in self._get_keys():
69 if k in populate_dict:
69 if k in populate_dict:
70 setattr(self, k, populate_dict[k])
70 setattr(self, k, populate_dict[k])
71
71
72 class RhodeCodeSettings(Base, BaseModel):
72 class RhodeCodeSettings(Base, BaseModel):
73 __tablename__ = 'rhodecode_settings'
73 __tablename__ = 'rhodecode_settings'
74 __table_args__ = (UniqueConstraint('app_settings_name'), {'useexisting':True})
74 __table_args__ = (UniqueConstraint('app_settings_name'), {'useexisting':True})
75 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
75 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
76 app_settings_name = Column("app_settings_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
76 app_settings_name = Column("app_settings_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
77 app_settings_value = Column("app_settings_value", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
77 app_settings_value = Column("app_settings_value", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
78
78
79 def __init__(self, k='', v=''):
79 def __init__(self, k='', v=''):
80 self.app_settings_name = k
80 self.app_settings_name = k
81 self.app_settings_value = v
81 self.app_settings_value = v
82
82
83 def __repr__(self):
83 def __repr__(self):
84 return "<%s('%s:%s')>" % (self.__class__.__name__,
84 return "<%s('%s:%s')>" % (self.__class__.__name__,
85 self.app_settings_name, self.app_settings_value)
85 self.app_settings_name, self.app_settings_value)
86
86
87 class RhodeCodeUi(Base, BaseModel):
87 class RhodeCodeUi(Base, BaseModel):
88 __tablename__ = 'rhodecode_ui'
88 __tablename__ = 'rhodecode_ui'
89 __table_args__ = {'useexisting':True}
89 __table_args__ = {'useexisting':True}
90 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
90 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
91 ui_section = Column("ui_section", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
91 ui_section = Column("ui_section", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
92 ui_key = Column("ui_key", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
92 ui_key = Column("ui_key", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
93 ui_value = Column("ui_value", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
93 ui_value = Column("ui_value", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
94 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
94 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
95
95
96
96
97 class User(Base, BaseModel):
97 class User(Base, BaseModel):
98 __tablename__ = 'users'
98 __tablename__ = 'users'
99 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'useexisting':True})
99 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'useexisting':True})
100 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
100 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
101 username = Column("username", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
101 username = Column("username", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
102 password = Column("password", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
102 password = Column("password", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
103 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
103 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
104 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
104 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
105 name = Column("name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
105 name = Column("name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
106 lastname = Column("lastname", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
106 lastname = Column("lastname", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
107 email = Column("email", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
107 email = Column("email", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
108 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
108 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
109 ldap_dn = Column("ldap_dn", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
109 ldap_dn = Column("ldap_dn", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
110
110
111 user_log = relationship('UserLog', cascade='all')
111 user_log = relationship('UserLog', cascade='all')
112 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
112 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
113
113
114 repositories = relationship('Repository')
114 repositories = relationship('Repository')
115 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
115 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
116
116
117 group_member = relationship('UsersGroupMember', cascade='all')
117 @property
118 @property
118 def full_contact(self):
119 def full_contact(self):
119 return '%s %s <%s>' % (self.name, self.lastname, self.email)
120 return '%s %s <%s>' % (self.name, self.lastname, self.email)
120
121
121
122
122 @property
123 @property
123 def is_admin(self):
124 def is_admin(self):
124 return self.admin
125 return self.admin
125
126
126 def __repr__(self):
127 def __repr__(self):
127 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
128 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
128 self.user_id, self.username)
129 self.user_id, self.username)
129
130
130 def update_lastlogin(self):
131 def update_lastlogin(self):
131 """Update user lastlogin"""
132 """Update user lastlogin"""
132
133
133 try:
134 try:
134 session = Session.object_session(self)
135 session = Session.object_session(self)
135 self.last_login = datetime.datetime.now()
136 self.last_login = datetime.datetime.now()
136 session.add(self)
137 session.add(self)
137 session.commit()
138 session.commit()
138 log.debug('updated user %s lastlogin', self.username)
139 log.debug('updated user %s lastlogin', self.username)
139 except (DatabaseError,):
140 except (DatabaseError,):
140 session.rollback()
141 session.rollback()
141
142
142
143
143 class UserLog(Base, BaseModel):
144 class UserLog(Base, BaseModel):
144 __tablename__ = 'user_logs'
145 __tablename__ = 'user_logs'
145 __table_args__ = {'useexisting':True}
146 __table_args__ = {'useexisting':True}
146 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
147 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
147 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
148 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
148 repository_id = Column("repository_id", Integer(length=None, convert_unicode=False, assert_unicode=None), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
149 repository_id = Column("repository_id", Integer(length=None, convert_unicode=False, assert_unicode=None), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
149 repository_name = Column("repository_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
150 repository_name = Column("repository_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
150 user_ip = Column("user_ip", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
151 user_ip = Column("user_ip", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
151 action = Column("action", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
152 action = Column("action", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
152 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
153 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
153
154
154 @property
155 @property
155 def action_as_day(self):
156 def action_as_day(self):
156 return date(*self.action_date.timetuple()[:3])
157 return date(*self.action_date.timetuple()[:3])
157
158
158 user = relationship('User')
159 user = relationship('User')
159 repository = relationship('Repository')
160 repository = relationship('Repository')
160
161
161
162
162 class UsersGroup(Base, BaseModel):
163 class UsersGroup(Base, BaseModel):
163 __tablename__ = 'users_groups'
164 __tablename__ = 'users_groups'
164 __table_args__ = {'useexisting':True}
165 __table_args__ = {'useexisting':True}
165
166
166 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
167 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
167 users_group_name = Column("users_group_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
168 users_group_name = Column("users_group_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
168 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
169 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
169
170
170 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
171 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
171
172
172 class UsersGroupMember(Base, BaseModel):
173 class UsersGroupMember(Base, BaseModel):
173 __tablename__ = 'users_groups_members'
174 __tablename__ = 'users_groups_members'
174 __table_args__ = {'useexisting':True}
175 __table_args__ = {'useexisting':True}
175
176
176 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
177 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
177 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
178 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
178 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
179 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
179
180
180 user = relationship('User', lazy='joined')
181 user = relationship('User', lazy='joined')
181 users_group = relationship('UsersGroup')
182 users_group = relationship('UsersGroup')
182
183
183 def __init__(self, gr_id='', u_id=''):
184 def __init__(self, gr_id='', u_id=''):
184 self.users_group_id = gr_id
185 self.users_group_id = gr_id
185 self.user_id = u_id
186 self.user_id = u_id
186
187
187 class Repository(Base, BaseModel):
188 class Repository(Base, BaseModel):
188 __tablename__ = 'repositories'
189 __tablename__ = 'repositories'
189 __table_args__ = (UniqueConstraint('repo_name'), {'useexisting':True},)
190 __table_args__ = (UniqueConstraint('repo_name'), {'useexisting':True},)
190 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
191 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
191 repo_name = Column("repo_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
192 repo_name = Column("repo_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
192 repo_type = Column("repo_type", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
193 repo_type = Column("repo_type", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
193 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
194 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
194 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
195 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
195 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
196 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
196 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
197 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
197 description = Column("description", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
198 description = Column("description", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
198 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
199 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
199 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
200 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
200
201
201 user = relationship('User')
202 user = relationship('User')
202 fork = relationship('Repository', remote_side=repo_id)
203 fork = relationship('Repository', remote_side=repo_id)
203 group = relationship('Group')
204 group = relationship('Group')
204 repo_to_perm = relationship('RepoToPerm', cascade='all', order_by='RepoToPerm.repo_to_perm_id')
205 repo_to_perm = relationship('RepoToPerm', cascade='all', order_by='RepoToPerm.repo_to_perm_id')
205 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
206 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
206 stats = relationship('Statistics', cascade='all', uselist=False)
207 stats = relationship('Statistics', cascade='all', uselist=False)
207
208
208 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
209 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
209
210
210 logs = relationship('UserLog', cascade='all')
211 logs = relationship('UserLog', cascade='all')
211
212
212 def __repr__(self):
213 def __repr__(self):
213 return "<%s('%s:%s')>" % (self.__class__.__name__,
214 return "<%s('%s:%s')>" % (self.__class__.__name__,
214 self.repo_id, self.repo_name)
215 self.repo_id, self.repo_name)
215
216
216 class Group(Base, BaseModel):
217 class Group(Base, BaseModel):
217 __tablename__ = 'groups'
218 __tablename__ = 'groups'
218 __table_args__ = (UniqueConstraint('group_name'), {'useexisting':True},)
219 __table_args__ = (UniqueConstraint('group_name'), {'useexisting':True},)
219
220
220 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
221 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
221 group_name = Column("group_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
222 group_name = Column("group_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
222 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
223 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
223
224
224 parent_group = relationship('Group', remote_side=group_id)
225 parent_group = relationship('Group', remote_side=group_id)
225
226
226
227
227 def __init__(self, group_name='', parent_group=None):
228 def __init__(self, group_name='', parent_group=None):
228 self.group_name = group_name
229 self.group_name = group_name
229 self.parent_group = parent_group
230 self.parent_group = parent_group
230
231
231 def __repr__(self):
232 def __repr__(self):
232 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
233 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
233 self.group_name)
234 self.group_name)
234
235
235 class Permission(Base, BaseModel):
236 class Permission(Base, BaseModel):
236 __tablename__ = 'permissions'
237 __tablename__ = 'permissions'
237 __table_args__ = {'useexisting':True}
238 __table_args__ = {'useexisting':True}
238 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
239 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
239 permission_name = Column("permission_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
240 permission_name = Column("permission_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
240 permission_longname = Column("permission_longname", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
241 permission_longname = Column("permission_longname", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
241
242
242 def __repr__(self):
243 def __repr__(self):
243 return "<%s('%s:%s')>" % (self.__class__.__name__,
244 return "<%s('%s:%s')>" % (self.__class__.__name__,
244 self.permission_id, self.permission_name)
245 self.permission_id, self.permission_name)
245
246
246 class RepoToPerm(Base, BaseModel):
247 class RepoToPerm(Base, BaseModel):
247 __tablename__ = 'repo_to_perm'
248 __tablename__ = 'repo_to_perm'
248 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'useexisting':True})
249 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'useexisting':True})
249 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
250 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
250 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
251 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
251 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
252 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
252 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
253 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
253
254
254 user = relationship('User')
255 user = relationship('User')
255 permission = relationship('Permission')
256 permission = relationship('Permission')
256 repository = relationship('Repository')
257 repository = relationship('Repository')
257
258
258 class UserToPerm(Base, BaseModel):
259 class UserToPerm(Base, BaseModel):
259 __tablename__ = 'user_to_perm'
260 __tablename__ = 'user_to_perm'
260 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'useexisting':True})
261 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'useexisting':True})
261 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
262 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
262 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
263 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
263 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
264 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
264
265
265 user = relationship('User')
266 user = relationship('User')
266 permission = relationship('Permission')
267 permission = relationship('Permission')
267
268
268
269
269 class UsersGroupToPerm(Base, BaseModel):
270 class UsersGroupToPerm(Base, BaseModel):
270 __tablename__ = 'users_group_to_perm'
271 __tablename__ = 'users_group_to_perm'
271 __table_args__ = (UniqueConstraint('users_group_id', 'permission_id'), {'useexisting':True})
272 __table_args__ = (UniqueConstraint('users_group_id', 'permission_id'), {'useexisting':True})
272 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
273 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
273 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
274 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
274 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
275 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
275 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
276 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
276
277
277 users_group = relationship('UsersGroup')
278 users_group = relationship('UsersGroup')
278 permission = relationship('Permission')
279 permission = relationship('Permission')
279 repository = relationship('Repository')
280 repository = relationship('Repository')
280
281
281 class GroupToPerm(Base, BaseModel):
282 class GroupToPerm(Base, BaseModel):
282 __tablename__ = 'group_to_perm'
283 __tablename__ = 'group_to_perm'
283 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'useexisting':True})
284 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'useexisting':True})
284
285
285 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
286 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
286 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
287 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
287 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
288 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
288 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
289 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
289
290
290 user = relationship('User')
291 user = relationship('User')
291 permission = relationship('Permission')
292 permission = relationship('Permission')
292 group = relationship('Group')
293 group = relationship('Group')
293
294
294 class Statistics(Base, BaseModel):
295 class Statistics(Base, BaseModel):
295 __tablename__ = 'statistics'
296 __tablename__ = 'statistics'
296 __table_args__ = (UniqueConstraint('repository_id'), {'useexisting':True})
297 __table_args__ = (UniqueConstraint('repository_id'), {'useexisting':True})
297 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
298 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
298 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
299 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
299 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
300 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
300 commit_activity = Column("commit_activity", LargeBinary(), nullable=False)#JSON data
301 commit_activity = Column("commit_activity", LargeBinary(), nullable=False)#JSON data
301 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
302 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
302 languages = Column("languages", LargeBinary(), nullable=False)#JSON data
303 languages = Column("languages", LargeBinary(), nullable=False)#JSON data
303
304
304 repository = relationship('Repository', single_parent=True)
305 repository = relationship('Repository', single_parent=True)
305
306
306 class UserFollowing(Base, BaseModel):
307 class UserFollowing(Base, BaseModel):
307 __tablename__ = 'user_followings'
308 __tablename__ = 'user_followings'
308 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
309 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
309 UniqueConstraint('user_id', 'follows_user_id')
310 UniqueConstraint('user_id', 'follows_user_id')
310 , {'useexisting':True})
311 , {'useexisting':True})
311
312
312 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
313 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
313 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
314 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
314 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
315 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
315 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
316 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
316
317
317 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
318 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
318
319
319 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
320 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
320 follows_repository = relationship('Repository', order_by='Repository.repo_name')
321 follows_repository = relationship('Repository', order_by='Repository.repo_name')
321
322
322 class CacheInvalidation(Base, BaseModel):
323 class CacheInvalidation(Base, BaseModel):
323 __tablename__ = 'cache_invalidation'
324 __tablename__ = 'cache_invalidation'
324 __table_args__ = (UniqueConstraint('cache_key'), {'useexisting':True})
325 __table_args__ = (UniqueConstraint('cache_key'), {'useexisting':True})
325 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
326 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
326 cache_key = Column("cache_key", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
327 cache_key = Column("cache_key", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
327 cache_args = Column("cache_args", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
328 cache_args = Column("cache_args", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
328 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
329 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
329
330
330
331
331 def __init__(self, cache_key, cache_args=''):
332 def __init__(self, cache_key, cache_args=''):
332 self.cache_key = cache_key
333 self.cache_key = cache_key
333 self.cache_args = cache_args
334 self.cache_args = cache_args
334 self.cache_active = False
335 self.cache_active = False
335
336
336 def __repr__(self):
337 def __repr__(self):
337 return "<%s('%s:%s')>" % (self.__class__.__name__,
338 return "<%s('%s:%s')>" % (self.__class__.__name__,
338 self.cache_id, self.cache_key)
339 self.cache_id, self.cache_key)
339
340
340 class DbMigrateVersion(Base, BaseModel):
341 class DbMigrateVersion(Base, BaseModel):
341 __tablename__ = 'db_migrate_version'
342 __tablename__ = 'db_migrate_version'
342 __table_args__ = {'useexisting':True}
343 __table_args__ = {'useexisting':True}
343 repository_id = Column('repository_id', String(250), primary_key=True)
344 repository_id = Column('repository_id', String(250), primary_key=True)
344 repository_path = Column('repository_path', Text)
345 repository_path = Column('repository_path', Text)
345 version = Column('version', Integer)
346 version = Column('version', Integer)
346
347
@@ -1,342 +1,344 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.repo
3 rhodecode.model.repo
4 ~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~
5
5
6 Repository model for rhodecode
6 Repository model for rhodecode
7
7
8 :created_on: Jun 5, 2010
8 :created_on: Jun 5, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
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, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27 import os
27 import os
28 import shutil
28 import shutil
29 import logging
29 import logging
30 import traceback
30 import traceback
31 from datetime import datetime
31 from datetime import datetime
32
32
33 from sqlalchemy.orm import joinedload
33 from sqlalchemy.orm import joinedload
34
34
35 from vcs.utils.lazy import LazyProperty
35 from vcs.utils.lazy import LazyProperty
36 from vcs.backends import get_backend
36 from vcs.backends import get_backend
37
37
38 from rhodecode.model import BaseModel
38 from rhodecode.model import BaseModel
39 from rhodecode.model.caching_query import FromCache
39 from rhodecode.model.caching_query import FromCache
40 from rhodecode.model.db import Repository, RepoToPerm, User, Permission, \
40 from rhodecode.model.db import Repository, RepoToPerm, User, Permission, \
41 Statistics, UsersGroup, UsersGroupToPerm, RhodeCodeUi
41 Statistics, UsersGroup, UsersGroupToPerm, RhodeCodeUi
42 from rhodecode.model.user import UserModel
42 from rhodecode.model.user import UserModel
43 from rhodecode.model.users_group import UsersGroupMember, UsersGroupModel
43 from rhodecode.model.users_group import UsersGroupMember, UsersGroupModel
44
44
45
45
46 log = logging.getLogger(__name__)
46 log = logging.getLogger(__name__)
47
47
48 class RepoModel(BaseModel):
48 class RepoModel(BaseModel):
49
49
50 @LazyProperty
50 @LazyProperty
51 def repos_path(self):
51 def repos_path(self):
52 """Get's the repositories root path from database
52 """Get's the repositories root path from database
53 """
53 """
54
54
55 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
55 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
56 return q.ui_value
56 return q.ui_value
57
57
58 def get(self, repo_id, cache=False):
58 def get(self, repo_id, cache=False):
59 repo = self.sa.query(Repository)\
59 repo = self.sa.query(Repository)\
60 .filter(Repository.repo_id == repo_id)
60 .filter(Repository.repo_id == repo_id)
61
61
62 if cache:
62 if cache:
63 repo = repo.options(FromCache("sql_cache_short",
63 repo = repo.options(FromCache("sql_cache_short",
64 "get_repo_%s" % repo_id))
64 "get_repo_%s" % repo_id))
65 return repo.scalar()
65 return repo.scalar()
66
66
67
67
68 def get_by_repo_name(self, repo_name, cache=False):
68 def get_by_repo_name(self, repo_name, cache=False):
69 repo = self.sa.query(Repository)\
69 repo = self.sa.query(Repository)\
70 .filter(Repository.repo_name == repo_name)
70 .filter(Repository.repo_name == repo_name)
71
71
72 if cache:
72 if cache:
73 repo = repo.options(FromCache("sql_cache_short",
73 repo = repo.options(FromCache("sql_cache_short",
74 "get_repo_%s" % repo_name))
74 "get_repo_%s" % repo_name))
75 return repo.scalar()
75 return repo.scalar()
76
76
77
77
78 def get_full(self, repo_name, cache=False, invalidate=False):
78 def get_full(self, repo_name, cache=False, invalidate=False):
79 repo = self.sa.query(Repository)\
79 repo = self.sa.query(Repository)\
80 .options(joinedload(Repository.fork))\
80 .options(joinedload(Repository.fork))\
81 .options(joinedload(Repository.user))\
81 .options(joinedload(Repository.user))\
82 .options(joinedload(Repository.followers))\
82 .options(joinedload(Repository.followers))\
83 .options(joinedload(Repository.repo_to_perm))\
83 .options(joinedload(Repository.repo_to_perm))\
84 .options(joinedload(Repository.users_group_to_perm))\
84 .options(joinedload(Repository.users_group_to_perm))\
85 .filter(Repository.repo_name == repo_name)\
85 .filter(Repository.repo_name == repo_name)\
86
86
87 if cache:
87 if cache:
88 repo = repo.options(FromCache("sql_cache_long",
88 repo = repo.options(FromCache("sql_cache_long",
89 "get_repo_full_%s" % repo_name))
89 "get_repo_full_%s" % repo_name))
90 if invalidate and cache:
90 if invalidate and cache:
91 repo.invalidate()
91 repo.invalidate()
92
92
93 return repo.scalar()
93 ret = repo.scalar()
94 self.sa.expunge_all()
95 return ret
94
96
95
97
96 def get_users_js(self):
98 def get_users_js(self):
97
99
98 users = self.sa.query(User).filter(User.active == True).all()
100 users = self.sa.query(User).filter(User.active == True).all()
99 u_tmpl = '''{id:%s, fname:"%s", lname:"%s", nname:"%s"},'''
101 u_tmpl = '''{id:%s, fname:"%s", lname:"%s", nname:"%s"},'''
100 users_array = '[%s]' % '\n'.join([u_tmpl % (u.user_id, u.name,
102 users_array = '[%s]' % '\n'.join([u_tmpl % (u.user_id, u.name,
101 u.lastname, u.username)
103 u.lastname, u.username)
102 for u in users])
104 for u in users])
103 return users_array
105 return users_array
104
106
105
107
106 def get_users_groups_js(self):
108 def get_users_groups_js(self):
107 users_groups = self.sa.query(UsersGroup)\
109 users_groups = self.sa.query(UsersGroup)\
108 .filter(UsersGroup.users_group_active == True).all()
110 .filter(UsersGroup.users_group_active == True).all()
109
111
110 g_tmpl = '''{id:%s, grname:"%s",grmembers:"%s"},'''
112 g_tmpl = '''{id:%s, grname:"%s",grmembers:"%s"},'''
111
113
112 users_groups_array = '[%s]' % '\n'.join([g_tmpl % \
114 users_groups_array = '[%s]' % '\n'.join([g_tmpl % \
113 (gr.users_group_id, gr.users_group_name,
115 (gr.users_group_id, gr.users_group_name,
114 len(gr.members))
116 len(gr.members))
115 for gr in users_groups])
117 for gr in users_groups])
116 return users_groups_array
118 return users_groups_array
117
119
118 def update(self, repo_name, form_data):
120 def update(self, repo_name, form_data):
119 try:
121 try:
120 cur_repo = self.get_by_repo_name(repo_name, cache=False)
122 cur_repo = self.get_by_repo_name(repo_name, cache=False)
121 user_model = UserModel(self.sa)
123 user_model = UserModel(self.sa)
122 users_group_model = UsersGroupModel(self.sa)
124 users_group_model = UsersGroupModel(self.sa)
123
125
124 #update permissions
126 #update permissions
125 for member, perm, member_type in form_data['perms_updates']:
127 for member, perm, member_type in form_data['perms_updates']:
126 if member_type == 'user':
128 if member_type == 'user':
127 r2p = self.sa.query(RepoToPerm)\
129 r2p = self.sa.query(RepoToPerm)\
128 .filter(RepoToPerm.user == user_model.get_by_username(member))\
130 .filter(RepoToPerm.user == user_model.get_by_username(member))\
129 .filter(RepoToPerm.repository == cur_repo)\
131 .filter(RepoToPerm.repository == cur_repo)\
130 .one()
132 .one()
131
133
132 r2p.permission = self.sa.query(Permission)\
134 r2p.permission = self.sa.query(Permission)\
133 .filter(Permission.permission_name == perm)\
135 .filter(Permission.permission_name == perm)\
134 .scalar()
136 .scalar()
135 self.sa.add(r2p)
137 self.sa.add(r2p)
136 else:
138 else:
137 g2p = self.sa.query(UsersGroupToPerm)\
139 g2p = self.sa.query(UsersGroupToPerm)\
138 .filter(UsersGroupToPerm.users_group == users_group_model.get_by_groupname(member))\
140 .filter(UsersGroupToPerm.users_group == users_group_model.get_by_groupname(member))\
139 .filter(UsersGroupToPerm.repository == cur_repo)\
141 .filter(UsersGroupToPerm.repository == cur_repo)\
140 .one()
142 .one()
141
143
142 g2p.permission = self.sa.query(Permission)\
144 g2p.permission = self.sa.query(Permission)\
143 .filter(Permission.permission_name == perm)\
145 .filter(Permission.permission_name == perm)\
144 .scalar()
146 .scalar()
145 self.sa.add(g2p)
147 self.sa.add(g2p)
146
148
147 #set new permissions
149 #set new permissions
148 for member, perm, member_type in form_data['perms_new']:
150 for member, perm, member_type in form_data['perms_new']:
149 if member_type == 'user':
151 if member_type == 'user':
150 r2p = RepoToPerm()
152 r2p = RepoToPerm()
151 r2p.repository = cur_repo
153 r2p.repository = cur_repo
152 r2p.user = user_model.get_by_username(member)
154 r2p.user = user_model.get_by_username(member)
153
155
154 r2p.permission = self.sa.query(Permission)\
156 r2p.permission = self.sa.query(Permission)\
155 .filter(Permission.permission_name == perm)\
157 .filter(Permission.permission_name == perm)\
156 .scalar()
158 .scalar()
157 self.sa.add(r2p)
159 self.sa.add(r2p)
158 else:
160 else:
159 g2p = UsersGroupToPerm()
161 g2p = UsersGroupToPerm()
160 g2p.repository = cur_repo
162 g2p.repository = cur_repo
161 g2p.users_group = users_group_model.get_by_groupname(member)
163 g2p.users_group = users_group_model.get_by_groupname(member)
162
164
163 g2p.permission = self.sa.query(Permission)\
165 g2p.permission = self.sa.query(Permission)\
164 .filter(Permission.permission_name == perm)\
166 .filter(Permission.permission_name == perm)\
165 .scalar()
167 .scalar()
166 self.sa.add(g2p)
168 self.sa.add(g2p)
167
169
168 #update current repo
170 #update current repo
169 for k, v in form_data.items():
171 for k, v in form_data.items():
170 if k == 'user':
172 if k == 'user':
171 cur_repo.user = user_model.get(v)
173 cur_repo.user = user_model.get(v)
172 else:
174 else:
173 setattr(cur_repo, k, v)
175 setattr(cur_repo, k, v)
174
176
175 self.sa.add(cur_repo)
177 self.sa.add(cur_repo)
176
178
177 if repo_name != form_data['repo_name']:
179 if repo_name != form_data['repo_name']:
178 #rename our data
180 #rename our data
179 self.__rename_repo(repo_name, form_data['repo_name'])
181 self.__rename_repo(repo_name, form_data['repo_name'])
180
182
181 self.sa.commit()
183 self.sa.commit()
182 except:
184 except:
183 log.error(traceback.format_exc())
185 log.error(traceback.format_exc())
184 self.sa.rollback()
186 self.sa.rollback()
185 raise
187 raise
186
188
187 def create(self, form_data, cur_user, just_db=False, fork=False):
189 def create(self, form_data, cur_user, just_db=False, fork=False):
188 try:
190 try:
189 if fork:
191 if fork:
190 #force str since hg doesn't go with unicode
192 #force str since hg doesn't go with unicode
191 repo_name = str(form_data['fork_name'])
193 repo_name = str(form_data['fork_name'])
192 org_name = str(form_data['repo_name'])
194 org_name = str(form_data['repo_name'])
193
195
194 else:
196 else:
195 org_name = repo_name = str(form_data['repo_name'])
197 org_name = repo_name = str(form_data['repo_name'])
196 new_repo = Repository()
198 new_repo = Repository()
197 new_repo.enable_statistics = True
199 new_repo.enable_statistics = True
198 for k, v in form_data.items():
200 for k, v in form_data.items():
199 if k == 'repo_name':
201 if k == 'repo_name':
200 v = repo_name
202 v = repo_name
201 setattr(new_repo, k, v)
203 setattr(new_repo, k, v)
202
204
203 if fork:
205 if fork:
204 parent_repo = self.sa.query(Repository)\
206 parent_repo = self.sa.query(Repository)\
205 .filter(Repository.repo_name == org_name).scalar()
207 .filter(Repository.repo_name == org_name).scalar()
206 new_repo.fork = parent_repo
208 new_repo.fork = parent_repo
207
209
208 new_repo.user_id = cur_user.user_id
210 new_repo.user_id = cur_user.user_id
209 self.sa.add(new_repo)
211 self.sa.add(new_repo)
210
212
211 #create default permission
213 #create default permission
212 repo_to_perm = RepoToPerm()
214 repo_to_perm = RepoToPerm()
213 default = 'repository.read'
215 default = 'repository.read'
214 for p in UserModel(self.sa).get_by_username('default', cache=False).user_perms:
216 for p in UserModel(self.sa).get_by_username('default', cache=False).user_perms:
215 if p.permission.permission_name.startswith('repository.'):
217 if p.permission.permission_name.startswith('repository.'):
216 default = p.permission.permission_name
218 default = p.permission.permission_name
217 break
219 break
218
220
219 default_perm = 'repository.none' if form_data['private'] else default
221 default_perm = 'repository.none' if form_data['private'] else default
220
222
221 repo_to_perm.permission_id = self.sa.query(Permission)\
223 repo_to_perm.permission_id = self.sa.query(Permission)\
222 .filter(Permission.permission_name == default_perm)\
224 .filter(Permission.permission_name == default_perm)\
223 .one().permission_id
225 .one().permission_id
224
226
225 repo_to_perm.repository = new_repo
227 repo_to_perm.repository = new_repo
226 repo_to_perm.user_id = UserModel(self.sa)\
228 repo_to_perm.user_id = UserModel(self.sa)\
227 .get_by_username('default', cache=False).user_id
229 .get_by_username('default', cache=False).user_id
228
230
229 self.sa.add(repo_to_perm)
231 self.sa.add(repo_to_perm)
230
232
231 if not just_db:
233 if not just_db:
232 self.__create_repo(repo_name, form_data['repo_type'])
234 self.__create_repo(repo_name, form_data['repo_type'])
233
235
234 self.sa.commit()
236 self.sa.commit()
235
237
236 #now automatically start following this repository as owner
238 #now automatically start following this repository as owner
237 from rhodecode.model.scm import ScmModel
239 from rhodecode.model.scm import ScmModel
238 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
240 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
239 cur_user.user_id)
241 cur_user.user_id)
240
242
241 except:
243 except:
242 log.error(traceback.format_exc())
244 log.error(traceback.format_exc())
243 self.sa.rollback()
245 self.sa.rollback()
244 raise
246 raise
245
247
246 def create_fork(self, form_data, cur_user):
248 def create_fork(self, form_data, cur_user):
247 from rhodecode.lib.celerylib import tasks, run_task
249 from rhodecode.lib.celerylib import tasks, run_task
248 run_task(tasks.create_repo_fork, form_data, cur_user)
250 run_task(tasks.create_repo_fork, form_data, cur_user)
249
251
250 def delete(self, repo):
252 def delete(self, repo):
251 try:
253 try:
252 self.sa.delete(repo)
254 self.sa.delete(repo)
253 self.__delete_repo(repo)
255 self.__delete_repo(repo)
254 self.sa.commit()
256 self.sa.commit()
255 except:
257 except:
256 log.error(traceback.format_exc())
258 log.error(traceback.format_exc())
257 self.sa.rollback()
259 self.sa.rollback()
258 raise
260 raise
259
261
260 def delete_perm_user(self, form_data, repo_name):
262 def delete_perm_user(self, form_data, repo_name):
261 try:
263 try:
262 self.sa.query(RepoToPerm)\
264 self.sa.query(RepoToPerm)\
263 .filter(RepoToPerm.repository \
265 .filter(RepoToPerm.repository \
264 == self.get_by_repo_name(repo_name))\
266 == self.get_by_repo_name(repo_name))\
265 .filter(RepoToPerm.user_id == form_data['user_id']).delete()
267 .filter(RepoToPerm.user_id == form_data['user_id']).delete()
266 self.sa.commit()
268 self.sa.commit()
267 except:
269 except:
268 log.error(traceback.format_exc())
270 log.error(traceback.format_exc())
269 self.sa.rollback()
271 self.sa.rollback()
270 raise
272 raise
271
273
272 def delete_perm_users_group(self, form_data, repo_name):
274 def delete_perm_users_group(self, form_data, repo_name):
273 try:
275 try:
274 self.sa.query(UsersGroupToPerm)\
276 self.sa.query(UsersGroupToPerm)\
275 .filter(UsersGroupToPerm.repository \
277 .filter(UsersGroupToPerm.repository \
276 == self.get_by_repo_name(repo_name))\
278 == self.get_by_repo_name(repo_name))\
277 .filter(UsersGroupToPerm.users_group_id \
279 .filter(UsersGroupToPerm.users_group_id \
278 == form_data['users_group_id']).delete()
280 == form_data['users_group_id']).delete()
279 self.sa.commit()
281 self.sa.commit()
280 except:
282 except:
281 log.error(traceback.format_exc())
283 log.error(traceback.format_exc())
282 self.sa.rollback()
284 self.sa.rollback()
283 raise
285 raise
284
286
285 def delete_stats(self, repo_name):
287 def delete_stats(self, repo_name):
286 try:
288 try:
287 self.sa.query(Statistics)\
289 self.sa.query(Statistics)\
288 .filter(Statistics.repository == \
290 .filter(Statistics.repository == \
289 self.get_by_repo_name(repo_name)).delete()
291 self.get_by_repo_name(repo_name)).delete()
290 self.sa.commit()
292 self.sa.commit()
291 except:
293 except:
292 log.error(traceback.format_exc())
294 log.error(traceback.format_exc())
293 self.sa.rollback()
295 self.sa.rollback()
294 raise
296 raise
295
297
296
298
297 def __create_repo(self, repo_name, alias):
299 def __create_repo(self, repo_name, alias):
298 """
300 """
299 makes repository on filesystem
301 makes repository on filesystem
300 :param repo_name:
302 :param repo_name:
301 :param alias:
303 :param alias:
302 """
304 """
303 from rhodecode.lib.utils import check_repo
305 from rhodecode.lib.utils import check_repo
304 repo_path = os.path.join(self.repos_path, repo_name)
306 repo_path = os.path.join(self.repos_path, repo_name)
305 if check_repo(repo_name, self.repos_path):
307 if check_repo(repo_name, self.repos_path):
306 log.info('creating repo %s in %s', repo_name, repo_path)
308 log.info('creating repo %s in %s', repo_name, repo_path)
307 backend = get_backend(alias)
309 backend = get_backend(alias)
308 backend(repo_path, create=True)
310 backend(repo_path, create=True)
309
311
310 def __rename_repo(self, old, new):
312 def __rename_repo(self, old, new):
311 """
313 """
312 renames repository on filesystem
314 renames repository on filesystem
313 :param old: old name
315 :param old: old name
314 :param new: new name
316 :param new: new name
315 """
317 """
316 log.info('renaming repo from %s to %s', old, new)
318 log.info('renaming repo from %s to %s', old, new)
317
319
318 old_path = os.path.join(self.repos_path, old)
320 old_path = os.path.join(self.repos_path, old)
319 new_path = os.path.join(self.repos_path, new)
321 new_path = os.path.join(self.repos_path, new)
320 if os.path.isdir(new_path):
322 if os.path.isdir(new_path):
321 raise Exception('Was trying to rename to already existing dir %s',
323 raise Exception('Was trying to rename to already existing dir %s',
322 new_path)
324 new_path)
323 shutil.move(old_path, new_path)
325 shutil.move(old_path, new_path)
324
326
325 def __delete_repo(self, repo):
327 def __delete_repo(self, repo):
326 """
328 """
327 removes repo from filesystem, the removal is acctually made by
329 removes repo from filesystem, the removal is acctually made by
328 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
330 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
329 repository is no longer valid for rhodecode, can be undeleted later on
331 repository is no longer valid for rhodecode, can be undeleted later on
330 by reverting the renames on this repository
332 by reverting the renames on this repository
331 :param repo: repo object
333 :param repo: repo object
332 """
334 """
333 rm_path = os.path.join(self.repos_path, repo.repo_name)
335 rm_path = os.path.join(self.repos_path, repo.repo_name)
334 log.info("Removing %s", rm_path)
336 log.info("Removing %s", rm_path)
335 #disable hg/git
337 #disable hg/git
336 alias = repo.repo_type
338 alias = repo.repo_type
337 shutil.move(os.path.join(rm_path, '.%s' % alias),
339 shutil.move(os.path.join(rm_path, '.%s' % alias),
338 os.path.join(rm_path, 'rm__.%s' % alias))
340 os.path.join(rm_path, 'rm__.%s' % alias))
339 #disable repo
341 #disable repo
340 shutil.move(rm_path, os.path.join(self.repos_path, 'rm__%s__%s' \
342 shutil.move(rm_path, os.path.join(self.repos_path, 'rm__%s__%s' \
341 % (datetime.today().isoformat(),
343 % (datetime.today().isoformat(),
342 repo.repo_name)))
344 repo.repo_name)))
@@ -1,384 +1,384 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) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
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, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27 import os
27 import os
28 import time
28 import time
29 import traceback
29 import traceback
30 import logging
30 import logging
31
31
32 from mercurial import ui
32 from mercurial import ui
33
33
34 from sqlalchemy.exc import DatabaseError
34 from sqlalchemy.exc import DatabaseError
35
35
36 from beaker.cache import cache_region, region_invalidate
36 from beaker.cache import cache_region, region_invalidate
37
37
38 from vcs import get_backend
38 from vcs import get_backend
39 from vcs.utils.helpers import get_scm
39 from vcs.utils.helpers import get_scm
40 from vcs.exceptions import RepositoryError, VCSError
40 from vcs.exceptions import RepositoryError, VCSError
41 from vcs.utils.lazy import LazyProperty
41 from vcs.utils.lazy import LazyProperty
42
42
43 from rhodecode import BACKENDS
43 from rhodecode import BACKENDS
44 from rhodecode.lib import helpers as h
44 from rhodecode.lib import helpers as h
45 from rhodecode.lib.auth import HasRepoPermissionAny
45 from rhodecode.lib.auth import HasRepoPermissionAny
46 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
46 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
47 action_logger
47 action_logger
48 from rhodecode.model import BaseModel
48 from rhodecode.model import BaseModel
49 from rhodecode.model.user import UserModel
49 from rhodecode.model.user import UserModel
50 from rhodecode.model.repo import RepoModel
50 from rhodecode.model.repo import RepoModel
51 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
51 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
52 UserFollowing, UserLog
52 UserFollowing, UserLog
53 from rhodecode.model.caching_query import FromCache
53 from rhodecode.model.caching_query import FromCache
54
54
55 log = logging.getLogger(__name__)
55 log = logging.getLogger(__name__)
56
56
57
57
58 class UserTemp(object):
58 class UserTemp(object):
59 def __init__(self, user_id):
59 def __init__(self, user_id):
60 self.user_id = user_id
60 self.user_id = user_id
61
61
62 def __repr__(self):
62 def __repr__(self):
63 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
63 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
64
64
65 class RepoTemp(object):
65 class RepoTemp(object):
66 def __init__(self, repo_id):
66 def __init__(self, repo_id):
67 self.repo_id = repo_id
67 self.repo_id = repo_id
68
68
69 def __repr__(self):
69 def __repr__(self):
70 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
70 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
71
71
72 class ScmModel(BaseModel):
72 class ScmModel(BaseModel):
73 """Generic Scm Model
73 """Generic Scm Model
74 """
74 """
75
75
76 @LazyProperty
76 @LazyProperty
77 def repos_path(self):
77 def repos_path(self):
78 """Get's the repositories root path from database
78 """Get's the repositories root path from database
79 """
79 """
80
80
81 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
81 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
82
82
83 return q.ui_value
83 return q.ui_value
84
84
85 def repo_scan(self, repos_path=None):
85 def repo_scan(self, repos_path=None):
86 """Listing of repositories in given path. This path should not be a
86 """Listing of repositories in given path. This path should not be a
87 repository itself. Return a dictionary of repository objects
87 repository itself. Return a dictionary of repository objects
88
88
89 :param repos_path: path to directory containing repositories
89 :param repos_path: path to directory containing repositories
90 """
90 """
91
91
92 log.info('scanning for repositories in %s', repos_path)
92 log.info('scanning for repositories in %s', repos_path)
93
93
94 if repos_path is None:
94 if repos_path is None:
95 repos_path = self.repos_path
95 repos_path = self.repos_path
96
96
97 baseui = make_ui('db')
97 baseui = make_ui('db')
98 repos_list = {}
98 repos_list = {}
99
99
100 for name, path in get_filesystem_repos(repos_path, recursive=True):
100 for name, path in get_filesystem_repos(repos_path, recursive=True):
101 try:
101 try:
102 if repos_list.has_key(name):
102 if repos_list.has_key(name):
103 raise RepositoryError('Duplicate repository name %s '
103 raise RepositoryError('Duplicate repository name %s '
104 'found in %s' % (name, path))
104 'found in %s' % (name, path))
105 else:
105 else:
106
106
107 klass = get_backend(path[0])
107 klass = get_backend(path[0])
108
108
109 if path[0] == 'hg' and path[0] in BACKENDS.keys():
109 if path[0] == 'hg' and path[0] in BACKENDS.keys():
110 repos_list[name] = klass(path[1], baseui=baseui)
110 repos_list[name] = klass(path[1], baseui=baseui)
111
111
112 if path[0] == 'git' and path[0] in BACKENDS.keys():
112 if path[0] == 'git' and path[0] in BACKENDS.keys():
113 repos_list[name] = klass(path[1])
113 repos_list[name] = klass(path[1])
114 except OSError:
114 except OSError:
115 continue
115 continue
116
116
117 return repos_list
117 return repos_list
118
118
119 def get_repos(self, all_repos=None):
119 def get_repos(self, all_repos=None):
120 """Get all repos from db and for each repo create it's backend instance.
120 """Get all repos from db and for each repo create it's backend instance.
121 and fill that backed with information from database
121 and fill that backed with information from database
122
122
123 :param all_repos: give specific repositories list, good for filtering
123 :param all_repos: give specific repositories list, good for filtering
124 """
124 """
125
125
126 if all_repos is None:
126 if all_repos is None:
127 all_repos = self.sa.query(Repository)\
127 all_repos = self.sa.query(Repository)\
128 .order_by(Repository.repo_name).all()
128 .order_by(Repository.repo_name).all()
129
129
130 #get the repositories that should be invalidated
130 #get the repositories that should be invalidated
131 invalidation_list = [str(x.cache_key) for x in \
131 invalidation_list = [str(x.cache_key) for x in \
132 self.sa.query(CacheInvalidation.cache_key)\
132 self.sa.query(CacheInvalidation.cache_key)\
133 .filter(CacheInvalidation.cache_active == False)\
133 .filter(CacheInvalidation.cache_active == False)\
134 .all()]
134 .all()]
135
135
136 for r in all_repos:
136 for r in all_repos:
137
137
138 repo, dbrepo = self.get(r.repo_name, invalidation_list)
138 repo, dbrepo = self.get(r.repo_name, invalidation_list)
139
139
140 if repo is not None:
140 if repo is not None:
141 last_change = repo.last_change
141 last_change = repo.last_change
142 tip = h.get_changeset_safe(repo, 'tip')
142 tip = h.get_changeset_safe(repo, 'tip')
143
143
144 tmp_d = {}
144 tmp_d = {}
145 tmp_d['name'] = r.repo_name
145 tmp_d['name'] = r.repo_name
146 tmp_d['name_sort'] = tmp_d['name'].lower()
146 tmp_d['name_sort'] = tmp_d['name'].lower()
147 tmp_d['description'] = dbrepo.description
147 tmp_d['description'] = dbrepo.description
148 tmp_d['description_sort'] = tmp_d['description']
148 tmp_d['description_sort'] = tmp_d['description']
149 tmp_d['last_change'] = last_change
149 tmp_d['last_change'] = last_change
150 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
150 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
151 tmp_d['tip'] = tip.raw_id
151 tmp_d['tip'] = tip.raw_id
152 tmp_d['tip_sort'] = tip.revision
152 tmp_d['tip_sort'] = tip.revision
153 tmp_d['rev'] = tip.revision
153 tmp_d['rev'] = tip.revision
154 tmp_d['contact'] = dbrepo.user.full_contact
154 tmp_d['contact'] = dbrepo.user.full_contact
155 tmp_d['contact_sort'] = tmp_d['contact']
155 tmp_d['contact_sort'] = tmp_d['contact']
156 tmp_d['owner_sort'] = tmp_d['contact']
156 tmp_d['owner_sort'] = tmp_d['contact']
157 tmp_d['repo_archives'] = list(repo._get_archives())
157 tmp_d['repo_archives'] = list(repo._get_archives())
158 tmp_d['last_msg'] = tip.message
158 tmp_d['last_msg'] = tip.message
159 tmp_d['repo'] = repo
159 tmp_d['repo'] = repo
160 tmp_d['dbrepo'] = dbrepo
160 tmp_d['dbrepo'] = dbrepo
161 yield tmp_d
161 yield tmp_d
162
162
163 def get(self, repo_name, invalidation_list=None, retval='all'):
163 def get(self, repo_name, invalidation_list=None, retval='all'):
164 """Returns a tuple of Repository,DbRepository,
164 """Returns a tuple of Repository,DbRepository,
165 Get's repository from given name, creates BackendInstance and
165 Get's repository from given name, creates BackendInstance and
166 propagates it's data from database with all additional information
166 propagates it's data from database with all additional information
167
167
168 :param repo_name:
168 :param repo_name:
169 :param invalidation_list: if a invalidation list is given the get
169 :param invalidation_list: if a invalidation list is given the get
170 method should not manually check if this repository needs
170 method should not manually check if this repository needs
171 invalidation and just invalidate the repositories in list
171 invalidation and just invalidate the repositories in list
172 :param retval: string specifing what to return one of 'repo','dbrepo',
172 :param retval: string specifing what to return one of 'repo','dbrepo',
173 'all'if repo or dbrepo is given it'll just lazy load chosen type
173 'all'if repo or dbrepo is given it'll just lazy load chosen type
174 and return None as the second
174 and return None as the second
175 """
175 """
176 if not HasRepoPermissionAny('repository.read', 'repository.write',
176 if not HasRepoPermissionAny('repository.read', 'repository.write',
177 'repository.admin')(repo_name, 'get repo check'):
177 'repository.admin')(repo_name, 'get repo check'):
178 return
178 return
179
179
180 #======================================================================
180 #======================================================================
181 # CACHE FUNCTION
181 # CACHE FUNCTION
182 #======================================================================
182 #======================================================================
183 @cache_region('long_term')
183 @cache_region('long_term')
184 def _get_repo(repo_name):
184 def _get_repo(repo_name):
185
185
186 repo_path = os.path.join(self.repos_path, repo_name)
186 repo_path = os.path.join(self.repos_path, repo_name)
187
187
188 try:
188 try:
189 alias = get_scm(repo_path)[0]
189 alias = get_scm(repo_path)[0]
190 log.debug('Creating instance of %s repository', alias)
190 log.debug('Creating instance of %s repository', alias)
191 backend = get_backend(alias)
191 backend = get_backend(alias)
192 except VCSError:
192 except VCSError:
193 log.error(traceback.format_exc())
193 log.error(traceback.format_exc())
194 log.error('Perhaps this repository is in db and not in '
194 log.error('Perhaps this repository is in db and not in '
195 'filesystem run rescan repositories with '
195 'filesystem run rescan repositories with '
196 '"destroy old data " option from admin panel')
196 '"destroy old data " option from admin panel')
197 return
197 return
198
198
199 if alias == 'hg':
199 if alias == 'hg':
200 repo = backend(repo_path, create=False, baseui=make_ui('db'))
200 repo = backend(repo_path, create=False, baseui=make_ui('db'))
201 #skip hidden web repository
201 #skip hidden web repository
202 if repo._get_hidden():
202 if repo._get_hidden():
203 return
203 return
204 else:
204 else:
205 repo = backend(repo_path, create=False)
205 repo = backend(repo_path, create=False)
206
206
207 return repo
207 return repo
208
208
209 pre_invalidate = True
209 pre_invalidate = True
210 dbinvalidate = False
210 dbinvalidate = False
211
211
212 if invalidation_list is not None:
212 if invalidation_list is not None:
213 pre_invalidate = repo_name in invalidation_list
213 pre_invalidate = repo_name in invalidation_list
214
214
215 if pre_invalidate:
215 if pre_invalidate:
216 #this returns object to invalidate
216 #this returns object to invalidate
217 invalidate = self._should_invalidate(repo_name)
217 invalidate = self._should_invalidate(repo_name)
218 if invalidate:
218 if invalidate:
219 log.info('invalidating cache for repository %s', repo_name)
219 log.info('invalidating cache for repository %s', repo_name)
220 #region_invalidate(_get_repo, None, repo_name)
220 region_invalidate(_get_repo, None, repo_name)
221 self._mark_invalidated(invalidate)
221 self._mark_invalidated(invalidate)
222 dbinvalidate = True
222 dbinvalidate = True
223
223
224 r, dbr = None, None
224 r, dbr = None, None
225 if retval == 'repo' or 'all':
225 if retval == 'repo' or 'all':
226 r = _get_repo(repo_name)
226 r = _get_repo(repo_name)
227 if retval == 'dbrepo' or 'all':
227 if retval == 'dbrepo' or 'all':
228 dbr = RepoModel(self.sa).get_full(repo_name, cache=True,
228 dbr = RepoModel(self.sa).get_full(repo_name, cache=True,
229 invalidate=dbinvalidate)
229 invalidate=dbinvalidate)
230
230
231
231
232 return r, dbr
232 return r, dbr
233
233
234
234
235
235
236 def mark_for_invalidation(self, repo_name):
236 def mark_for_invalidation(self, repo_name):
237 """Puts cache invalidation task into db for
237 """Puts cache invalidation task into db for
238 further global cache invalidation
238 further global cache invalidation
239
239
240 :param repo_name: this repo that should invalidation take place
240 :param repo_name: this repo that should invalidation take place
241 """
241 """
242
242
243 log.debug('marking %s for invalidation', repo_name)
243 log.debug('marking %s for invalidation', repo_name)
244 cache = self.sa.query(CacheInvalidation)\
244 cache = self.sa.query(CacheInvalidation)\
245 .filter(CacheInvalidation.cache_key == repo_name).scalar()
245 .filter(CacheInvalidation.cache_key == repo_name).scalar()
246
246
247 if cache:
247 if cache:
248 #mark this cache as inactive
248 #mark this cache as inactive
249 cache.cache_active = False
249 cache.cache_active = False
250 else:
250 else:
251 log.debug('cache key not found in invalidation db -> creating one')
251 log.debug('cache key not found in invalidation db -> creating one')
252 cache = CacheInvalidation(repo_name)
252 cache = CacheInvalidation(repo_name)
253
253
254 try:
254 try:
255 self.sa.add(cache)
255 self.sa.add(cache)
256 self.sa.commit()
256 self.sa.commit()
257 except (DatabaseError,):
257 except (DatabaseError,):
258 log.error(traceback.format_exc())
258 log.error(traceback.format_exc())
259 self.sa.rollback()
259 self.sa.rollback()
260
260
261
261
262 def toggle_following_repo(self, follow_repo_id, user_id):
262 def toggle_following_repo(self, follow_repo_id, user_id):
263
263
264 f = self.sa.query(UserFollowing)\
264 f = self.sa.query(UserFollowing)\
265 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
265 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
266 .filter(UserFollowing.user_id == user_id).scalar()
266 .filter(UserFollowing.user_id == user_id).scalar()
267
267
268 if f is not None:
268 if f is not None:
269
269
270 try:
270 try:
271 self.sa.delete(f)
271 self.sa.delete(f)
272 self.sa.commit()
272 self.sa.commit()
273 action_logger(UserTemp(user_id),
273 action_logger(UserTemp(user_id),
274 'stopped_following_repo',
274 'stopped_following_repo',
275 RepoTemp(follow_repo_id))
275 RepoTemp(follow_repo_id))
276 return
276 return
277 except:
277 except:
278 log.error(traceback.format_exc())
278 log.error(traceback.format_exc())
279 self.sa.rollback()
279 self.sa.rollback()
280 raise
280 raise
281
281
282
282
283 try:
283 try:
284 f = UserFollowing()
284 f = UserFollowing()
285 f.user_id = user_id
285 f.user_id = user_id
286 f.follows_repo_id = follow_repo_id
286 f.follows_repo_id = follow_repo_id
287 self.sa.add(f)
287 self.sa.add(f)
288 self.sa.commit()
288 self.sa.commit()
289 action_logger(UserTemp(user_id),
289 action_logger(UserTemp(user_id),
290 'started_following_repo',
290 'started_following_repo',
291 RepoTemp(follow_repo_id))
291 RepoTemp(follow_repo_id))
292 except:
292 except:
293 log.error(traceback.format_exc())
293 log.error(traceback.format_exc())
294 self.sa.rollback()
294 self.sa.rollback()
295 raise
295 raise
296
296
297 def toggle_following_user(self, follow_user_id , user_id):
297 def toggle_following_user(self, follow_user_id , user_id):
298 f = self.sa.query(UserFollowing)\
298 f = self.sa.query(UserFollowing)\
299 .filter(UserFollowing.follows_user_id == follow_user_id)\
299 .filter(UserFollowing.follows_user_id == follow_user_id)\
300 .filter(UserFollowing.user_id == user_id).scalar()
300 .filter(UserFollowing.user_id == user_id).scalar()
301
301
302 if f is not None:
302 if f is not None:
303 try:
303 try:
304 self.sa.delete(f)
304 self.sa.delete(f)
305 self.sa.commit()
305 self.sa.commit()
306 return
306 return
307 except:
307 except:
308 log.error(traceback.format_exc())
308 log.error(traceback.format_exc())
309 self.sa.rollback()
309 self.sa.rollback()
310 raise
310 raise
311
311
312 try:
312 try:
313 f = UserFollowing()
313 f = UserFollowing()
314 f.user_id = user_id
314 f.user_id = user_id
315 f.follows_user_id = follow_user_id
315 f.follows_user_id = follow_user_id
316 self.sa.add(f)
316 self.sa.add(f)
317 self.sa.commit()
317 self.sa.commit()
318 except:
318 except:
319 log.error(traceback.format_exc())
319 log.error(traceback.format_exc())
320 self.sa.rollback()
320 self.sa.rollback()
321 raise
321 raise
322
322
323 def is_following_repo(self, repo_name, user_id, cache=False):
323 def is_following_repo(self, repo_name, user_id, cache=False):
324 r = self.sa.query(Repository)\
324 r = self.sa.query(Repository)\
325 .filter(Repository.repo_name == repo_name).scalar()
325 .filter(Repository.repo_name == repo_name).scalar()
326
326
327 f = self.sa.query(UserFollowing)\
327 f = self.sa.query(UserFollowing)\
328 .filter(UserFollowing.follows_repository == r)\
328 .filter(UserFollowing.follows_repository == r)\
329 .filter(UserFollowing.user_id == user_id).scalar()
329 .filter(UserFollowing.user_id == user_id).scalar()
330
330
331 return f is not None
331 return f is not None
332
332
333 def is_following_user(self, username, user_id, cache=False):
333 def is_following_user(self, username, user_id, cache=False):
334 u = UserModel(self.sa).get_by_username(username)
334 u = UserModel(self.sa).get_by_username(username)
335
335
336 f = self.sa.query(UserFollowing)\
336 f = self.sa.query(UserFollowing)\
337 .filter(UserFollowing.follows_user == u)\
337 .filter(UserFollowing.follows_user == u)\
338 .filter(UserFollowing.user_id == user_id).scalar()
338 .filter(UserFollowing.user_id == user_id).scalar()
339
339
340 return f is not None
340 return f is not None
341
341
342 def get_followers(self, repo_id):
342 def get_followers(self, repo_id):
343 return self.sa.query(UserFollowing)\
343 return self.sa.query(UserFollowing)\
344 .filter(UserFollowing.follows_repo_id == repo_id).count()
344 .filter(UserFollowing.follows_repo_id == repo_id).count()
345
345
346 def get_forks(self, repo_id):
346 def get_forks(self, repo_id):
347 return self.sa.query(Repository)\
347 return self.sa.query(Repository)\
348 .filter(Repository.fork_id == repo_id).count()
348 .filter(Repository.fork_id == repo_id).count()
349
349
350
350
351 def get_unread_journal(self):
351 def get_unread_journal(self):
352 return self.sa.query(UserLog).count()
352 return self.sa.query(UserLog).count()
353
353
354
354
355 def _should_invalidate(self, repo_name):
355 def _should_invalidate(self, repo_name):
356 """Looks up database for invalidation signals for this repo_name
356 """Looks up database for invalidation signals for this repo_name
357
357
358 :param repo_name:
358 :param repo_name:
359 """
359 """
360
360
361 ret = self.sa.query(CacheInvalidation)\
361 ret = self.sa.query(CacheInvalidation)\
362 .filter(CacheInvalidation.cache_key == repo_name)\
362 .filter(CacheInvalidation.cache_key == repo_name)\
363 .filter(CacheInvalidation.cache_active == False)\
363 .filter(CacheInvalidation.cache_active == False)\
364 .scalar()
364 .scalar()
365
365
366 return ret
366 return ret
367
367
368 def _mark_invalidated(self, cache_key):
368 def _mark_invalidated(self, cache_key):
369 """ Marks all occurrences of cache to invalidation as already
369 """ Marks all occurrences of cache to invalidation as already
370 invalidated
370 invalidated
371
371
372 :param cache_key:
372 :param cache_key:
373 """
373 """
374
374
375 if cache_key:
375 if cache_key:
376 log.debug('marking %s as already invalidated', cache_key)
376 log.debug('marking %s as already invalidated', cache_key)
377 try:
377 try:
378 cache_key.cache_active = True
378 cache_key.cache_active = True
379 self.sa.add(cache_key)
379 self.sa.add(cache_key)
380 self.sa.commit()
380 self.sa.commit()
381 except (DatabaseError,):
381 except (DatabaseError,):
382 log.error(traceback.format_exc())
382 log.error(traceback.format_exc())
383 self.sa.rollback()
383 self.sa.rollback()
384
384
General Comments 0
You need to be logged in to leave comments. Login now