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