##// END OF EJS Templates
New repo model create function...
marcink -
r2524:9d4b8074 beta
parent child Browse files
Show More
@@ -1,442 +1,442 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.repos
3 rhodecode.controllers.admin.repos
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Repositories controller for RhodeCode
6 Repositories controller for RhodeCode
7
7
8 :created_on: Apr 7, 2010
8 :created_on: Apr 7, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28 import formencode
28 import formencode
29 from formencode import htmlfill
29 from formencode import htmlfill
30
30
31 from webob.exc import HTTPInternalServerError
31 from webob.exc import HTTPInternalServerError
32 from pylons import request, session, tmpl_context as c, url
32 from pylons import request, session, tmpl_context as c, url
33 from pylons.controllers.util import redirect
33 from pylons.controllers.util import redirect
34 from pylons.i18n.translation import _
34 from pylons.i18n.translation import _
35 from sqlalchemy.exc import IntegrityError
35 from sqlalchemy.exc import IntegrityError
36
36
37 from rhodecode.lib import helpers as h
37 from rhodecode.lib import helpers as h
38 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
38 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
39 HasPermissionAnyDecorator, HasRepoPermissionAllDecorator
39 HasPermissionAnyDecorator, HasRepoPermissionAllDecorator
40 from rhodecode.lib.base import BaseController, render
40 from rhodecode.lib.base import BaseController, render
41 from rhodecode.lib.utils import invalidate_cache, action_logger, repo_name_slug
41 from rhodecode.lib.utils import invalidate_cache, action_logger, repo_name_slug
42 from rhodecode.lib.helpers import get_token
42 from rhodecode.lib.helpers import get_token
43 from rhodecode.model.meta import Session
43 from rhodecode.model.meta import Session
44 from rhodecode.model.db import User, Repository, UserFollowing, RepoGroup
44 from rhodecode.model.db import User, Repository, UserFollowing, RepoGroup
45 from rhodecode.model.forms import RepoForm
45 from rhodecode.model.forms import RepoForm
46 from rhodecode.model.scm import ScmModel
46 from rhodecode.model.scm import ScmModel
47 from rhodecode.model.repo import RepoModel
47 from rhodecode.model.repo import RepoModel
48
48
49 log = logging.getLogger(__name__)
49 log = logging.getLogger(__name__)
50
50
51
51
52 class ReposController(BaseController):
52 class ReposController(BaseController):
53 """
53 """
54 REST Controller styled on the Atom Publishing Protocol"""
54 REST Controller styled on the Atom Publishing Protocol"""
55 # To properly map this controller, ensure your config/routing.py
55 # To properly map this controller, ensure your config/routing.py
56 # file has a resource setup:
56 # file has a resource setup:
57 # map.resource('repo', 'repos')
57 # map.resource('repo', 'repos')
58
58
59 @LoginRequired()
59 @LoginRequired()
60 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
60 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
61 def __before__(self):
61 def __before__(self):
62 c.admin_user = session.get('admin_user')
62 c.admin_user = session.get('admin_user')
63 c.admin_username = session.get('admin_username')
63 c.admin_username = session.get('admin_username')
64 super(ReposController, self).__before__()
64 super(ReposController, self).__before__()
65
65
66 def __load_defaults(self):
66 def __load_defaults(self):
67 c.repo_groups = RepoGroup.groups_choices()
67 c.repo_groups = RepoGroup.groups_choices()
68 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
68 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
69
69
70 repo_model = RepoModel()
70 repo_model = RepoModel()
71 c.users_array = repo_model.get_users_js()
71 c.users_array = repo_model.get_users_js()
72 c.users_groups_array = repo_model.get_users_groups_js()
72 c.users_groups_array = repo_model.get_users_groups_js()
73 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
73 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
74 c.landing_revs_choices = choices
74 c.landing_revs_choices = choices
75
75
76 def __load_data(self, repo_name=None):
76 def __load_data(self, repo_name=None):
77 """
77 """
78 Load defaults settings for edit, and update
78 Load defaults settings for edit, and update
79
79
80 :param repo_name:
80 :param repo_name:
81 """
81 """
82 self.__load_defaults()
82 self.__load_defaults()
83
83
84 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
84 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
85 repo = db_repo.scm_instance
85 repo = db_repo.scm_instance
86
86
87 if c.repo_info is None:
87 if c.repo_info is None:
88 h.flash(_('%s repository is not mapped to db perhaps'
88 h.flash(_('%s repository is not mapped to db perhaps'
89 ' it was created or renamed from the filesystem'
89 ' it was created or renamed from the filesystem'
90 ' please run the application again'
90 ' please run the application again'
91 ' in order to rescan repositories') % repo_name,
91 ' in order to rescan repositories') % repo_name,
92 category='error')
92 category='error')
93
93
94 return redirect(url('repos'))
94 return redirect(url('repos'))
95
95
96 choices, c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info)
96 choices, c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info)
97 c.landing_revs_choices = choices
97 c.landing_revs_choices = choices
98
98
99 c.default_user_id = User.get_by_username('default').user_id
99 c.default_user_id = User.get_by_username('default').user_id
100 c.in_public_journal = UserFollowing.query()\
100 c.in_public_journal = UserFollowing.query()\
101 .filter(UserFollowing.user_id == c.default_user_id)\
101 .filter(UserFollowing.user_id == c.default_user_id)\
102 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
102 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
103
103
104 if c.repo_info.stats:
104 if c.repo_info.stats:
105 # this is on what revision we ended up so we add +1 for count
105 # this is on what revision we ended up so we add +1 for count
106 last_rev = c.repo_info.stats.stat_on_revision + 1
106 last_rev = c.repo_info.stats.stat_on_revision + 1
107 else:
107 else:
108 last_rev = 0
108 last_rev = 0
109 c.stats_revision = last_rev
109 c.stats_revision = last_rev
110
110
111 c.repo_last_rev = repo.count() if repo.revisions else 0
111 c.repo_last_rev = repo.count() if repo.revisions else 0
112
112
113 if last_rev == 0 or c.repo_last_rev == 0:
113 if last_rev == 0 or c.repo_last_rev == 0:
114 c.stats_percentage = 0
114 c.stats_percentage = 0
115 else:
115 else:
116 c.stats_percentage = '%.2f' % ((float((last_rev)) /
116 c.stats_percentage = '%.2f' % ((float((last_rev)) /
117 c.repo_last_rev) * 100)
117 c.repo_last_rev) * 100)
118
118
119 defaults = RepoModel()._get_defaults(repo_name)
119 defaults = RepoModel()._get_defaults(repo_name)
120
120
121 c.repos_list = [('', _('--REMOVE FORK--'))]
121 c.repos_list = [('', _('--REMOVE FORK--'))]
122 c.repos_list += [(x.repo_id, x.repo_name) for x in
122 c.repos_list += [(x.repo_id, x.repo_name) for x in
123 Repository.query().order_by(Repository.repo_name).all()]
123 Repository.query().order_by(Repository.repo_name).all()]
124
124
125 return defaults
125 return defaults
126
126
127 @HasPermissionAllDecorator('hg.admin')
127 @HasPermissionAllDecorator('hg.admin')
128 def index(self, format='html'):
128 def index(self, format='html'):
129 """GET /repos: All items in the collection"""
129 """GET /repos: All items in the collection"""
130 # url('repos')
130 # url('repos')
131
131
132 c.repos_list = ScmModel().get_repos(Repository.query()
132 c.repos_list = ScmModel().get_repos(Repository.query()
133 .order_by(Repository.repo_name)
133 .order_by(Repository.repo_name)
134 .all(), sort_key='name_sort')
134 .all(), sort_key='name_sort')
135 return render('admin/repos/repos.html')
135 return render('admin/repos/repos.html')
136
136
137 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
137 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
138 def create(self):
138 def create(self):
139 """
139 """
140 POST /repos: Create a new item"""
140 POST /repos: Create a new item"""
141 # url('repos')
141 # url('repos')
142
142
143 self.__load_defaults()
143 self.__load_defaults()
144 form_result = {}
144 form_result = {}
145 try:
145 try:
146 form_result = RepoForm(repo_groups=c.repo_groups_choices,
146 form_result = RepoForm(repo_groups=c.repo_groups_choices,
147 landing_revs=c.landing_revs_choices)()\
147 landing_revs=c.landing_revs_choices)()\
148 .to_python(dict(request.POST))
148 .to_python(dict(request.POST))
149 RepoModel().create(form_result, self.rhodecode_user)
149 RepoModel().create(form_result, self.rhodecode_user.user_id)
150 if form_result['clone_uri']:
150 if form_result['clone_uri']:
151 h.flash(_('created repository %s from %s') \
151 h.flash(_('created repository %s from %s') \
152 % (form_result['repo_name'], form_result['clone_uri']),
152 % (form_result['repo_name'], form_result['clone_uri']),
153 category='success')
153 category='success')
154 else:
154 else:
155 h.flash(_('created repository %s') % form_result['repo_name'],
155 h.flash(_('created repository %s') % form_result['repo_name'],
156 category='success')
156 category='success')
157
157
158 if request.POST.get('user_created'):
158 if request.POST.get('user_created'):
159 # created by regular non admin user
159 # created by regular non admin user
160 action_logger(self.rhodecode_user, 'user_created_repo',
160 action_logger(self.rhodecode_user, 'user_created_repo',
161 form_result['repo_name_full'], self.ip_addr,
161 form_result['repo_name_full'], self.ip_addr,
162 self.sa)
162 self.sa)
163 else:
163 else:
164 action_logger(self.rhodecode_user, 'admin_created_repo',
164 action_logger(self.rhodecode_user, 'admin_created_repo',
165 form_result['repo_name_full'], self.ip_addr,
165 form_result['repo_name_full'], self.ip_addr,
166 self.sa)
166 self.sa)
167 Session.commit()
167 Session.commit()
168 except formencode.Invalid, errors:
168 except formencode.Invalid, errors:
169
169
170 c.new_repo = errors.value['repo_name']
170 c.new_repo = errors.value['repo_name']
171
171
172 if request.POST.get('user_created'):
172 if request.POST.get('user_created'):
173 r = render('admin/repos/repo_add_create_repository.html')
173 r = render('admin/repos/repo_add_create_repository.html')
174 else:
174 else:
175 r = render('admin/repos/repo_add.html')
175 r = render('admin/repos/repo_add.html')
176
176
177 return htmlfill.render(
177 return htmlfill.render(
178 r,
178 r,
179 defaults=errors.value,
179 defaults=errors.value,
180 errors=errors.error_dict or {},
180 errors=errors.error_dict or {},
181 prefix_error=False,
181 prefix_error=False,
182 encoding="UTF-8")
182 encoding="UTF-8")
183
183
184 except Exception:
184 except Exception:
185 log.error(traceback.format_exc())
185 log.error(traceback.format_exc())
186 msg = _('error occurred during creation of repository %s') \
186 msg = _('error occurred during creation of repository %s') \
187 % form_result.get('repo_name')
187 % form_result.get('repo_name')
188 h.flash(msg, category='error')
188 h.flash(msg, category='error')
189 if request.POST.get('user_created'):
189 if request.POST.get('user_created'):
190 return redirect(url('home'))
190 return redirect(url('home'))
191 return redirect(url('repos'))
191 return redirect(url('repos'))
192
192
193 @HasPermissionAllDecorator('hg.admin')
193 @HasPermissionAllDecorator('hg.admin')
194 def new(self, format='html'):
194 def new(self, format='html'):
195 """GET /repos/new: Form to create a new item"""
195 """GET /repos/new: Form to create a new item"""
196 new_repo = request.GET.get('repo', '')
196 new_repo = request.GET.get('repo', '')
197 c.new_repo = repo_name_slug(new_repo)
197 c.new_repo = repo_name_slug(new_repo)
198 self.__load_defaults()
198 self.__load_defaults()
199 return render('admin/repos/repo_add.html')
199 return render('admin/repos/repo_add.html')
200
200
201 @HasPermissionAllDecorator('hg.admin')
201 @HasPermissionAllDecorator('hg.admin')
202 def update(self, repo_name):
202 def update(self, repo_name):
203 """
203 """
204 PUT /repos/repo_name: Update an existing item"""
204 PUT /repos/repo_name: Update an existing item"""
205 # Forms posted to this method should contain a hidden field:
205 # Forms posted to this method should contain a hidden field:
206 # <input type="hidden" name="_method" value="PUT" />
206 # <input type="hidden" name="_method" value="PUT" />
207 # Or using helpers:
207 # Or using helpers:
208 # h.form(url('repo', repo_name=ID),
208 # h.form(url('repo', repo_name=ID),
209 # method='put')
209 # method='put')
210 # url('repo', repo_name=ID)
210 # url('repo', repo_name=ID)
211 self.__load_defaults()
211 self.__load_defaults()
212 repo_model = RepoModel()
212 repo_model = RepoModel()
213 changed_name = repo_name
213 changed_name = repo_name
214 _form = RepoForm(edit=True, old_data={'repo_name': repo_name},
214 _form = RepoForm(edit=True, old_data={'repo_name': repo_name},
215 repo_groups=c.repo_groups_choices,
215 repo_groups=c.repo_groups_choices,
216 landing_revs=c.landing_revs_choices)()
216 landing_revs=c.landing_revs_choices)()
217 try:
217 try:
218 form_result = _form.to_python(dict(request.POST))
218 form_result = _form.to_python(dict(request.POST))
219 repo = repo_model.update(repo_name, form_result)
219 repo = repo_model.update(repo_name, form_result)
220 invalidate_cache('get_repo_cached_%s' % repo_name)
220 invalidate_cache('get_repo_cached_%s' % repo_name)
221 h.flash(_('Repository %s updated successfully' % repo_name),
221 h.flash(_('Repository %s updated successfully' % repo_name),
222 category='success')
222 category='success')
223 changed_name = repo.repo_name
223 changed_name = repo.repo_name
224 action_logger(self.rhodecode_user, 'admin_updated_repo',
224 action_logger(self.rhodecode_user, 'admin_updated_repo',
225 changed_name, self.ip_addr, self.sa)
225 changed_name, self.ip_addr, self.sa)
226 Session.commit()
226 Session.commit()
227 except formencode.Invalid, errors:
227 except formencode.Invalid, errors:
228 defaults = self.__load_data(repo_name)
228 defaults = self.__load_data(repo_name)
229 defaults.update(errors.value)
229 defaults.update(errors.value)
230 return htmlfill.render(
230 return htmlfill.render(
231 render('admin/repos/repo_edit.html'),
231 render('admin/repos/repo_edit.html'),
232 defaults=defaults,
232 defaults=defaults,
233 errors=errors.error_dict or {},
233 errors=errors.error_dict or {},
234 prefix_error=False,
234 prefix_error=False,
235 encoding="UTF-8")
235 encoding="UTF-8")
236
236
237 except Exception:
237 except Exception:
238 log.error(traceback.format_exc())
238 log.error(traceback.format_exc())
239 h.flash(_('error occurred during update of repository %s') \
239 h.flash(_('error occurred during update of repository %s') \
240 % repo_name, category='error')
240 % repo_name, category='error')
241 return redirect(url('edit_repo', repo_name=changed_name))
241 return redirect(url('edit_repo', repo_name=changed_name))
242
242
243 @HasPermissionAllDecorator('hg.admin')
243 @HasPermissionAllDecorator('hg.admin')
244 def delete(self, repo_name):
244 def delete(self, repo_name):
245 """
245 """
246 DELETE /repos/repo_name: Delete an existing item"""
246 DELETE /repos/repo_name: Delete an existing item"""
247 # Forms posted to this method should contain a hidden field:
247 # Forms posted to this method should contain a hidden field:
248 # <input type="hidden" name="_method" value="DELETE" />
248 # <input type="hidden" name="_method" value="DELETE" />
249 # Or using helpers:
249 # Or using helpers:
250 # h.form(url('repo', repo_name=ID),
250 # h.form(url('repo', repo_name=ID),
251 # method='delete')
251 # method='delete')
252 # url('repo', repo_name=ID)
252 # url('repo', repo_name=ID)
253
253
254 repo_model = RepoModel()
254 repo_model = RepoModel()
255 repo = repo_model.get_by_repo_name(repo_name)
255 repo = repo_model.get_by_repo_name(repo_name)
256 if not repo:
256 if not repo:
257 h.flash(_('%s repository is not mapped to db perhaps'
257 h.flash(_('%s repository is not mapped to db perhaps'
258 ' it was moved or renamed from the filesystem'
258 ' it was moved or renamed from the filesystem'
259 ' please run the application again'
259 ' please run the application again'
260 ' in order to rescan repositories') % repo_name,
260 ' in order to rescan repositories') % repo_name,
261 category='error')
261 category='error')
262
262
263 return redirect(url('repos'))
263 return redirect(url('repos'))
264 try:
264 try:
265 action_logger(self.rhodecode_user, 'admin_deleted_repo',
265 action_logger(self.rhodecode_user, 'admin_deleted_repo',
266 repo_name, self.ip_addr, self.sa)
266 repo_name, self.ip_addr, self.sa)
267 repo_model.delete(repo)
267 repo_model.delete(repo)
268 invalidate_cache('get_repo_cached_%s' % repo_name)
268 invalidate_cache('get_repo_cached_%s' % repo_name)
269 h.flash(_('deleted repository %s') % repo_name, category='success')
269 h.flash(_('deleted repository %s') % repo_name, category='success')
270 Session.commit()
270 Session.commit()
271 except IntegrityError, e:
271 except IntegrityError, e:
272 if e.message.find('repositories_fork_id_fkey') != -1:
272 if e.message.find('repositories_fork_id_fkey') != -1:
273 log.error(traceback.format_exc())
273 log.error(traceback.format_exc())
274 h.flash(_('Cannot delete %s it still contains attached '
274 h.flash(_('Cannot delete %s it still contains attached '
275 'forks') % repo_name,
275 'forks') % repo_name,
276 category='warning')
276 category='warning')
277 else:
277 else:
278 log.error(traceback.format_exc())
278 log.error(traceback.format_exc())
279 h.flash(_('An error occurred during '
279 h.flash(_('An error occurred during '
280 'deletion of %s') % repo_name,
280 'deletion of %s') % repo_name,
281 category='error')
281 category='error')
282
282
283 except Exception, e:
283 except Exception, e:
284 log.error(traceback.format_exc())
284 log.error(traceback.format_exc())
285 h.flash(_('An error occurred during deletion of %s') % repo_name,
285 h.flash(_('An error occurred during deletion of %s') % repo_name,
286 category='error')
286 category='error')
287
287
288 return redirect(url('repos'))
288 return redirect(url('repos'))
289
289
290 @HasRepoPermissionAllDecorator('repository.admin')
290 @HasRepoPermissionAllDecorator('repository.admin')
291 def delete_perm_user(self, repo_name):
291 def delete_perm_user(self, repo_name):
292 """
292 """
293 DELETE an existing repository permission user
293 DELETE an existing repository permission user
294
294
295 :param repo_name:
295 :param repo_name:
296 """
296 """
297 try:
297 try:
298 RepoModel().revoke_user_permission(repo=repo_name,
298 RepoModel().revoke_user_permission(repo=repo_name,
299 user=request.POST['user_id'])
299 user=request.POST['user_id'])
300 Session.commit()
300 Session.commit()
301 except Exception:
301 except Exception:
302 log.error(traceback.format_exc())
302 log.error(traceback.format_exc())
303 h.flash(_('An error occurred during deletion of repository user'),
303 h.flash(_('An error occurred during deletion of repository user'),
304 category='error')
304 category='error')
305 raise HTTPInternalServerError()
305 raise HTTPInternalServerError()
306
306
307 @HasRepoPermissionAllDecorator('repository.admin')
307 @HasRepoPermissionAllDecorator('repository.admin')
308 def delete_perm_users_group(self, repo_name):
308 def delete_perm_users_group(self, repo_name):
309 """
309 """
310 DELETE an existing repository permission users group
310 DELETE an existing repository permission users group
311
311
312 :param repo_name:
312 :param repo_name:
313 """
313 """
314
314
315 try:
315 try:
316 RepoModel().revoke_users_group_permission(
316 RepoModel().revoke_users_group_permission(
317 repo=repo_name, group_name=request.POST['users_group_id']
317 repo=repo_name, group_name=request.POST['users_group_id']
318 )
318 )
319 Session.commit()
319 Session.commit()
320 except Exception:
320 except Exception:
321 log.error(traceback.format_exc())
321 log.error(traceback.format_exc())
322 h.flash(_('An error occurred during deletion of repository'
322 h.flash(_('An error occurred during deletion of repository'
323 ' users groups'),
323 ' users groups'),
324 category='error')
324 category='error')
325 raise HTTPInternalServerError()
325 raise HTTPInternalServerError()
326
326
327 @HasPermissionAllDecorator('hg.admin')
327 @HasPermissionAllDecorator('hg.admin')
328 def repo_stats(self, repo_name):
328 def repo_stats(self, repo_name):
329 """
329 """
330 DELETE an existing repository statistics
330 DELETE an existing repository statistics
331
331
332 :param repo_name:
332 :param repo_name:
333 """
333 """
334
334
335 try:
335 try:
336 RepoModel().delete_stats(repo_name)
336 RepoModel().delete_stats(repo_name)
337 Session.commit()
337 Session.commit()
338 except Exception, e:
338 except Exception, e:
339 h.flash(_('An error occurred during deletion of repository stats'),
339 h.flash(_('An error occurred during deletion of repository stats'),
340 category='error')
340 category='error')
341 return redirect(url('edit_repo', repo_name=repo_name))
341 return redirect(url('edit_repo', repo_name=repo_name))
342
342
343 @HasPermissionAllDecorator('hg.admin')
343 @HasPermissionAllDecorator('hg.admin')
344 def repo_cache(self, repo_name):
344 def repo_cache(self, repo_name):
345 """
345 """
346 INVALIDATE existing repository cache
346 INVALIDATE existing repository cache
347
347
348 :param repo_name:
348 :param repo_name:
349 """
349 """
350
350
351 try:
351 try:
352 ScmModel().mark_for_invalidation(repo_name)
352 ScmModel().mark_for_invalidation(repo_name)
353 Session.commit()
353 Session.commit()
354 except Exception, e:
354 except Exception, e:
355 h.flash(_('An error occurred during cache invalidation'),
355 h.flash(_('An error occurred during cache invalidation'),
356 category='error')
356 category='error')
357 return redirect(url('edit_repo', repo_name=repo_name))
357 return redirect(url('edit_repo', repo_name=repo_name))
358
358
359 @HasPermissionAllDecorator('hg.admin')
359 @HasPermissionAllDecorator('hg.admin')
360 def repo_public_journal(self, repo_name):
360 def repo_public_journal(self, repo_name):
361 """
361 """
362 Set's this repository to be visible in public journal,
362 Set's this repository to be visible in public journal,
363 in other words assing default user to follow this repo
363 in other words assing default user to follow this repo
364
364
365 :param repo_name:
365 :param repo_name:
366 """
366 """
367
367
368 cur_token = request.POST.get('auth_token')
368 cur_token = request.POST.get('auth_token')
369 token = get_token()
369 token = get_token()
370 if cur_token == token:
370 if cur_token == token:
371 try:
371 try:
372 repo_id = Repository.get_by_repo_name(repo_name).repo_id
372 repo_id = Repository.get_by_repo_name(repo_name).repo_id
373 user_id = User.get_by_username('default').user_id
373 user_id = User.get_by_username('default').user_id
374 self.scm_model.toggle_following_repo(repo_id, user_id)
374 self.scm_model.toggle_following_repo(repo_id, user_id)
375 h.flash(_('Updated repository visibility in public journal'),
375 h.flash(_('Updated repository visibility in public journal'),
376 category='success')
376 category='success')
377 Session.commit()
377 Session.commit()
378 except:
378 except:
379 h.flash(_('An error occurred during setting this'
379 h.flash(_('An error occurred during setting this'
380 ' repository in public journal'),
380 ' repository in public journal'),
381 category='error')
381 category='error')
382
382
383 else:
383 else:
384 h.flash(_('Token mismatch'), category='error')
384 h.flash(_('Token mismatch'), category='error')
385 return redirect(url('edit_repo', repo_name=repo_name))
385 return redirect(url('edit_repo', repo_name=repo_name))
386
386
387 @HasPermissionAllDecorator('hg.admin')
387 @HasPermissionAllDecorator('hg.admin')
388 def repo_pull(self, repo_name):
388 def repo_pull(self, repo_name):
389 """
389 """
390 Runs task to update given repository with remote changes,
390 Runs task to update given repository with remote changes,
391 ie. make pull on remote location
391 ie. make pull on remote location
392
392
393 :param repo_name:
393 :param repo_name:
394 """
394 """
395 try:
395 try:
396 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
396 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
397 h.flash(_('Pulled from remote location'), category='success')
397 h.flash(_('Pulled from remote location'), category='success')
398 except Exception, e:
398 except Exception, e:
399 h.flash(_('An error occurred during pull from remote location'),
399 h.flash(_('An error occurred during pull from remote location'),
400 category='error')
400 category='error')
401
401
402 return redirect(url('edit_repo', repo_name=repo_name))
402 return redirect(url('edit_repo', repo_name=repo_name))
403
403
404 @HasPermissionAllDecorator('hg.admin')
404 @HasPermissionAllDecorator('hg.admin')
405 def repo_as_fork(self, repo_name):
405 def repo_as_fork(self, repo_name):
406 """
406 """
407 Mark given repository as a fork of another
407 Mark given repository as a fork of another
408
408
409 :param repo_name:
409 :param repo_name:
410 """
410 """
411 try:
411 try:
412 fork_id = request.POST.get('id_fork_of')
412 fork_id = request.POST.get('id_fork_of')
413 repo = ScmModel().mark_as_fork(repo_name, fork_id,
413 repo = ScmModel().mark_as_fork(repo_name, fork_id,
414 self.rhodecode_user.username)
414 self.rhodecode_user.username)
415 fork = repo.fork.repo_name if repo.fork else _('Nothing')
415 fork = repo.fork.repo_name if repo.fork else _('Nothing')
416 Session.commit()
416 Session.commit()
417 h.flash(_('Marked repo %s as fork of %s' % (repo_name,fork)),
417 h.flash(_('Marked repo %s as fork of %s' % (repo_name,fork)),
418 category='success')
418 category='success')
419 except Exception, e:
419 except Exception, e:
420 raise
420 raise
421 h.flash(_('An error occurred during this operation'),
421 h.flash(_('An error occurred during this operation'),
422 category='error')
422 category='error')
423
423
424 return redirect(url('edit_repo', repo_name=repo_name))
424 return redirect(url('edit_repo', repo_name=repo_name))
425
425
426 @HasPermissionAllDecorator('hg.admin')
426 @HasPermissionAllDecorator('hg.admin')
427 def show(self, repo_name, format='html'):
427 def show(self, repo_name, format='html'):
428 """GET /repos/repo_name: Show a specific item"""
428 """GET /repos/repo_name: Show a specific item"""
429 # url('repo', repo_name=ID)
429 # url('repo', repo_name=ID)
430
430
431 @HasPermissionAllDecorator('hg.admin')
431 @HasPermissionAllDecorator('hg.admin')
432 def edit(self, repo_name, format='html'):
432 def edit(self, repo_name, format='html'):
433 """GET /repos/repo_name/edit: Form to edit an existing item"""
433 """GET /repos/repo_name/edit: Form to edit an existing item"""
434 # url('edit_repo', repo_name=ID)
434 # url('edit_repo', repo_name=ID)
435 defaults = self.__load_data(repo_name)
435 defaults = self.__load_data(repo_name)
436
436
437 return htmlfill.render(
437 return htmlfill.render(
438 render('admin/repos/repo_edit.html'),
438 render('admin/repos/repo_edit.html'),
439 defaults=defaults,
439 defaults=defaults,
440 encoding="UTF-8",
440 encoding="UTF-8",
441 force_defaults=False
441 force_defaults=False
442 )
442 )
@@ -1,128 +1,128 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.celerylib.__init__
3 rhodecode.lib.celerylib.__init__
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 celery libs for RhodeCode
6 celery libs for RhodeCode
7
7
8 :created_on: Nov 27, 2010
8 :created_on: Nov 27, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import sys
27 import sys
28 import socket
28 import socket
29 import traceback
29 import traceback
30 import logging
30 import logging
31 from os.path import dirname as dn, join as jn
31 from os.path import dirname as dn, join as jn
32 from pylons import config
32 from pylons import config
33
33
34 from hashlib import md5
34 from hashlib import md5
35 from decorator import decorator
35 from decorator import decorator
36
36
37 from rhodecode.lib.vcs.utils.lazy import LazyProperty
37 from rhodecode.lib.vcs.utils.lazy import LazyProperty
38 from rhodecode import CELERY_ON, CELERY_EAGER
38 from rhodecode import CELERY_ON, CELERY_EAGER
39 from rhodecode.lib.utils2 import str2bool, safe_str
39 from rhodecode.lib.utils2 import str2bool, safe_str
40 from rhodecode.lib.pidlock import DaemonLock, LockHeld
40 from rhodecode.lib.pidlock import DaemonLock, LockHeld
41 from rhodecode.model import init_model
41 from rhodecode.model import init_model
42 from rhodecode.model import meta
42 from rhodecode.model import meta
43 from rhodecode.model.db import Statistics, Repository, User
43 from rhodecode.model.db import Statistics, Repository, User
44
44
45 from sqlalchemy import engine_from_config
45 from sqlalchemy import engine_from_config
46
46
47 from celery.messaging import establish_connection
47 from celery.messaging import establish_connection
48
48
49 log = logging.getLogger(__name__)
49 log = logging.getLogger(__name__)
50
50
51
51
52 class ResultWrapper(object):
52 class ResultWrapper(object):
53 def __init__(self, task):
53 def __init__(self, task):
54 self.task = task
54 self.task = task
55
55
56 @LazyProperty
56 @LazyProperty
57 def result(self):
57 def result(self):
58 return self.task
58 return self.task
59
59
60
60
61 def run_task(task, *args, **kwargs):
61 def run_task(task, *args, **kwargs):
62 if CELERY_ON:
62 if CELERY_ON:
63 try:
63 try:
64 t = task.apply_async(args=args, kwargs=kwargs)
64 t = task.apply_async(args=args, kwargs=kwargs)
65 log.info('running task %s:%s' % (t.task_id, task))
65 log.info('running task %s:%s' % (t.task_id, task))
66 return t
66 return t
67
67
68 except socket.error, e:
68 except socket.error, e:
69 if isinstance(e, IOError) and e.errno == 111:
69 if isinstance(e, IOError) and e.errno == 111:
70 log.debug('Unable to connect to celeryd. Sync execution')
70 log.debug('Unable to connect to celeryd. Sync execution')
71 else:
71 else:
72 log.error(traceback.format_exc())
72 log.error(traceback.format_exc())
73 except KeyError, e:
73 except KeyError, e:
74 log.debug('Unable to connect to celeryd. Sync execution')
74 log.debug('Unable to connect to celeryd. Sync execution')
75 except Exception, e:
75 except Exception, e:
76 log.error(traceback.format_exc())
76 log.error(traceback.format_exc())
77
77
78 log.debug('executing task %s in sync mode' % task)
78 log.debug('executing task %s in sync mode' % task)
79 return ResultWrapper(task(*args, **kwargs))
79 return ResultWrapper(task(*args, **kwargs))
80
80
81
81
82 def __get_lockkey(func, *fargs, **fkwargs):
82 def __get_lockkey(func, *fargs, **fkwargs):
83 params = list(fargs)
83 params = list(fargs)
84 params.extend(['%s-%s' % ar for ar in fkwargs.items()])
84 params.extend(['%s-%s' % ar for ar in fkwargs.items()])
85
85
86 func_name = str(func.__name__) if hasattr(func, '__name__') else str(func)
86 func_name = str(func.__name__) if hasattr(func, '__name__') else str(func)
87
87
88 lockkey = 'task_%s.lock' % \
88 lockkey = 'task_%s.lock' % \
89 md5(func_name + '-' + '-'.join(map(safe_str, params))).hexdigest()
89 md5(func_name + '-' + '-'.join(map(safe_str, params))).hexdigest()
90 return lockkey
90 return lockkey
91
91
92
92
93 def locked_task(func):
93 def locked_task(func):
94 def __wrapper(func, *fargs, **fkwargs):
94 def __wrapper(func, *fargs, **fkwargs):
95 lockkey = __get_lockkey(func, *fargs, **fkwargs)
95 lockkey = __get_lockkey(func, *fargs, **fkwargs)
96 lockkey_path = config['here']
96 lockkey_path = config['here']
97
97
98 log.info('running task with lockkey %s' % lockkey)
98 log.info('running task with lockkey %s' % lockkey)
99 try:
99 try:
100 l = DaemonLock(file_=jn(lockkey_path, lockkey))
100 l = DaemonLock(file_=jn(lockkey_path, lockkey))
101 ret = func(*fargs, **fkwargs)
101 ret = func(*fargs, **fkwargs)
102 l.release()
102 l.release()
103 return ret
103 return ret
104 except LockHeld:
104 except LockHeld:
105 log.info('LockHeld')
105 log.info('LockHeld')
106 return 'Task with key %s already running' % lockkey
106 return 'Task with key %s already running' % lockkey
107
107
108 return decorator(__wrapper, func)
108 return decorator(__wrapper, func)
109
109
110
110
111 def get_session():
111 def get_session():
112 if CELERY_ON:
112 if CELERY_ON:
113 engine = engine_from_config(config, 'sqlalchemy.db1.')
113 engine = engine_from_config(config, 'sqlalchemy.db1.')
114 init_model(engine)
114 init_model(engine)
115 sa = meta.Session
115 sa = meta.Session()
116 return sa
116 return sa
117
117
118
118
119 def dbsession(func):
119 def dbsession(func):
120 def __wrapper(func, *fargs, **fkwargs):
120 def __wrapper(func, *fargs, **fkwargs):
121 try:
121 try:
122 ret = func(*fargs, **fkwargs)
122 ret = func(*fargs, **fkwargs)
123 return ret
123 return ret
124 finally:
124 finally:
125 if CELERY_ON and CELERY_EAGER is False:
125 if CELERY_ON and CELERY_EAGER is False:
126 meta.Session.remove()
126 meta.Session.remove()
127
127
128 return decorator(__wrapper, func)
128 return decorator(__wrapper, func)
@@ -1,421 +1,421 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.celerylib.tasks
3 rhodecode.lib.celerylib.tasks
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 RhodeCode task modules, containing all task that suppose to be run
6 RhodeCode task modules, containing all task that suppose to be run
7 by celery daemon
7 by celery daemon
8
8
9 :created_on: Oct 6, 2010
9 :created_on: Oct 6, 2010
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software: you can redistribute it and/or modify
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
17 # (at your option) any later version.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 from celery.decorators import task
26 from celery.decorators import task
27
27
28 import os
28 import os
29 import traceback
29 import traceback
30 import logging
30 import logging
31 from os.path import join as jn
31 from os.path import join as jn
32
32
33 from time import mktime
33 from time import mktime
34 from operator import itemgetter
34 from operator import itemgetter
35 from string import lower
35 from string import lower
36
36
37 from pylons import config, url
37 from pylons import config, url
38 from pylons.i18n.translation import _
38 from pylons.i18n.translation import _
39
39
40 from rhodecode.lib.vcs import get_backend
40 from rhodecode.lib.vcs import get_backend
41
41
42 from rhodecode import CELERY_ON, CELERY_EAGER
42 from rhodecode import CELERY_ON, CELERY_EAGER
43 from rhodecode.lib.utils2 import safe_str
43 from rhodecode.lib.utils2 import safe_str
44 from rhodecode.lib.celerylib import run_task, locked_task, dbsession, \
44 from rhodecode.lib.celerylib import run_task, locked_task, dbsession, \
45 str2bool, __get_lockkey, LockHeld, DaemonLock, get_session
45 str2bool, __get_lockkey, LockHeld, DaemonLock, get_session
46 from rhodecode.lib.helpers import person
46 from rhodecode.lib.helpers import person
47 from rhodecode.lib.rcmail.smtp_mailer import SmtpMailer
47 from rhodecode.lib.rcmail.smtp_mailer import SmtpMailer
48 from rhodecode.lib.utils import add_cache, action_logger
48 from rhodecode.lib.utils import add_cache, action_logger
49 from rhodecode.lib.compat import json, OrderedDict
49 from rhodecode.lib.compat import json, OrderedDict
50 from rhodecode.lib.hooks import log_create_repository
50 from rhodecode.lib.hooks import log_create_repository
51
51
52 from rhodecode.model.db import Statistics, Repository, User
52 from rhodecode.model.db import Statistics, Repository, User
53
53
54
54
55 add_cache(config)
55 add_cache(config)
56
56
57 __all__ = ['whoosh_index', 'get_commits_stats',
57 __all__ = ['whoosh_index', 'get_commits_stats',
58 'reset_user_password', 'send_email']
58 'reset_user_password', 'send_email']
59
59
60
60
61 def get_logger(cls):
61 def get_logger(cls):
62 if CELERY_ON:
62 if CELERY_ON:
63 try:
63 try:
64 log = cls.get_logger()
64 log = cls.get_logger()
65 except:
65 except:
66 log = logging.getLogger(__name__)
66 log = logging.getLogger(__name__)
67 else:
67 else:
68 log = logging.getLogger(__name__)
68 log = logging.getLogger(__name__)
69
69
70 return log
70 return log
71
71
72
72
73 @task(ignore_result=True)
73 @task(ignore_result=True)
74 @locked_task
74 @locked_task
75 @dbsession
75 @dbsession
76 def whoosh_index(repo_location, full_index):
76 def whoosh_index(repo_location, full_index):
77 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
77 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
78 log = get_logger(whoosh_index)
78 log = get_logger(whoosh_index)
79 DBS = get_session()
79 DBS = get_session()
80
80
81 index_location = config['index_dir']
81 index_location = config['index_dir']
82 WhooshIndexingDaemon(index_location=index_location,
82 WhooshIndexingDaemon(index_location=index_location,
83 repo_location=repo_location, sa=DBS)\
83 repo_location=repo_location, sa=DBS)\
84 .run(full_index=full_index)
84 .run(full_index=full_index)
85
85
86
86
87 @task(ignore_result=True)
87 @task(ignore_result=True)
88 @dbsession
88 @dbsession
89 def get_commits_stats(repo_name, ts_min_y, ts_max_y):
89 def get_commits_stats(repo_name, ts_min_y, ts_max_y):
90 log = get_logger(get_commits_stats)
90 log = get_logger(get_commits_stats)
91 DBS = get_session()
91 DBS = get_session()
92 lockkey = __get_lockkey('get_commits_stats', repo_name, ts_min_y,
92 lockkey = __get_lockkey('get_commits_stats', repo_name, ts_min_y,
93 ts_max_y)
93 ts_max_y)
94 lockkey_path = config['here']
94 lockkey_path = config['here']
95
95
96 log.info('running task with lockkey %s' % lockkey)
96 log.info('running task with lockkey %s' % lockkey)
97
97
98 try:
98 try:
99 lock = l = DaemonLock(file_=jn(lockkey_path, lockkey))
99 lock = l = DaemonLock(file_=jn(lockkey_path, lockkey))
100
100
101 # for js data compatibility cleans the key for person from '
101 # for js data compatibility cleans the key for person from '
102 akc = lambda k: person(k).replace('"', "")
102 akc = lambda k: person(k).replace('"', "")
103
103
104 co_day_auth_aggr = {}
104 co_day_auth_aggr = {}
105 commits_by_day_aggregate = {}
105 commits_by_day_aggregate = {}
106 repo = Repository.get_by_repo_name(repo_name)
106 repo = Repository.get_by_repo_name(repo_name)
107 if repo is None:
107 if repo is None:
108 return True
108 return True
109
109
110 repo = repo.scm_instance
110 repo = repo.scm_instance
111 repo_size = repo.count()
111 repo_size = repo.count()
112 # return if repo have no revisions
112 # return if repo have no revisions
113 if repo_size < 1:
113 if repo_size < 1:
114 lock.release()
114 lock.release()
115 return True
115 return True
116
116
117 skip_date_limit = True
117 skip_date_limit = True
118 parse_limit = int(config['app_conf'].get('commit_parse_limit'))
118 parse_limit = int(config['app_conf'].get('commit_parse_limit'))
119 last_rev = None
119 last_rev = None
120 last_cs = None
120 last_cs = None
121 timegetter = itemgetter('time')
121 timegetter = itemgetter('time')
122
122
123 dbrepo = DBS.query(Repository)\
123 dbrepo = DBS.query(Repository)\
124 .filter(Repository.repo_name == repo_name).scalar()
124 .filter(Repository.repo_name == repo_name).scalar()
125 cur_stats = DBS.query(Statistics)\
125 cur_stats = DBS.query(Statistics)\
126 .filter(Statistics.repository == dbrepo).scalar()
126 .filter(Statistics.repository == dbrepo).scalar()
127
127
128 if cur_stats is not None:
128 if cur_stats is not None:
129 last_rev = cur_stats.stat_on_revision
129 last_rev = cur_stats.stat_on_revision
130
130
131 if last_rev == repo.get_changeset().revision and repo_size > 1:
131 if last_rev == repo.get_changeset().revision and repo_size > 1:
132 # pass silently without any work if we're not on first revision or
132 # pass silently without any work if we're not on first revision or
133 # current state of parsing revision(from db marker) is the
133 # current state of parsing revision(from db marker) is the
134 # last revision
134 # last revision
135 lock.release()
135 lock.release()
136 return True
136 return True
137
137
138 if cur_stats:
138 if cur_stats:
139 commits_by_day_aggregate = OrderedDict(json.loads(
139 commits_by_day_aggregate = OrderedDict(json.loads(
140 cur_stats.commit_activity_combined))
140 cur_stats.commit_activity_combined))
141 co_day_auth_aggr = json.loads(cur_stats.commit_activity)
141 co_day_auth_aggr = json.loads(cur_stats.commit_activity)
142
142
143 log.debug('starting parsing %s' % parse_limit)
143 log.debug('starting parsing %s' % parse_limit)
144 lmktime = mktime
144 lmktime = mktime
145
145
146 last_rev = last_rev + 1 if last_rev >= 0 else 0
146 last_rev = last_rev + 1 if last_rev >= 0 else 0
147 log.debug('Getting revisions from %s to %s' % (
147 log.debug('Getting revisions from %s to %s' % (
148 last_rev, last_rev + parse_limit)
148 last_rev, last_rev + parse_limit)
149 )
149 )
150 for cs in repo[last_rev:last_rev + parse_limit]:
150 for cs in repo[last_rev:last_rev + parse_limit]:
151 log.debug('parsing %s' % cs)
151 log.debug('parsing %s' % cs)
152 last_cs = cs # remember last parsed changeset
152 last_cs = cs # remember last parsed changeset
153 k = lmktime([cs.date.timetuple()[0], cs.date.timetuple()[1],
153 k = lmktime([cs.date.timetuple()[0], cs.date.timetuple()[1],
154 cs.date.timetuple()[2], 0, 0, 0, 0, 0, 0])
154 cs.date.timetuple()[2], 0, 0, 0, 0, 0, 0])
155
155
156 if akc(cs.author) in co_day_auth_aggr:
156 if akc(cs.author) in co_day_auth_aggr:
157 try:
157 try:
158 l = [timegetter(x) for x in
158 l = [timegetter(x) for x in
159 co_day_auth_aggr[akc(cs.author)]['data']]
159 co_day_auth_aggr[akc(cs.author)]['data']]
160 time_pos = l.index(k)
160 time_pos = l.index(k)
161 except ValueError:
161 except ValueError:
162 time_pos = False
162 time_pos = False
163
163
164 if time_pos >= 0 and time_pos is not False:
164 if time_pos >= 0 and time_pos is not False:
165
165
166 datadict = \
166 datadict = \
167 co_day_auth_aggr[akc(cs.author)]['data'][time_pos]
167 co_day_auth_aggr[akc(cs.author)]['data'][time_pos]
168
168
169 datadict["commits"] += 1
169 datadict["commits"] += 1
170 datadict["added"] += len(cs.added)
170 datadict["added"] += len(cs.added)
171 datadict["changed"] += len(cs.changed)
171 datadict["changed"] += len(cs.changed)
172 datadict["removed"] += len(cs.removed)
172 datadict["removed"] += len(cs.removed)
173
173
174 else:
174 else:
175 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
175 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
176
176
177 datadict = {"time": k,
177 datadict = {"time": k,
178 "commits": 1,
178 "commits": 1,
179 "added": len(cs.added),
179 "added": len(cs.added),
180 "changed": len(cs.changed),
180 "changed": len(cs.changed),
181 "removed": len(cs.removed),
181 "removed": len(cs.removed),
182 }
182 }
183 co_day_auth_aggr[akc(cs.author)]['data']\
183 co_day_auth_aggr[akc(cs.author)]['data']\
184 .append(datadict)
184 .append(datadict)
185
185
186 else:
186 else:
187 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
187 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
188 co_day_auth_aggr[akc(cs.author)] = {
188 co_day_auth_aggr[akc(cs.author)] = {
189 "label": akc(cs.author),
189 "label": akc(cs.author),
190 "data": [{"time":k,
190 "data": [{"time":k,
191 "commits":1,
191 "commits":1,
192 "added":len(cs.added),
192 "added":len(cs.added),
193 "changed":len(cs.changed),
193 "changed":len(cs.changed),
194 "removed":len(cs.removed),
194 "removed":len(cs.removed),
195 }],
195 }],
196 "schema": ["commits"],
196 "schema": ["commits"],
197 }
197 }
198
198
199 #gather all data by day
199 #gather all data by day
200 if k in commits_by_day_aggregate:
200 if k in commits_by_day_aggregate:
201 commits_by_day_aggregate[k] += 1
201 commits_by_day_aggregate[k] += 1
202 else:
202 else:
203 commits_by_day_aggregate[k] = 1
203 commits_by_day_aggregate[k] = 1
204
204
205 overview_data = sorted(commits_by_day_aggregate.items(),
205 overview_data = sorted(commits_by_day_aggregate.items(),
206 key=itemgetter(0))
206 key=itemgetter(0))
207
207
208 if not co_day_auth_aggr:
208 if not co_day_auth_aggr:
209 co_day_auth_aggr[akc(repo.contact)] = {
209 co_day_auth_aggr[akc(repo.contact)] = {
210 "label": akc(repo.contact),
210 "label": akc(repo.contact),
211 "data": [0, 1],
211 "data": [0, 1],
212 "schema": ["commits"],
212 "schema": ["commits"],
213 }
213 }
214
214
215 stats = cur_stats if cur_stats else Statistics()
215 stats = cur_stats if cur_stats else Statistics()
216 stats.commit_activity = json.dumps(co_day_auth_aggr)
216 stats.commit_activity = json.dumps(co_day_auth_aggr)
217 stats.commit_activity_combined = json.dumps(overview_data)
217 stats.commit_activity_combined = json.dumps(overview_data)
218
218
219 log.debug('last revison %s' % last_rev)
219 log.debug('last revison %s' % last_rev)
220 leftovers = len(repo.revisions[last_rev:])
220 leftovers = len(repo.revisions[last_rev:])
221 log.debug('revisions to parse %s' % leftovers)
221 log.debug('revisions to parse %s' % leftovers)
222
222
223 if last_rev == 0 or leftovers < parse_limit:
223 if last_rev == 0 or leftovers < parse_limit:
224 log.debug('getting code trending stats')
224 log.debug('getting code trending stats')
225 stats.languages = json.dumps(__get_codes_stats(repo_name))
225 stats.languages = json.dumps(__get_codes_stats(repo_name))
226
226
227 try:
227 try:
228 stats.repository = dbrepo
228 stats.repository = dbrepo
229 stats.stat_on_revision = last_cs.revision if last_cs else 0
229 stats.stat_on_revision = last_cs.revision if last_cs else 0
230 DBS.add(stats)
230 DBS.add(stats)
231 DBS.commit()
231 DBS.commit()
232 except:
232 except:
233 log.error(traceback.format_exc())
233 log.error(traceback.format_exc())
234 DBS.rollback()
234 DBS.rollback()
235 lock.release()
235 lock.release()
236 return False
236 return False
237
237
238 # final release
238 # final release
239 lock.release()
239 lock.release()
240
240
241 # execute another task if celery is enabled
241 # execute another task if celery is enabled
242 if len(repo.revisions) > 1 and CELERY_ON:
242 if len(repo.revisions) > 1 and CELERY_ON:
243 run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y)
243 run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y)
244 return True
244 return True
245 except LockHeld:
245 except LockHeld:
246 log.info('LockHeld')
246 log.info('LockHeld')
247 return 'Task with key %s already running' % lockkey
247 return 'Task with key %s already running' % lockkey
248
248
249 @task(ignore_result=True)
249 @task(ignore_result=True)
250 @dbsession
250 @dbsession
251 def send_password_link(user_email):
251 def send_password_link(user_email):
252 from rhodecode.model.notification import EmailNotificationModel
252 from rhodecode.model.notification import EmailNotificationModel
253
253
254 log = get_logger(send_password_link)
254 log = get_logger(send_password_link)
255 DBS = get_session()
255 DBS = get_session()
256
256
257 try:
257 try:
258 user = User.get_by_email(user_email)
258 user = User.get_by_email(user_email)
259 if user:
259 if user:
260 log.debug('password reset user found %s' % user)
260 log.debug('password reset user found %s' % user)
261 link = url('reset_password_confirmation', key=user.api_key,
261 link = url('reset_password_confirmation', key=user.api_key,
262 qualified=True)
262 qualified=True)
263 reg_type = EmailNotificationModel.TYPE_PASSWORD_RESET
263 reg_type = EmailNotificationModel.TYPE_PASSWORD_RESET
264 body = EmailNotificationModel().get_email_tmpl(reg_type,
264 body = EmailNotificationModel().get_email_tmpl(reg_type,
265 **{'user':user.short_contact,
265 **{'user':user.short_contact,
266 'reset_url':link})
266 'reset_url':link})
267 log.debug('sending email')
267 log.debug('sending email')
268 run_task(send_email, user_email,
268 run_task(send_email, user_email,
269 _("password reset link"), body)
269 _("password reset link"), body)
270 log.info('send new password mail to %s' % user_email)
270 log.info('send new password mail to %s' % user_email)
271 else:
271 else:
272 log.debug("password reset email %s not found" % user_email)
272 log.debug("password reset email %s not found" % user_email)
273 except:
273 except:
274 log.error(traceback.format_exc())
274 log.error(traceback.format_exc())
275 return False
275 return False
276
276
277 return True
277 return True
278
278
279 @task(ignore_result=True)
279 @task(ignore_result=True)
280 @dbsession
280 @dbsession
281 def reset_user_password(user_email):
281 def reset_user_password(user_email):
282 from rhodecode.lib import auth
282 from rhodecode.lib import auth
283
283
284 log = get_logger(reset_user_password)
284 log = get_logger(reset_user_password)
285 DBS = get_session()
285 DBS = get_session()
286
286
287 try:
287 try:
288 try:
288 try:
289 user = User.get_by_email(user_email)
289 user = User.get_by_email(user_email)
290 new_passwd = auth.PasswordGenerator().gen_password(8,
290 new_passwd = auth.PasswordGenerator().gen_password(8,
291 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
291 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
292 if user:
292 if user:
293 user.password = auth.get_crypt_password(new_passwd)
293 user.password = auth.get_crypt_password(new_passwd)
294 user.api_key = auth.generate_api_key(user.username)
294 user.api_key = auth.generate_api_key(user.username)
295 DBS.add(user)
295 DBS.add(user)
296 DBS.commit()
296 DBS.commit()
297 log.info('change password for %s' % user_email)
297 log.info('change password for %s' % user_email)
298 if new_passwd is None:
298 if new_passwd is None:
299 raise Exception('unable to generate new password')
299 raise Exception('unable to generate new password')
300 except:
300 except:
301 log.error(traceback.format_exc())
301 log.error(traceback.format_exc())
302 DBS.rollback()
302 DBS.rollback()
303
303
304 run_task(send_email, user_email,
304 run_task(send_email, user_email,
305 'Your new password',
305 'Your new password',
306 'Your new RhodeCode password:%s' % (new_passwd))
306 'Your new RhodeCode password:%s' % (new_passwd))
307 log.info('send new password mail to %s' % user_email)
307 log.info('send new password mail to %s' % user_email)
308
308
309 except:
309 except:
310 log.error('Failed to update user password')
310 log.error('Failed to update user password')
311 log.error(traceback.format_exc())
311 log.error(traceback.format_exc())
312
312
313 return True
313 return True
314
314
315
315
316 @task(ignore_result=True)
316 @task(ignore_result=True)
317 @dbsession
317 @dbsession
318 def send_email(recipients, subject, body, html_body=''):
318 def send_email(recipients, subject, body, html_body=''):
319 """
319 """
320 Sends an email with defined parameters from the .ini files.
320 Sends an email with defined parameters from the .ini files.
321
321
322 :param recipients: list of recipients, it this is empty the defined email
322 :param recipients: list of recipients, it this is empty the defined email
323 address from field 'email_to' is used instead
323 address from field 'email_to' is used instead
324 :param subject: subject of the mail
324 :param subject: subject of the mail
325 :param body: body of the mail
325 :param body: body of the mail
326 :param html_body: html version of body
326 :param html_body: html version of body
327 """
327 """
328 log = get_logger(send_email)
328 log = get_logger(send_email)
329 DBS = get_session()
329 DBS = get_session()
330
330
331 email_config = config
331 email_config = config
332 subject = "%s %s" % (email_config.get('email_prefix', ''), subject)
332 subject = "%s %s" % (email_config.get('email_prefix', ''), subject)
333 if not recipients:
333 if not recipients:
334 # if recipients are not defined we send to email_config + all admins
334 # if recipients are not defined we send to email_config + all admins
335 admins = [u.email for u in User.query()
335 admins = [u.email for u in User.query()
336 .filter(User.admin == True).all()]
336 .filter(User.admin == True).all()]
337 recipients = [email_config.get('email_to')] + admins
337 recipients = [email_config.get('email_to')] + admins
338
338
339 mail_from = email_config.get('app_email_from', 'RhodeCode')
339 mail_from = email_config.get('app_email_from', 'RhodeCode')
340 user = email_config.get('smtp_username')
340 user = email_config.get('smtp_username')
341 passwd = email_config.get('smtp_password')
341 passwd = email_config.get('smtp_password')
342 mail_server = email_config.get('smtp_server')
342 mail_server = email_config.get('smtp_server')
343 mail_port = email_config.get('smtp_port')
343 mail_port = email_config.get('smtp_port')
344 tls = str2bool(email_config.get('smtp_use_tls'))
344 tls = str2bool(email_config.get('smtp_use_tls'))
345 ssl = str2bool(email_config.get('smtp_use_ssl'))
345 ssl = str2bool(email_config.get('smtp_use_ssl'))
346 debug = str2bool(config.get('debug'))
346 debug = str2bool(config.get('debug'))
347 smtp_auth = email_config.get('smtp_auth')
347 smtp_auth = email_config.get('smtp_auth')
348
348
349 try:
349 try:
350 m = SmtpMailer(mail_from, user, passwd, mail_server, smtp_auth,
350 m = SmtpMailer(mail_from, user, passwd, mail_server, smtp_auth,
351 mail_port, ssl, tls, debug=debug)
351 mail_port, ssl, tls, debug=debug)
352 m.send(recipients, subject, body, html_body)
352 m.send(recipients, subject, body, html_body)
353 except:
353 except:
354 log.error('Mail sending failed')
354 log.error('Mail sending failed')
355 log.error(traceback.format_exc())
355 log.error(traceback.format_exc())
356 return False
356 return False
357 return True
357 return True
358
358
359
359
360 @task(ignore_result=True)
360 @task(ignore_result=True)
361 @dbsession
361 @dbsession
362 def create_repo_fork(form_data, cur_user):
362 def create_repo_fork(form_data, cur_user):
363 """
363 """
364 Creates a fork of repository using interval VCS methods
364 Creates a fork of repository using interval VCS methods
365
365
366 :param form_data:
366 :param form_data:
367 :param cur_user:
367 :param cur_user:
368 """
368 """
369 from rhodecode.model.repo import RepoModel
369 from rhodecode.model.repo import RepoModel
370
370
371 log = get_logger(create_repo_fork)
371 log = get_logger(create_repo_fork)
372 DBS = get_session()
372 DBS = get_session()
373
373
374 base_path = Repository.base_path()
374 base_path = Repository.base_path()
375
375
376 fork_repo = RepoModel(DBS).create(form_data, cur_user,
376 fork_repo = RepoModel(DBS).create(form_data, cur_user.user_id,
377 just_db=True, fork=True)
377 just_db=True, fork=True)
378
378
379 alias = form_data['repo_type']
379 alias = form_data['repo_type']
380 org_repo_name = form_data['org_path']
380 org_repo_name = form_data['org_path']
381 fork_name = form_data['repo_name_full']
381 fork_name = form_data['repo_name_full']
382 update_after_clone = form_data['update_after_clone']
382 update_after_clone = form_data['update_after_clone']
383 source_repo_path = os.path.join(base_path, org_repo_name)
383 source_repo_path = os.path.join(base_path, org_repo_name)
384 destination_fork_path = os.path.join(base_path, fork_name)
384 destination_fork_path = os.path.join(base_path, fork_name)
385
385
386 log.info('creating fork of %s as %s', source_repo_path,
386 log.info('creating fork of %s as %s', source_repo_path,
387 destination_fork_path)
387 destination_fork_path)
388 backend = get_backend(alias)
388 backend = get_backend(alias)
389 backend(safe_str(destination_fork_path), create=True,
389 backend(safe_str(destination_fork_path), create=True,
390 src_url=safe_str(source_repo_path),
390 src_url=safe_str(source_repo_path),
391 update_after_clone=update_after_clone)
391 update_after_clone=update_after_clone)
392 log_create_repository(fork_repo.get_dict(), created_by=cur_user.username)
392 log_create_repository(fork_repo.get_dict(), created_by=cur_user.username)
393
393
394 action_logger(cur_user, 'user_forked_repo:%s' % fork_name,
394 action_logger(cur_user, 'user_forked_repo:%s' % fork_name,
395 org_repo_name, '', DBS)
395 org_repo_name, '', DBS)
396
396
397 action_logger(cur_user, 'user_created_fork:%s' % fork_name,
397 action_logger(cur_user, 'user_created_fork:%s' % fork_name,
398 fork_name, '', DBS)
398 fork_name, '', DBS)
399 # finally commit at latest possible stage
399 # finally commit at latest possible stage
400 DBS.commit()
400 DBS.commit()
401
401
402
402
403 def __get_codes_stats(repo_name):
403 def __get_codes_stats(repo_name):
404 from rhodecode.config.conf import LANGUAGES_EXTENSIONS_MAP
404 from rhodecode.config.conf import LANGUAGES_EXTENSIONS_MAP
405 repo = Repository.get_by_repo_name(repo_name).scm_instance
405 repo = Repository.get_by_repo_name(repo_name).scm_instance
406
406
407 tip = repo.get_changeset()
407 tip = repo.get_changeset()
408 code_stats = {}
408 code_stats = {}
409
409
410 def aggregate(cs):
410 def aggregate(cs):
411 for f in cs[2]:
411 for f in cs[2]:
412 ext = lower(f.extension)
412 ext = lower(f.extension)
413 if ext in LANGUAGES_EXTENSIONS_MAP.keys() and not f.is_binary:
413 if ext in LANGUAGES_EXTENSIONS_MAP.keys() and not f.is_binary:
414 if ext in code_stats:
414 if ext in code_stats:
415 code_stats[ext] += 1
415 code_stats[ext] += 1
416 else:
416 else:
417 code_stats[ext] = 1
417 code_stats[ext] = 1
418
418
419 map(aggregate, tip.walk('/'))
419 map(aggregate, tip.walk('/'))
420
420
421 return code_stats or {}
421 return code_stats or {}
@@ -1,518 +1,538 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) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 from __future__ import with_statement
25 from __future__ import with_statement
26 import os
26 import os
27 import shutil
27 import shutil
28 import logging
28 import logging
29 import traceback
29 import traceback
30 import pkg_resources
30 import pkg_resources
31 from os.path import dirname as dn, join as jn
31 from os.path import dirname as dn, join as jn
32 from datetime import datetime
32 from datetime import datetime
33
33
34 from rhodecode.lib.vcs.backends import get_backend
34 from rhodecode.lib.vcs.backends import get_backend
35 from rhodecode.lib.compat import json
35 from rhodecode.lib.compat import json
36 from rhodecode.lib.utils2 import LazyProperty, safe_str, safe_unicode
36 from rhodecode.lib.utils2 import LazyProperty, safe_str, safe_unicode
37 from rhodecode.lib.caching_query import FromCache
37 from rhodecode.lib.caching_query import FromCache
38 from rhodecode.lib.hooks import log_create_repository
38 from rhodecode.lib.hooks import log_create_repository
39
39
40 from rhodecode.model import BaseModel
40 from rhodecode.model import BaseModel
41 from rhodecode.model.db import Repository, UserRepoToPerm, User, Permission, \
41 from rhodecode.model.db import Repository, UserRepoToPerm, User, Permission, \
42 Statistics, UsersGroup, UsersGroupRepoToPerm, RhodeCodeUi, RepoGroup
42 Statistics, UsersGroup, UsersGroupRepoToPerm, RhodeCodeUi, RepoGroup
43 from rhodecode.lib import helpers as h
43 from rhodecode.lib import helpers as h
44
44
45
45
46 log = logging.getLogger(__name__)
46 log = logging.getLogger(__name__)
47
47
48
48
49 class RepoModel(BaseModel):
49 class RepoModel(BaseModel):
50
50
51 cls = Repository
51 cls = Repository
52 URL_SEPARATOR = Repository.url_sep()
52
53
53 def __get_users_group(self, users_group):
54 def __get_users_group(self, users_group):
54 return self._get_instance(UsersGroup, users_group,
55 return self._get_instance(UsersGroup, users_group,
55 callback=UsersGroup.get_by_group_name)
56 callback=UsersGroup.get_by_group_name)
56
57
57 def __get_repos_group(self, repos_group):
58 def __get_repos_group(self, repos_group):
58 return self._get_instance(RepoGroup, repos_group,
59 return self._get_instance(RepoGroup, repos_group,
59 callback=RepoGroup.get_by_group_name)
60 callback=RepoGroup.get_by_group_name)
60
61
61 @LazyProperty
62 @LazyProperty
62 def repos_path(self):
63 def repos_path(self):
63 """
64 """
64 Get's the repositories root path from database
65 Get's the repositories root path from database
65 """
66 """
66
67
67 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
68 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
68 return q.ui_value
69 return q.ui_value
69
70
70 def get(self, repo_id, cache=False):
71 def get(self, repo_id, cache=False):
71 repo = self.sa.query(Repository)\
72 repo = self.sa.query(Repository)\
72 .filter(Repository.repo_id == repo_id)
73 .filter(Repository.repo_id == repo_id)
73
74
74 if cache:
75 if cache:
75 repo = repo.options(FromCache("sql_cache_short",
76 repo = repo.options(FromCache("sql_cache_short",
76 "get_repo_%s" % repo_id))
77 "get_repo_%s" % repo_id))
77 return repo.scalar()
78 return repo.scalar()
78
79
79 def get_repo(self, repository):
80 def get_repo(self, repository):
80 return self._get_repo(repository)
81 return self._get_repo(repository)
81
82
82 def get_by_repo_name(self, repo_name, cache=False):
83 def get_by_repo_name(self, repo_name, cache=False):
83 repo = self.sa.query(Repository)\
84 repo = self.sa.query(Repository)\
84 .filter(Repository.repo_name == repo_name)
85 .filter(Repository.repo_name == repo_name)
85
86
86 if cache:
87 if cache:
87 repo = repo.options(FromCache("sql_cache_short",
88 repo = repo.options(FromCache("sql_cache_short",
88 "get_repo_%s" % repo_name))
89 "get_repo_%s" % repo_name))
89 return repo.scalar()
90 return repo.scalar()
90
91
91 def get_users_js(self):
92 def get_users_js(self):
92 users = self.sa.query(User).filter(User.active == True).all()
93 users = self.sa.query(User).filter(User.active == True).all()
93 return json.dumps([
94 return json.dumps([
94 {
95 {
95 'id': u.user_id,
96 'id': u.user_id,
96 'fname': u.name,
97 'fname': u.name,
97 'lname': u.lastname,
98 'lname': u.lastname,
98 'nname': u.username,
99 'nname': u.username,
99 'gravatar_lnk': h.gravatar_url(u.email, 14)
100 'gravatar_lnk': h.gravatar_url(u.email, 14)
100 } for u in users]
101 } for u in users]
101 )
102 )
102
103
103 def get_users_groups_js(self):
104 def get_users_groups_js(self):
104 users_groups = self.sa.query(UsersGroup)\
105 users_groups = self.sa.query(UsersGroup)\
105 .filter(UsersGroup.users_group_active == True).all()
106 .filter(UsersGroup.users_group_active == True).all()
106
107
107 return json.dumps([
108 return json.dumps([
108 {
109 {
109 'id': gr.users_group_id,
110 'id': gr.users_group_id,
110 'grname': gr.users_group_name,
111 'grname': gr.users_group_name,
111 'grmembers': len(gr.members),
112 'grmembers': len(gr.members),
112 } for gr in users_groups]
113 } for gr in users_groups]
113 )
114 )
114
115
115 def _get_defaults(self, repo_name):
116 def _get_defaults(self, repo_name):
116 """
117 """
117 Get's information about repository, and returns a dict for
118 Get's information about repository, and returns a dict for
118 usage in forms
119 usage in forms
119
120
120 :param repo_name:
121 :param repo_name:
121 """
122 """
122
123
123 repo_info = Repository.get_by_repo_name(repo_name)
124 repo_info = Repository.get_by_repo_name(repo_name)
124
125
125 if repo_info is None:
126 if repo_info is None:
126 return None
127 return None
127
128
128 defaults = repo_info.get_dict()
129 defaults = repo_info.get_dict()
129 group, repo_name = repo_info.groups_and_repo
130 group, repo_name = repo_info.groups_and_repo
130 defaults['repo_name'] = repo_name
131 defaults['repo_name'] = repo_name
131 defaults['repo_group'] = getattr(group[-1] if group else None,
132 defaults['repo_group'] = getattr(group[-1] if group else None,
132 'group_id', None)
133 'group_id', None)
133
134
134 # fill owner
135 # fill owner
135 if repo_info.user:
136 if repo_info.user:
136 defaults.update({'user': repo_info.user.username})
137 defaults.update({'user': repo_info.user.username})
137 else:
138 else:
138 replacement_user = User.query().filter(User.admin ==
139 replacement_user = User.query().filter(User.admin ==
139 True).first().username
140 True).first().username
140 defaults.update({'user': replacement_user})
141 defaults.update({'user': replacement_user})
141
142
142 # fill repository users
143 # fill repository users
143 for p in repo_info.repo_to_perm:
144 for p in repo_info.repo_to_perm:
144 defaults.update({'u_perm_%s' % p.user.username:
145 defaults.update({'u_perm_%s' % p.user.username:
145 p.permission.permission_name})
146 p.permission.permission_name})
146
147
147 # fill repository groups
148 # fill repository groups
148 for p in repo_info.users_group_to_perm:
149 for p in repo_info.users_group_to_perm:
149 defaults.update({'g_perm_%s' % p.users_group.users_group_name:
150 defaults.update({'g_perm_%s' % p.users_group.users_group_name:
150 p.permission.permission_name})
151 p.permission.permission_name})
151
152
152 return defaults
153 return defaults
153
154
154 def update(self, repo_name, form_data):
155 def update(self, repo_name, form_data):
155 try:
156 try:
156 cur_repo = self.get_by_repo_name(repo_name, cache=False)
157 cur_repo = self.get_by_repo_name(repo_name, cache=False)
157
158
158 # update permissions
159 # update permissions
159 for member, perm, member_type in form_data['perms_updates']:
160 for member, perm, member_type in form_data['perms_updates']:
160 if member_type == 'user':
161 if member_type == 'user':
161 # this updates existing one
162 # this updates existing one
162 RepoModel().grant_user_permission(
163 RepoModel().grant_user_permission(
163 repo=cur_repo, user=member, perm=perm
164 repo=cur_repo, user=member, perm=perm
164 )
165 )
165 else:
166 else:
166 RepoModel().grant_users_group_permission(
167 RepoModel().grant_users_group_permission(
167 repo=cur_repo, group_name=member, perm=perm
168 repo=cur_repo, group_name=member, perm=perm
168 )
169 )
169 # set new permissions
170 # set new permissions
170 for member, perm, member_type in form_data['perms_new']:
171 for member, perm, member_type in form_data['perms_new']:
171 if member_type == 'user':
172 if member_type == 'user':
172 RepoModel().grant_user_permission(
173 RepoModel().grant_user_permission(
173 repo=cur_repo, user=member, perm=perm
174 repo=cur_repo, user=member, perm=perm
174 )
175 )
175 else:
176 else:
176 RepoModel().grant_users_group_permission(
177 RepoModel().grant_users_group_permission(
177 repo=cur_repo, group_name=member, perm=perm
178 repo=cur_repo, group_name=member, perm=perm
178 )
179 )
179
180
180 # update current repo
181 # update current repo
181 for k, v in form_data.items():
182 for k, v in form_data.items():
182 if k == 'user':
183 if k == 'user':
183 cur_repo.user = User.get_by_username(v)
184 cur_repo.user = User.get_by_username(v)
184 elif k == 'repo_name':
185 elif k == 'repo_name':
185 pass
186 pass
186 elif k == 'repo_group':
187 elif k == 'repo_group':
187 cur_repo.group = RepoGroup.get(v)
188 cur_repo.group = RepoGroup.get(v)
188
189
189 else:
190 else:
190 setattr(cur_repo, k, v)
191 setattr(cur_repo, k, v)
191
192
192 new_name = cur_repo.get_new_name(form_data['repo_name'])
193 new_name = cur_repo.get_new_name(form_data['repo_name'])
193 cur_repo.repo_name = new_name
194 cur_repo.repo_name = new_name
194
195
195 self.sa.add(cur_repo)
196 self.sa.add(cur_repo)
196
197
197 if repo_name != new_name:
198 if repo_name != new_name:
198 # rename repository
199 # rename repository
199 self.__rename_repo(old=repo_name, new=new_name)
200 self.__rename_repo(old=repo_name, new=new_name)
200
201
201 return cur_repo
202 return cur_repo
202 except:
203 except:
203 log.error(traceback.format_exc())
204 log.error(traceback.format_exc())
204 raise
205 raise
205
206
206 def create(self, form_data, cur_user, just_db=False, fork=False):
207 def create_repo(self, repo_name, repo_type, description, owner,
208 private=False, clone_uri=None, repos_group=None,
209 landing_rev='tip', just_db=False, fork_of=None,
210 copy_fork_permissions=False):
207 from rhodecode.model.scm import ScmModel
211 from rhodecode.model.scm import ScmModel
208
212
213 owner = self._get_user(owner)
214 fork_of = self._get_repo(fork_of)
215 repo_group = self.__get_repos_group(repos_group)
209 try:
216 try:
210 if fork:
211 fork_parent_id = form_data['fork_parent_id']
212
217
213 # repo name is just a name of repository
218 # repo name is just a name of repository
214 # while repo_name_full is a full qualified name that is combined
219 # while repo_name_full is a full qualified name that is combined
215 # with name and path of group
220 # with name and path of group
216 repo_name = form_data['repo_name']
221 repo_name = repo_name.split(self.URL_SEPARATOR)[-1]
217 repo_name_full = form_data['repo_name_full']
222 repo_name_full = repo_name
218
223
219 new_repo = Repository()
224 new_repo = Repository()
220 new_repo.enable_statistics = False
225 new_repo.enable_statistics = False
226 new_repo.repo_name = repo_name_full
227 new_repo.repo_type = repo_type
228 new_repo.user = owner
229 new_repo.group = repo_group
230 new_repo.description = description or repo_name
231 new_repo.private = private
232 new_repo.clone_uri = clone_uri
233 new_repo.landing_rev = landing_rev
221
234
222 for k, v in form_data.items():
235 if fork_of:
223 if k == 'repo_name':
236 parent_repo = fork_of
224 v = repo_name_full
225 if k == 'repo_group':
226 k = 'group_id'
227 if k == 'description':
228 v = v or repo_name
229
230 setattr(new_repo, k, v)
231
232 if fork:
233 parent_repo = Repository.get(fork_parent_id)
234 new_repo.fork = parent_repo
237 new_repo.fork = parent_repo
235
238
236 new_repo.user_id = cur_user.user_id
237 self.sa.add(new_repo)
239 self.sa.add(new_repo)
238
240
239 def _create_default_perms():
241 def _create_default_perms():
240 # create default permission
242 # create default permission
241 repo_to_perm = UserRepoToPerm()
243 repo_to_perm = UserRepoToPerm()
242 default = 'repository.read'
244 default = 'repository.read'
243 for p in User.get_by_username('default').user_perms:
245 for p in User.get_by_username('default').user_perms:
244 if p.permission.permission_name.startswith('repository.'):
246 if p.permission.permission_name.startswith('repository.'):
245 default = p.permission.permission_name
247 default = p.permission.permission_name
246 break
248 break
247
249
248 default_perm = 'repository.none' if form_data['private'] else default
250 default_perm = 'repository.none' if private else default
249
251
250 repo_to_perm.permission_id = self.sa.query(Permission)\
252 repo_to_perm.permission_id = self.sa.query(Permission)\
251 .filter(Permission.permission_name == default_perm)\
253 .filter(Permission.permission_name == default_perm)\
252 .one().permission_id
254 .one().permission_id
253
255
254 repo_to_perm.repository = new_repo
256 repo_to_perm.repository = new_repo
255 repo_to_perm.user_id = User.get_by_username('default').user_id
257 repo_to_perm.user_id = User.get_by_username('default').user_id
256
258
257 self.sa.add(repo_to_perm)
259 self.sa.add(repo_to_perm)
258
260
259 if fork:
261 if fork_of:
260 if form_data.get('copy_permissions'):
262 if copy_fork_permissions:
261 repo = Repository.get(fork_parent_id)
263 repo = fork_of
262 user_perms = UserRepoToPerm.query()\
264 user_perms = UserRepoToPerm.query()\
263 .filter(UserRepoToPerm.repository == repo).all()
265 .filter(UserRepoToPerm.repository == repo).all()
264 group_perms = UsersGroupRepoToPerm.query()\
266 group_perms = UsersGroupRepoToPerm.query()\
265 .filter(UsersGroupRepoToPerm.repository == repo).all()
267 .filter(UsersGroupRepoToPerm.repository == repo).all()
266
268
267 for perm in user_perms:
269 for perm in user_perms:
268 UserRepoToPerm.create(perm.user, new_repo,
270 UserRepoToPerm.create(perm.user, new_repo,
269 perm.permission)
271 perm.permission)
270
272
271 for perm in group_perms:
273 for perm in group_perms:
272 UsersGroupRepoToPerm.create(perm.users_group, new_repo,
274 UsersGroupRepoToPerm.create(perm.users_group, new_repo,
273 perm.permission)
275 perm.permission)
274 else:
276 else:
275 _create_default_perms()
277 _create_default_perms()
276 else:
278 else:
277 _create_default_perms()
279 _create_default_perms()
278
280
279 if not just_db:
281 if not just_db:
280 self.__create_repo(repo_name, form_data['repo_type'],
282 self.__create_repo(repo_name, repo_type,
281 form_data['repo_group'],
283 repo_group,
282 form_data['clone_uri'])
284 clone_uri)
283 log_create_repository(new_repo.get_dict(),
285 log_create_repository(new_repo.get_dict(),
284 created_by=cur_user.username)
286 created_by=owner.username)
285
287
286 # now automatically start following this repository as owner
288 # now automatically start following this repository as owner
287 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
289 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
288 cur_user.user_id)
290 owner.user_id)
289 return new_repo
291 return new_repo
290 except:
292 except:
291 log.error(traceback.format_exc())
293 log.error(traceback.format_exc())
292 raise
294 raise
293
295
296 def create(self, form_data, cur_user, just_db=False, fork=None):
297
298 repo_name = form_data['repo_name_full']
299 repo_type = form_data['repo_type']
300 description = form_data['description']
301 owner = cur_user
302 private = form_data['private']
303 clone_uri = form_data.get('clone_uri')
304 repos_group = form_data['repo_group']
305 landing_rev = form_data['landing_rev']
306 copy_fork_permissions = form_data.get('copy_permissions')
307 fork_of = form_data.get('fork_parent_id')
308 return self.create_repo(
309 repo_name, repo_type, description, owner, private, clone_uri,
310 repos_group, landing_rev, just_db, fork_of, copy_fork_permissions
311 )
312
294 def create_fork(self, form_data, cur_user):
313 def create_fork(self, form_data, cur_user):
295 """
314 """
296 Simple wrapper into executing celery task for fork creation
315 Simple wrapper into executing celery task for fork creation
297
316
298 :param form_data:
317 :param form_data:
299 :param cur_user:
318 :param cur_user:
300 """
319 """
301 from rhodecode.lib.celerylib import tasks, run_task
320 from rhodecode.lib.celerylib import tasks, run_task
302 run_task(tasks.create_repo_fork, form_data, cur_user)
321 run_task(tasks.create_repo_fork, form_data, cur_user)
303
322
304 def delete(self, repo):
323 def delete(self, repo):
305 repo = self._get_repo(repo)
324 repo = self._get_repo(repo)
306 try:
325 if repo:
307 self.sa.delete(repo)
326 try:
308 self.__delete_repo(repo)
327 self.sa.delete(repo)
309 except:
328 self.__delete_repo(repo)
310 log.error(traceback.format_exc())
329 except:
311 raise
330 log.error(traceback.format_exc())
331 raise
312
332
313 def grant_user_permission(self, repo, user, perm):
333 def grant_user_permission(self, repo, user, perm):
314 """
334 """
315 Grant permission for user on given repository, or update existing one
335 Grant permission for user on given repository, or update existing one
316 if found
336 if found
317
337
318 :param repo: Instance of Repository, repository_id, or repository name
338 :param repo: Instance of Repository, repository_id, or repository name
319 :param user: Instance of User, user_id or username
339 :param user: Instance of User, user_id or username
320 :param perm: Instance of Permission, or permission_name
340 :param perm: Instance of Permission, or permission_name
321 """
341 """
322 user = self._get_user(user)
342 user = self._get_user(user)
323 repo = self._get_repo(repo)
343 repo = self._get_repo(repo)
324 permission = self._get_perm(perm)
344 permission = self._get_perm(perm)
325
345
326 # check if we have that permission already
346 # check if we have that permission already
327 obj = self.sa.query(UserRepoToPerm)\
347 obj = self.sa.query(UserRepoToPerm)\
328 .filter(UserRepoToPerm.user == user)\
348 .filter(UserRepoToPerm.user == user)\
329 .filter(UserRepoToPerm.repository == repo)\
349 .filter(UserRepoToPerm.repository == repo)\
330 .scalar()
350 .scalar()
331 if obj is None:
351 if obj is None:
332 # create new !
352 # create new !
333 obj = UserRepoToPerm()
353 obj = UserRepoToPerm()
334 obj.repository = repo
354 obj.repository = repo
335 obj.user = user
355 obj.user = user
336 obj.permission = permission
356 obj.permission = permission
337 self.sa.add(obj)
357 self.sa.add(obj)
338
358
339 def revoke_user_permission(self, repo, user):
359 def revoke_user_permission(self, repo, user):
340 """
360 """
341 Revoke permission for user on given repository
361 Revoke permission for user on given repository
342
362
343 :param repo: Instance of Repository, repository_id, or repository name
363 :param repo: Instance of Repository, repository_id, or repository name
344 :param user: Instance of User, user_id or username
364 :param user: Instance of User, user_id or username
345 """
365 """
346
366
347 user = self._get_user(user)
367 user = self._get_user(user)
348 repo = self._get_repo(repo)
368 repo = self._get_repo(repo)
349
369
350 obj = self.sa.query(UserRepoToPerm)\
370 obj = self.sa.query(UserRepoToPerm)\
351 .filter(UserRepoToPerm.repository == repo)\
371 .filter(UserRepoToPerm.repository == repo)\
352 .filter(UserRepoToPerm.user == user)\
372 .filter(UserRepoToPerm.user == user)\
353 .one()
373 .one()
354 self.sa.delete(obj)
374 self.sa.delete(obj)
355
375
356 def grant_users_group_permission(self, repo, group_name, perm):
376 def grant_users_group_permission(self, repo, group_name, perm):
357 """
377 """
358 Grant permission for users group on given repository, or update
378 Grant permission for users group on given repository, or update
359 existing one if found
379 existing one if found
360
380
361 :param repo: Instance of Repository, repository_id, or repository name
381 :param repo: Instance of Repository, repository_id, or repository name
362 :param group_name: Instance of UserGroup, users_group_id,
382 :param group_name: Instance of UserGroup, users_group_id,
363 or users group name
383 or users group name
364 :param perm: Instance of Permission, or permission_name
384 :param perm: Instance of Permission, or permission_name
365 """
385 """
366 repo = self._get_repo(repo)
386 repo = self._get_repo(repo)
367 group_name = self.__get_users_group(group_name)
387 group_name = self.__get_users_group(group_name)
368 permission = self._get_perm(perm)
388 permission = self._get_perm(perm)
369
389
370 # check if we have that permission already
390 # check if we have that permission already
371 obj = self.sa.query(UsersGroupRepoToPerm)\
391 obj = self.sa.query(UsersGroupRepoToPerm)\
372 .filter(UsersGroupRepoToPerm.users_group == group_name)\
392 .filter(UsersGroupRepoToPerm.users_group == group_name)\
373 .filter(UsersGroupRepoToPerm.repository == repo)\
393 .filter(UsersGroupRepoToPerm.repository == repo)\
374 .scalar()
394 .scalar()
375
395
376 if obj is None:
396 if obj is None:
377 # create new
397 # create new
378 obj = UsersGroupRepoToPerm()
398 obj = UsersGroupRepoToPerm()
379
399
380 obj.repository = repo
400 obj.repository = repo
381 obj.users_group = group_name
401 obj.users_group = group_name
382 obj.permission = permission
402 obj.permission = permission
383 self.sa.add(obj)
403 self.sa.add(obj)
384
404
385 def revoke_users_group_permission(self, repo, group_name):
405 def revoke_users_group_permission(self, repo, group_name):
386 """
406 """
387 Revoke permission for users group on given repository
407 Revoke permission for users group on given repository
388
408
389 :param repo: Instance of Repository, repository_id, or repository name
409 :param repo: Instance of Repository, repository_id, or repository name
390 :param group_name: Instance of UserGroup, users_group_id,
410 :param group_name: Instance of UserGroup, users_group_id,
391 or users group name
411 or users group name
392 """
412 """
393 repo = self._get_repo(repo)
413 repo = self._get_repo(repo)
394 group_name = self.__get_users_group(group_name)
414 group_name = self.__get_users_group(group_name)
395
415
396 obj = self.sa.query(UsersGroupRepoToPerm)\
416 obj = self.sa.query(UsersGroupRepoToPerm)\
397 .filter(UsersGroupRepoToPerm.repository == repo)\
417 .filter(UsersGroupRepoToPerm.repository == repo)\
398 .filter(UsersGroupRepoToPerm.users_group == group_name)\
418 .filter(UsersGroupRepoToPerm.users_group == group_name)\
399 .one()
419 .one()
400 self.sa.delete(obj)
420 self.sa.delete(obj)
401
421
402 def delete_stats(self, repo_name):
422 def delete_stats(self, repo_name):
403 """
423 """
404 removes stats for given repo
424 removes stats for given repo
405
425
406 :param repo_name:
426 :param repo_name:
407 """
427 """
408 try:
428 try:
409 obj = self.sa.query(Statistics)\
429 obj = self.sa.query(Statistics)\
410 .filter(Statistics.repository ==
430 .filter(Statistics.repository ==
411 self.get_by_repo_name(repo_name))\
431 self.get_by_repo_name(repo_name))\
412 .one()
432 .one()
413 self.sa.delete(obj)
433 self.sa.delete(obj)
414 except:
434 except:
415 log.error(traceback.format_exc())
435 log.error(traceback.format_exc())
416 raise
436 raise
417
437
418 def __create_repo(self, repo_name, alias, new_parent_id, clone_uri=False):
438 def __create_repo(self, repo_name, alias, new_parent_id, clone_uri=False):
419 """
439 """
420 makes repository on filesystem. It's group aware means it'll create
440 makes repository on filesystem. It's group aware means it'll create
421 a repository within a group, and alter the paths accordingly of
441 a repository within a group, and alter the paths accordingly of
422 group location
442 group location
423
443
424 :param repo_name:
444 :param repo_name:
425 :param alias:
445 :param alias:
426 :param parent_id:
446 :param parent_id:
427 :param clone_uri:
447 :param clone_uri:
428 """
448 """
429 from rhodecode.lib.utils import is_valid_repo, is_valid_repos_group
449 from rhodecode.lib.utils import is_valid_repo, is_valid_repos_group
430
450
431 if new_parent_id:
451 if new_parent_id:
432 paths = RepoGroup.get(new_parent_id)\
452 paths = RepoGroup.get(new_parent_id)\
433 .full_path.split(RepoGroup.url_sep())
453 .full_path.split(RepoGroup.url_sep())
434 new_parent_path = os.sep.join(paths)
454 new_parent_path = os.sep.join(paths)
435 else:
455 else:
436 new_parent_path = ''
456 new_parent_path = ''
437
457
438 # we need to make it str for mercurial
458 # we need to make it str for mercurial
439 repo_path = os.path.join(*map(lambda x: safe_str(x),
459 repo_path = os.path.join(*map(lambda x: safe_str(x),
440 [self.repos_path, new_parent_path, repo_name]))
460 [self.repos_path, new_parent_path, repo_name]))
441
461
442 # check if this path is not a repository
462 # check if this path is not a repository
443 if is_valid_repo(repo_path, self.repos_path):
463 if is_valid_repo(repo_path, self.repos_path):
444 raise Exception('This path %s is a valid repository' % repo_path)
464 raise Exception('This path %s is a valid repository' % repo_path)
445
465
446 # check if this path is a group
466 # check if this path is a group
447 if is_valid_repos_group(repo_path, self.repos_path):
467 if is_valid_repos_group(repo_path, self.repos_path):
448 raise Exception('This path %s is a valid group' % repo_path)
468 raise Exception('This path %s is a valid group' % repo_path)
449
469
450 log.info('creating repo %s in %s @ %s' % (
470 log.info('creating repo %s in %s @ %s' % (
451 repo_name, safe_unicode(repo_path), clone_uri
471 repo_name, safe_unicode(repo_path), clone_uri
452 )
472 )
453 )
473 )
454 backend = get_backend(alias)
474 backend = get_backend(alias)
455 if alias == 'hg':
475 if alias == 'hg':
456 backend(repo_path, create=True, src_url=clone_uri)
476 backend(repo_path, create=True, src_url=clone_uri)
457 elif alias == 'git':
477 elif alias == 'git':
458 r = backend(repo_path, create=True, src_url=clone_uri, bare=True)
478 r = backend(repo_path, create=True, src_url=clone_uri, bare=True)
459 # add rhodecode hook into this repo
479 # add rhodecode hook into this repo
460
480
461 loc = jn(r.path, 'hooks')
481 loc = jn(r.path, 'hooks')
462 if not r.bare:
482 if not r.bare:
463 loc = jn(r.path, '.git', 'hooks')
483 loc = jn(r.path, '.git', 'hooks')
464 if not os.path.isdir(loc):
484 if not os.path.isdir(loc):
465 os.makedirs(loc)
485 os.makedirs(loc)
466
486
467 tmpl = pkg_resources.resource_string(
487 tmpl = pkg_resources.resource_string(
468 'rhodecode', jn('config', 'post_receive_tmpl.py')
488 'rhodecode', jn('config', 'post_receive_tmpl.py')
469 )
489 )
470 _hook_file = jn(loc, 'post-receive')
490 _hook_file = jn(loc, 'post-receive')
471 with open(_hook_file, 'wb') as f:
491 with open(_hook_file, 'wb') as f:
472 f.write(tmpl)
492 f.write(tmpl)
473 os.chmod(_hook_file, 0755)
493 os.chmod(_hook_file, 0755)
474
494
475 else:
495 else:
476 raise Exception('Undefined alias %s' % alias)
496 raise Exception('Undefined alias %s' % alias)
477
497
478 def __rename_repo(self, old, new):
498 def __rename_repo(self, old, new):
479 """
499 """
480 renames repository on filesystem
500 renames repository on filesystem
481
501
482 :param old: old name
502 :param old: old name
483 :param new: new name
503 :param new: new name
484 """
504 """
485 log.info('renaming repo from %s to %s' % (old, new))
505 log.info('renaming repo from %s to %s' % (old, new))
486
506
487 old_path = os.path.join(self.repos_path, old)
507 old_path = os.path.join(self.repos_path, old)
488 new_path = os.path.join(self.repos_path, new)
508 new_path = os.path.join(self.repos_path, new)
489 if os.path.isdir(new_path):
509 if os.path.isdir(new_path):
490 raise Exception(
510 raise Exception(
491 'Was trying to rename to already existing dir %s' % new_path
511 'Was trying to rename to already existing dir %s' % new_path
492 )
512 )
493 shutil.move(old_path, new_path)
513 shutil.move(old_path, new_path)
494
514
495 def __delete_repo(self, repo):
515 def __delete_repo(self, repo):
496 """
516 """
497 removes repo from filesystem, the removal is acctually made by
517 removes repo from filesystem, the removal is acctually made by
498 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
518 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
499 repository is no longer valid for rhodecode, can be undeleted later on
519 repository is no longer valid for rhodecode, can be undeleted later on
500 by reverting the renames on this repository
520 by reverting the renames on this repository
501
521
502 :param repo: repo object
522 :param repo: repo object
503 """
523 """
504 rm_path = os.path.join(self.repos_path, repo.repo_name)
524 rm_path = os.path.join(self.repos_path, repo.repo_name)
505 log.info("Removing %s" % (rm_path))
525 log.info("Removing %s" % (rm_path))
506 # disable hg/git internal that it doesn't get detected as repo
526 # disable hg/git internal that it doesn't get detected as repo
507 alias = repo.repo_type
527 alias = repo.repo_type
508
528
509 bare = getattr(repo.scm_instance, 'bare', False)
529 bare = getattr(repo.scm_instance, 'bare', False)
510
530
511 if not bare:
531 if not bare:
512 # skip this for bare git repos
532 # skip this for bare git repos
513 shutil.move(os.path.join(rm_path, '.%s' % alias),
533 shutil.move(os.path.join(rm_path, '.%s' % alias),
514 os.path.join(rm_path, 'rm__.%s' % alias))
534 os.path.join(rm_path, 'rm__.%s' % alias))
515 # disable repo
535 # disable repo
516 _d = 'rm__%s__%s' % (datetime.now().strftime('%Y%m%d_%H%M%S_%f'),
536 _d = 'rm__%s__%s' % (datetime.now().strftime('%Y%m%d_%H%M%S_%f'),
517 repo.repo_name)
537 repo.repo_name)
518 shutil.move(rm_path, os.path.join(self.repos_path, _d))
538 shutil.move(rm_path, os.path.join(self.repos_path, _d))
@@ -1,192 +1,194 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.users_group
3 rhodecode.model.users_group
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 users group model for RhodeCode
6 users group model for RhodeCode
7
7
8 :created_on: Oct 1, 2011
8 :created_on: Oct 1, 2011
9 :author: nvinot
9 :author: nvinot
10 :copyright: (C) 2011-2011 Nicolas Vinot <aeris@imirhil.fr>
10 :copyright: (C) 2011-2011 Nicolas Vinot <aeris@imirhil.fr>
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software: you can redistribute it and/or modify
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
17 # (at your option) any later version.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26
26
27 import logging
27 import logging
28 import traceback
28 import traceback
29
29
30 from rhodecode.model import BaseModel
30 from rhodecode.model import BaseModel
31 from rhodecode.model.db import UsersGroupMember, UsersGroup,\
31 from rhodecode.model.db import UsersGroupMember, UsersGroup,\
32 UsersGroupRepoToPerm, Permission, UsersGroupToPerm, User
32 UsersGroupRepoToPerm, Permission, UsersGroupToPerm, User
33 from rhodecode.lib.exceptions import UsersGroupsAssignedException
33 from rhodecode.lib.exceptions import UsersGroupsAssignedException
34
34
35 log = logging.getLogger(__name__)
35 log = logging.getLogger(__name__)
36
36
37
37
38 class UsersGroupModel(BaseModel):
38 class UsersGroupModel(BaseModel):
39
39
40 cls = UsersGroup
41
40 def __get_users_group(self, users_group):
42 def __get_users_group(self, users_group):
41 return self._get_instance(UsersGroup, users_group,
43 return self._get_instance(UsersGroup, users_group,
42 callback=UsersGroup.get_by_group_name)
44 callback=UsersGroup.get_by_group_name)
43
45
44 def get(self, users_group_id, cache=False):
46 def get(self, users_group_id, cache=False):
45 return UsersGroup.get(users_group_id)
47 return UsersGroup.get(users_group_id)
46
48
47 def get_group(self, users_group):
49 def get_group(self, users_group):
48 return self.__get_users_group(users_group)
50 return self.__get_users_group(users_group)
49
51
50 def get_by_name(self, name, cache=False, case_insensitive=False):
52 def get_by_name(self, name, cache=False, case_insensitive=False):
51 return UsersGroup.get_by_group_name(name, cache, case_insensitive)
53 return UsersGroup.get_by_group_name(name, cache, case_insensitive)
52
54
53 def create(self, name, active=True):
55 def create(self, name, active=True):
54 try:
56 try:
55 new = UsersGroup()
57 new = UsersGroup()
56 new.users_group_name = name
58 new.users_group_name = name
57 new.users_group_active = active
59 new.users_group_active = active
58 self.sa.add(new)
60 self.sa.add(new)
59 return new
61 return new
60 except:
62 except:
61 log.error(traceback.format_exc())
63 log.error(traceback.format_exc())
62 raise
64 raise
63
65
64 def update(self, users_group, form_data):
66 def update(self, users_group, form_data):
65
67
66 try:
68 try:
67 users_group = self.__get_users_group(users_group)
69 users_group = self.__get_users_group(users_group)
68
70
69 for k, v in form_data.items():
71 for k, v in form_data.items():
70 if k == 'users_group_members':
72 if k == 'users_group_members':
71 users_group.members = []
73 users_group.members = []
72 self.sa.flush()
74 self.sa.flush()
73 members_list = []
75 members_list = []
74 if v:
76 if v:
75 v = [v] if isinstance(v, basestring) else v
77 v = [v] if isinstance(v, basestring) else v
76 for u_id in set(v):
78 for u_id in set(v):
77 member = UsersGroupMember(users_group.users_group_id, u_id)
79 member = UsersGroupMember(users_group.users_group_id, u_id)
78 members_list.append(member)
80 members_list.append(member)
79 setattr(users_group, 'members', members_list)
81 setattr(users_group, 'members', members_list)
80 setattr(users_group, k, v)
82 setattr(users_group, k, v)
81
83
82 self.sa.add(users_group)
84 self.sa.add(users_group)
83 except:
85 except:
84 log.error(traceback.format_exc())
86 log.error(traceback.format_exc())
85 raise
87 raise
86
88
87 def delete(self, users_group, force=False):
89 def delete(self, users_group, force=False):
88 """
90 """
89 Deletes repos group, unless force flag is used
91 Deletes repos group, unless force flag is used
90 raises exception if there are members in that group, else deletes
92 raises exception if there are members in that group, else deletes
91 group and users
93 group and users
92
94
93 :param users_group:
95 :param users_group:
94 :param force:
96 :param force:
95 """
97 """
96 try:
98 try:
97 users_group = self.__get_users_group(users_group)
99 users_group = self.__get_users_group(users_group)
98
100
99 # check if this group is not assigned to repo
101 # check if this group is not assigned to repo
100 assigned_groups = UsersGroupRepoToPerm.query()\
102 assigned_groups = UsersGroupRepoToPerm.query()\
101 .filter(UsersGroupRepoToPerm.users_group == users_group).all()
103 .filter(UsersGroupRepoToPerm.users_group == users_group).all()
102
104
103 if assigned_groups and force is False:
105 if assigned_groups and force is False:
104 raise UsersGroupsAssignedException('RepoGroup assigned to %s' %
106 raise UsersGroupsAssignedException('RepoGroup assigned to %s' %
105 assigned_groups)
107 assigned_groups)
106
108
107 self.sa.delete(users_group)
109 self.sa.delete(users_group)
108 except:
110 except:
109 log.error(traceback.format_exc())
111 log.error(traceback.format_exc())
110 raise
112 raise
111
113
112 def add_user_to_group(self, users_group, user):
114 def add_user_to_group(self, users_group, user):
113 users_group = self.__get_users_group(users_group)
115 users_group = self.__get_users_group(users_group)
114 user = self._get_user(user)
116 user = self._get_user(user)
115
117
116 for m in users_group.members:
118 for m in users_group.members:
117 u = m.user
119 u = m.user
118 if u.user_id == user.user_id:
120 if u.user_id == user.user_id:
119 return True
121 return True
120
122
121 try:
123 try:
122 users_group_member = UsersGroupMember()
124 users_group_member = UsersGroupMember()
123 users_group_member.user = user
125 users_group_member.user = user
124 users_group_member.users_group = users_group
126 users_group_member.users_group = users_group
125
127
126 users_group.members.append(users_group_member)
128 users_group.members.append(users_group_member)
127 user.group_member.append(users_group_member)
129 user.group_member.append(users_group_member)
128
130
129 self.sa.add(users_group_member)
131 self.sa.add(users_group_member)
130 return users_group_member
132 return users_group_member
131 except:
133 except:
132 log.error(traceback.format_exc())
134 log.error(traceback.format_exc())
133 raise
135 raise
134
136
135 def remove_user_from_group(self, users_group, user):
137 def remove_user_from_group(self, users_group, user):
136 users_group = self.__get_users_group(users_group)
138 users_group = self.__get_users_group(users_group)
137 user = self._get_user(user)
139 user = self._get_user(user)
138
140
139 users_group_member = None
141 users_group_member = None
140 for m in users_group.members:
142 for m in users_group.members:
141 if m.user.user_id == user.user_id:
143 if m.user.user_id == user.user_id:
142 # Found this user's membership row
144 # Found this user's membership row
143 users_group_member = m
145 users_group_member = m
144 break
146 break
145
147
146 if users_group_member:
148 if users_group_member:
147 try:
149 try:
148 self.sa.delete(users_group_member)
150 self.sa.delete(users_group_member)
149 return True
151 return True
150 except:
152 except:
151 log.error(traceback.format_exc())
153 log.error(traceback.format_exc())
152 raise
154 raise
153 else:
155 else:
154 # User isn't in that group
156 # User isn't in that group
155 return False
157 return False
156
158
157 def has_perm(self, users_group, perm):
159 def has_perm(self, users_group, perm):
158 users_group = self.__get_users_group(users_group)
160 users_group = self.__get_users_group(users_group)
159 perm = self._get_perm(perm)
161 perm = self._get_perm(perm)
160
162
161 return UsersGroupToPerm.query()\
163 return UsersGroupToPerm.query()\
162 .filter(UsersGroupToPerm.users_group == users_group)\
164 .filter(UsersGroupToPerm.users_group == users_group)\
163 .filter(UsersGroupToPerm.permission == perm).scalar() is not None
165 .filter(UsersGroupToPerm.permission == perm).scalar() is not None
164
166
165 def grant_perm(self, users_group, perm):
167 def grant_perm(self, users_group, perm):
166 if not isinstance(perm, Permission):
168 if not isinstance(perm, Permission):
167 raise Exception('perm needs to be an instance of Permission class')
169 raise Exception('perm needs to be an instance of Permission class')
168
170
169 users_group = self.__get_users_group(users_group)
171 users_group = self.__get_users_group(users_group)
170
172
171 # if this permission is already granted skip it
173 # if this permission is already granted skip it
172 _perm = UsersGroupToPerm.query()\
174 _perm = UsersGroupToPerm.query()\
173 .filter(UsersGroupToPerm.users_group == users_group)\
175 .filter(UsersGroupToPerm.users_group == users_group)\
174 .filter(UsersGroupToPerm.permission == perm)\
176 .filter(UsersGroupToPerm.permission == perm)\
175 .scalar()
177 .scalar()
176 if _perm:
178 if _perm:
177 return
179 return
178
180
179 new = UsersGroupToPerm()
181 new = UsersGroupToPerm()
180 new.users_group = users_group
182 new.users_group = users_group
181 new.permission = perm
183 new.permission = perm
182 self.sa.add(new)
184 self.sa.add(new)
183
185
184 def revoke_perm(self, users_group, perm):
186 def revoke_perm(self, users_group, perm):
185 users_group = self.__get_users_group(users_group)
187 users_group = self.__get_users_group(users_group)
186 perm = self._get_perm(perm)
188 perm = self._get_perm(perm)
187
189
188 obj = UsersGroupToPerm.query()\
190 obj = UsersGroupToPerm.query()\
189 .filter(UsersGroupToPerm.users_group == users_group)\
191 .filter(UsersGroupToPerm.users_group == users_group)\
190 .filter(UsersGroupToPerm.permission == perm).scalar()
192 .filter(UsersGroupToPerm.permission == perm).scalar()
191 if obj:
193 if obj:
192 self.sa.delete(obj)
194 self.sa.delete(obj)
General Comments 0
You need to be logged in to leave comments. Login now