comparison rhodecode/model/db.py @ 2031:82a88013a3fd

merge 1.3 into stable
author Marcin Kuzminski <marcin@python-works.com>
date Sun, 26 Feb 2012 17:25:09 +0200
parents a1b8bd86c488 6020e3884a58
children 9ab21c5ddb84
comparison
equal deleted inserted replaced
2005:ab0e122b38a7 2031:82a88013a3fd
5 5
6 Database Models for RhodeCode 6 Database Models for RhodeCode
7 7
8 :created_on: Apr 08, 2010 8 :created_on: Apr 08, 2010
9 :author: marcink 9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> 10 :copyright: (C) 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
25 25
26 import os 26 import os
27 import logging 27 import logging
28 import datetime 28 import datetime
29 import traceback 29 import traceback
30 from datetime import date 30 from collections import defaultdict
31 31
32 from sqlalchemy import * 32 from sqlalchemy import *
33 from sqlalchemy.ext.hybrid import hybrid_property 33 from sqlalchemy.ext.hybrid import hybrid_property
34 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates 34 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
35 from beaker.cache import cache_region, region_invalidate 35 from beaker.cache import cache_region, region_invalidate
36 36
37 from vcs import get_backend 37 from rhodecode.lib.vcs import get_backend
38 from vcs.utils.helpers import get_scm 38 from rhodecode.lib.vcs.utils.helpers import get_scm
39 from vcs.exceptions import VCSError 39 from rhodecode.lib.vcs.exceptions import VCSError
40 from vcs.utils.lazy import LazyProperty 40 from rhodecode.lib.vcs.utils.lazy import LazyProperty
41 41
42 from rhodecode.lib import str2bool, safe_str, get_changeset_safe, \ 42 from rhodecode.lib import str2bool, safe_str, get_changeset_safe, safe_unicode
43 generate_api_key, safe_unicode
44 from rhodecode.lib.exceptions import UsersGroupsAssignedException
45 from rhodecode.lib.compat import json 43 from rhodecode.lib.compat import json
44 from rhodecode.lib.caching_query import FromCache
46 45
47 from rhodecode.model.meta import Base, Session 46 from rhodecode.model.meta import Base, Session
48 from rhodecode.model.caching_query import FromCache
49 47
50 48
51 log = logging.getLogger(__name__) 49 log = logging.getLogger(__name__)
52 50
53 #============================================================================== 51 #==============================================================================
85 else: 83 else:
86 return json.JSONEncoder.default(self, obj) 84 return json.JSONEncoder.default(self, obj)
87 85
88 86
89 class BaseModel(object): 87 class BaseModel(object):
90 """Base Model for all classess 88 """
91 89 Base Model for all classess
92 """ 90 """
93 91
94 @classmethod 92 @classmethod
95 def _get_keys(cls): 93 def _get_keys(cls):
96 """return column names for this model """ 94 """return column names for this model """
97 return class_mapper(cls).c.keys() 95 return class_mapper(cls).c.keys()
98 96
99 def get_dict(self): 97 def get_dict(self):
100 """return dict with keys and values corresponding 98 """
99 return dict with keys and values corresponding
101 to this model data """ 100 to this model data """
102 101
103 d = {} 102 d = {}
104 for k in self._get_keys(): 103 for k in self._get_keys():
105 d[k] = getattr(self, k) 104 d[k] = getattr(self, k)
140 139
141 @classmethod 140 @classmethod
142 def delete(cls, id_): 141 def delete(cls, id_):
143 obj = cls.query().get(id_) 142 obj = cls.query().get(id_)
144 Session.delete(obj) 143 Session.delete(obj)
145 Session.commit() 144
146 145
147 146 class RhodeCodeSetting(Base, BaseModel):
148 class RhodeCodeSettings(Base, BaseModel):
149 __tablename__ = 'rhodecode_settings' 147 __tablename__ = 'rhodecode_settings'
150 __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True}) 148 __table_args__ = (
149 UniqueConstraint('app_settings_name'),
150 {'extend_existing': True}
151 )
151 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) 152 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
152 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 153 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
153 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 154 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
154 155
155 def __init__(self, k='', v=''): 156 def __init__(self, k='', v=''):
156 self.app_settings_name = k 157 self.app_settings_name = k
157 self.app_settings_value = v 158 self.app_settings_value = v
158
159 159
160 @validates('_app_settings_value') 160 @validates('_app_settings_value')
161 def validate_settings_value(self, key, val): 161 def validate_settings_value(self, key, val):
162 assert type(val) == unicode 162 assert type(val) == unicode
163 return val 163 return val
164 164
165 @hybrid_property 165 @hybrid_property
166 def app_settings_value(self): 166 def app_settings_value(self):
167 v = self._app_settings_value 167 v = self._app_settings_value
168 if v == 'ldap_active': 168 if self.app_settings_name == 'ldap_active':
169 v = str2bool(v) 169 v = str2bool(v)
170 return v 170 return v
171 171
172 @app_settings_value.setter 172 @app_settings_value.setter
173 def app_settings_value(self, val): 173 def app_settings_value(self, val):
177 :param val: 177 :param val:
178 """ 178 """
179 self._app_settings_value = safe_unicode(val) 179 self._app_settings_value = safe_unicode(val)
180 180
181 def __repr__(self): 181 def __repr__(self):
182 return "<%s('%s:%s')>" % (self.__class__.__name__, 182 return "<%s('%s:%s')>" % (
183 self.app_settings_name, self.app_settings_value) 183 self.__class__.__name__,
184 184 self.app_settings_name, self.app_settings_value
185 )
185 186
186 @classmethod 187 @classmethod
187 def get_by_name(cls, ldap_key): 188 def get_by_name(cls, ldap_key):
188 return cls.query()\ 189 return cls.query()\
189 .filter(cls.app_settings_name == ldap_key).scalar() 190 .filter(cls.app_settings_name == ldap_key).scalar()
216 return fd 217 return fd
217 218
218 219
219 class RhodeCodeUi(Base, BaseModel): 220 class RhodeCodeUi(Base, BaseModel):
220 __tablename__ = 'rhodecode_ui' 221 __tablename__ = 'rhodecode_ui'
221 __table_args__ = (UniqueConstraint('ui_key'), {'extend_existing':True}) 222 __table_args__ = (
223 UniqueConstraint('ui_key'),
224 {'extend_existing': True}
225 )
222 226
223 HOOK_UPDATE = 'changegroup.update' 227 HOOK_UPDATE = 'changegroup.update'
224 HOOK_REPO_SIZE = 'changegroup.repo_size' 228 HOOK_REPO_SIZE = 'changegroup.repo_size'
225 HOOK_PUSH = 'pretxnchangegroup.push_logger' 229 HOOK_PUSH = 'pretxnchangegroup.push_logger'
226 HOOK_PULL = 'preoutgoing.pull_logger' 230 HOOK_PULL = 'preoutgoing.pull_logger'
229 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 233 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
230 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 234 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
231 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 235 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
232 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True) 236 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
233 237
234
235 @classmethod 238 @classmethod
236 def get_by_key(cls, key): 239 def get_by_key(cls, key):
237 return cls.query().filter(cls.ui_key == key) 240 return cls.query().filter(cls.ui_key == key)
238
239 241
240 @classmethod 242 @classmethod
241 def get_builtin_hooks(cls): 243 def get_builtin_hooks(cls):
242 q = cls.query() 244 q = cls.query()
243 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, 245 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
261 new_ui.ui_active = True 263 new_ui.ui_active = True
262 new_ui.ui_key = key 264 new_ui.ui_key = key
263 new_ui.ui_value = val 265 new_ui.ui_value = val
264 266
265 Session.add(new_ui) 267 Session.add(new_ui)
266 Session.commit()
267 268
268 269
269 class User(Base, BaseModel): 270 class User(Base, BaseModel):
270 __tablename__ = 'users' 271 __tablename__ = 'users'
271 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True}) 272 __table_args__ = (
273 UniqueConstraint('username'), UniqueConstraint('email'),
274 {'extend_existing': True}
275 )
272 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) 276 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
273 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 277 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
274 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 278 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
275 active = Column("active", Boolean(), nullable=True, unique=None, default=None) 279 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
276 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False) 280 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
284 user_log = relationship('UserLog', cascade='all') 288 user_log = relationship('UserLog', cascade='all')
285 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all') 289 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
286 290
287 repositories = relationship('Repository') 291 repositories = relationship('Repository')
288 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all') 292 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
289 repo_to_perm = relationship('RepoToPerm', primaryjoin='RepoToPerm.user_id==User.user_id', cascade='all') 293 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
290 294
291 group_member = relationship('UsersGroupMember', cascade='all') 295 group_member = relationship('UsersGroupMember', cascade='all')
296
297 notifications = relationship('UserNotification',)
292 298
293 @hybrid_property 299 @hybrid_property
294 def email(self): 300 def email(self):
295 return self._email 301 return self._email
296 302
301 @property 307 @property
302 def full_name(self): 308 def full_name(self):
303 return '%s %s' % (self.name, self.lastname) 309 return '%s %s' % (self.name, self.lastname)
304 310
305 @property 311 @property
312 def full_name_or_username(self):
313 return ('%s %s' % (self.name, self.lastname)
314 if (self.name and self.lastname) else self.username)
315
316 @property
306 def full_contact(self): 317 def full_contact(self):
307 return '%s %s <%s>' % (self.name, self.lastname, self.email) 318 return '%s %s <%s>' % (self.name, self.lastname, self.email)
308 319
309 @property 320 @property
310 def short_contact(self): 321 def short_contact(self):
313 @property 324 @property
314 def is_admin(self): 325 def is_admin(self):
315 return self.admin 326 return self.admin
316 327
317 def __repr__(self): 328 def __repr__(self):
318 try: 329 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
319 return "<%s('id:%s:%s')>" % (self.__class__.__name__, 330 self.user_id, self.username)
320 self.user_id, self.username) 331
321 except: 332 @classmethod
322 return self.__class__.__name__ 333 def get_by_username(cls, username, case_insensitive=False, cache=False):
323
324 def __json__(self):
325 return {'email': self.email}
326
327 @classmethod
328 def get_by_username(cls, username, case_insensitive=False):
329 if case_insensitive: 334 if case_insensitive:
330 return Session.query(cls).filter(cls.username.ilike(username)).scalar() 335 q = cls.query().filter(cls.username.ilike(username))
331 else: 336 else:
332 return Session.query(cls).filter(cls.username == username).scalar() 337 q = cls.query().filter(cls.username == username)
333 338
334 @classmethod 339 if cache:
335 def get_by_api_key(cls, api_key): 340 q = q.options(FromCache("sql_cache_short",
336 return cls.query().filter(cls.api_key == api_key).one() 341 "get_user_%s" % username))
342 return q.scalar()
343
344 @classmethod
345 def get_by_api_key(cls, api_key, cache=False):
346 q = cls.query().filter(cls.api_key == api_key)
347
348 if cache:
349 q = q.options(FromCache("sql_cache_short",
350 "get_api_key_%s" % api_key))
351 return q.scalar()
352
353 @classmethod
354 def get_by_email(cls, email, case_insensitive=False, cache=False):
355 if case_insensitive:
356 q = cls.query().filter(cls.email.ilike(email))
357 else:
358 q = cls.query().filter(cls.email == email)
359
360 if cache:
361 q = q.options(FromCache("sql_cache_short",
362 "get_api_key_%s" % email))
363 return q.scalar()
337 364
338 def update_lastlogin(self): 365 def update_lastlogin(self):
339 """Update user lastlogin""" 366 """Update user lastlogin"""
340
341 self.last_login = datetime.datetime.now() 367 self.last_login = datetime.datetime.now()
342 Session.add(self) 368 Session.add(self)
343 Session.commit() 369 log.debug('updated user %s lastlogin' % self.username)
344 log.debug('updated user %s lastlogin', self.username) 370
345 371 def __json__(self):
346 @classmethod 372 return dict(
347 def create(cls, form_data): 373 email=self.email,
348 from rhodecode.lib.auth import get_crypt_password 374 full_name=self.full_name,
349 375 full_name_or_username=self.full_name_or_username,
350 try: 376 short_contact=self.short_contact,
351 new_user = cls() 377 full_contact=self.full_contact
352 for k, v in form_data.items(): 378 )
353 if k == 'password': 379
354 v = get_crypt_password(v)
355 setattr(new_user, k, v)
356
357 new_user.api_key = generate_api_key(form_data['username'])
358 Session.add(new_user)
359 Session.commit()
360 return new_user
361 except:
362 log.error(traceback.format_exc())
363 Session.rollback()
364 raise
365 380
366 class UserLog(Base, BaseModel): 381 class UserLog(Base, BaseModel):
367 __tablename__ = 'user_logs' 382 __tablename__ = 'user_logs'
368 __table_args__ = {'extend_existing':True} 383 __table_args__ = {'extend_existing': True}
369 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) 384 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
370 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None) 385 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
371 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None) 386 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
372 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 387 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
373 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 388 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
374 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 389 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
375 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None) 390 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
376 391
377 @property 392 @property
378 def action_as_day(self): 393 def action_as_day(self):
379 return date(*self.action_date.timetuple()[:3]) 394 return datetime.date(*self.action_date.timetuple()[:3])
380 395
381 user = relationship('User') 396 user = relationship('User')
382 repository = relationship('Repository') 397 repository = relationship('Repository',cascade='')
383 398
384 399
385 class UsersGroup(Base, BaseModel): 400 class UsersGroup(Base, BaseModel):
386 __tablename__ = 'users_groups' 401 __tablename__ = 'users_groups'
387 __table_args__ = {'extend_existing':True} 402 __table_args__ = {'extend_existing': True}
388 403
389 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) 404 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
390 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None) 405 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
391 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None) 406 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
392 407
394 409
395 def __repr__(self): 410 def __repr__(self):
396 return '<userGroup(%s)>' % (self.users_group_name) 411 return '<userGroup(%s)>' % (self.users_group_name)
397 412
398 @classmethod 413 @classmethod
399 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False): 414 def get_by_group_name(cls, group_name, cache=False,
415 case_insensitive=False):
400 if case_insensitive: 416 if case_insensitive:
401 gr = cls.query()\ 417 q = cls.query().filter(cls.users_group_name.ilike(group_name))
402 .filter(cls.users_group_name.ilike(group_name))
403 else: 418 else:
404 gr = cls.query()\ 419 q = cls.query().filter(cls.users_group_name == group_name)
405 .filter(cls.users_group_name == group_name)
406 if cache: 420 if cache:
407 gr = gr.options(FromCache("sql_cache_short", 421 q = q.options(FromCache("sql_cache_short",
408 "get_user_%s" % group_name)) 422 "get_user_%s" % group_name))
409 return gr.scalar() 423 return q.scalar()
410
411 424
412 @classmethod 425 @classmethod
413 def get(cls, users_group_id, cache=False): 426 def get(cls, users_group_id, cache=False):
414 users_group = cls.query() 427 users_group = cls.query()
415 if cache: 428 if cache:
416 users_group = users_group.options(FromCache("sql_cache_short", 429 users_group = users_group.options(FromCache("sql_cache_short",
417 "get_users_group_%s" % users_group_id)) 430 "get_users_group_%s" % users_group_id))
418 return users_group.get(users_group_id) 431 return users_group.get(users_group_id)
419 432
420 @classmethod
421 def create(cls, form_data):
422 try:
423 new_users_group = cls()
424 for k, v in form_data.items():
425 setattr(new_users_group, k, v)
426
427 Session.add(new_users_group)
428 Session.commit()
429 return new_users_group
430 except:
431 log.error(traceback.format_exc())
432 Session.rollback()
433 raise
434
435 @classmethod
436 def update(cls, users_group_id, form_data):
437
438 try:
439 users_group = cls.get(users_group_id, cache=False)
440
441 for k, v in form_data.items():
442 if k == 'users_group_members':
443 users_group.members = []
444 Session.flush()
445 members_list = []
446 if v:
447 v = [v] if isinstance(v, basestring) else v
448 for u_id in set(v):
449 member = UsersGroupMember(users_group_id, u_id)
450 members_list.append(member)
451 setattr(users_group, 'members', members_list)
452 setattr(users_group, k, v)
453
454 Session.add(users_group)
455 Session.commit()
456 except:
457 log.error(traceback.format_exc())
458 Session.rollback()
459 raise
460
461 @classmethod
462 def delete(cls, users_group_id):
463 try:
464
465 # check if this group is not assigned to repo
466 assigned_groups = UsersGroupRepoToPerm.query()\
467 .filter(UsersGroupRepoToPerm.users_group_id ==
468 users_group_id).all()
469
470 if assigned_groups:
471 raise UsersGroupsAssignedException('Group assigned to %s' %
472 assigned_groups)
473
474 users_group = cls.get(users_group_id, cache=False)
475 Session.delete(users_group)
476 Session.commit()
477 except:
478 log.error(traceback.format_exc())
479 Session.rollback()
480 raise
481 433
482 class UsersGroupMember(Base, BaseModel): 434 class UsersGroupMember(Base, BaseModel):
483 __tablename__ = 'users_groups_members' 435 __tablename__ = 'users_groups_members'
484 __table_args__ = {'extend_existing':True} 436 __table_args__ = {'extend_existing': True}
485 437
486 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) 438 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
487 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None) 439 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
488 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None) 440 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
489 441
492 444
493 def __init__(self, gr_id='', u_id=''): 445 def __init__(self, gr_id='', u_id=''):
494 self.users_group_id = gr_id 446 self.users_group_id = gr_id
495 self.user_id = u_id 447 self.user_id = u_id
496 448
497 @staticmethod
498 def add_user_to_group(group, user):
499 ugm = UsersGroupMember()
500 ugm.users_group = group
501 ugm.user = user
502 Session.add(ugm)
503 Session.commit()
504 return ugm
505 449
506 class Repository(Base, BaseModel): 450 class Repository(Base, BaseModel):
507 __tablename__ = 'repositories' 451 __tablename__ = 'repositories'
508 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},) 452 __table_args__ = (
453 UniqueConstraint('repo_name'),
454 {'extend_existing': True},
455 )
509 456
510 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) 457 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
511 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None) 458 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
512 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None) 459 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
513 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg') 460 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
519 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now) 466 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
520 467
521 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None) 468 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
522 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None) 469 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
523 470
524
525 user = relationship('User') 471 user = relationship('User')
526 fork = relationship('Repository', remote_side=repo_id) 472 fork = relationship('Repository', remote_side=repo_id)
527 group = relationship('Group') 473 group = relationship('RepoGroup')
528 repo_to_perm = relationship('RepoToPerm', cascade='all', order_by='RepoToPerm.repo_to_perm_id') 474 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
529 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all') 475 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
530 stats = relationship('Statistics', cascade='all', uselist=False) 476 stats = relationship('Statistics', cascade='all', uselist=False)
531 477
532 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all') 478 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
533 479
534 logs = relationship('UserLog', cascade='all') 480 logs = relationship('UserLog')
535 481
536 def __repr__(self): 482 def __repr__(self):
537 return "<%s('%s:%s')>" % (self.__class__.__name__, 483 return "<%s('%s:%s')>" % (self.__class__.__name__,
538 self.repo_id, self.repo_name) 484 self.repo_id, self.repo_name)
539 485
545 def get_by_repo_name(cls, repo_name): 491 def get_by_repo_name(cls, repo_name):
546 q = Session.query(cls).filter(cls.repo_name == repo_name) 492 q = Session.query(cls).filter(cls.repo_name == repo_name)
547 q = q.options(joinedload(Repository.fork))\ 493 q = q.options(joinedload(Repository.fork))\
548 .options(joinedload(Repository.user))\ 494 .options(joinedload(Repository.user))\
549 .options(joinedload(Repository.group)) 495 .options(joinedload(Repository.group))
550 return q.one() 496 return q.scalar()
551 497
552 @classmethod 498 @classmethod
553 def get_repo_forks(cls, repo_id): 499 def get_repo_forks(cls, repo_id):
554 return cls.query().filter(Repository.fork_id == repo_id) 500 return cls.query().filter(Repository.fork_id == repo_id)
555 501
558 """ 504 """
559 Returns base path when all repos are stored 505 Returns base path when all repos are stored
560 506
561 :param cls: 507 :param cls:
562 """ 508 """
563 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == 509 q = Session.query(RhodeCodeUi)\
564 cls.url_sep()) 510 .filter(RhodeCodeUi.ui_key == cls.url_sep())
565 q.options(FromCache("sql_cache_short", "repository_repo_path")) 511 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
566 return q.one().ui_value 512 return q.one().ui_value
567 513
568 @property 514 @property
569 def just_name(self): 515 def just_name(self):
570 return self.repo_name.split(Repository.url_sep())[-1] 516 return self.repo_name.split(Repository.url_sep())[-1]
596 Returns base full path for that repository means where it actually 542 Returns base full path for that repository means where it actually
597 exists on a filesystem 543 exists on a filesystem
598 """ 544 """
599 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == 545 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
600 Repository.url_sep()) 546 Repository.url_sep())
601 q.options(FromCache("sql_cache_short", "repository_repo_path")) 547 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
602 return q.one().ui_value 548 return q.one().ui_value
603 549
604 @property 550 @property
605 def repo_full_path(self): 551 def repo_full_path(self):
606 p = [self.repo_path] 552 p = [self.repo_path]
631 #clean the baseui object 577 #clean the baseui object
632 baseui._ocfg = config.config() 578 baseui._ocfg = config.config()
633 baseui._ucfg = config.config() 579 baseui._ucfg = config.config()
634 baseui._tcfg = config.config() 580 baseui._tcfg = config.config()
635 581
636
637 ret = RhodeCodeUi.query()\ 582 ret = RhodeCodeUi.query()\
638 .options(FromCache("sql_cache_short", "repository_repo_ui")).all() 583 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
639 584
640 hg_ui = ret 585 hg_ui = ret
641 for ui_ in hg_ui: 586 for ui_ in hg_ui:
649 @classmethod 594 @classmethod
650 def is_valid(cls, repo_name): 595 def is_valid(cls, repo_name):
651 """ 596 """
652 returns True if given repo name is a valid filesystem repository 597 returns True if given repo name is a valid filesystem repository
653 598
654 @param cls: 599 :param cls:
655 @param repo_name: 600 :param repo_name:
656 """ 601 """
657 from rhodecode.lib.utils import is_valid_repo 602 from rhodecode.lib.utils import is_valid_repo
658 603
659 return is_valid_repo(repo_name, cls.base_path()) 604 return is_valid_repo(repo_name, cls.base_path())
660
661 605
662 #========================================================================== 606 #==========================================================================
663 # SCM PROPERTIES 607 # SCM PROPERTIES
664 #========================================================================== 608 #==========================================================================
665 609
676 620
677 @property 621 @property
678 def last_change(self): 622 def last_change(self):
679 return self.scm_instance.last_change 623 return self.scm_instance.last_change
680 624
625 def comments(self, revisions=None):
626 """
627 Returns comments for this repository grouped by revisions
628
629 :param revisions: filter query by revisions only
630 """
631 cmts = ChangesetComment.query()\
632 .filter(ChangesetComment.repo == self)
633 if revisions:
634 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
635 grouped = defaultdict(list)
636 for cmt in cmts.all():
637 grouped[cmt.revision].append(cmt)
638 return grouped
639
681 #========================================================================== 640 #==========================================================================
682 # SCM CACHE INSTANCE 641 # SCM CACHE INSTANCE
683 #========================================================================== 642 #==========================================================================
684 643
685 @property 644 @property
686 def invalidate(self): 645 def invalidate(self):
687 """ 646 return CacheInvalidation.invalidate(self.repo_name)
688 Returns Invalidation object if this repo should be invalidated
689 None otherwise. `cache_active = False` means that this cache
690 state is not valid and needs to be invalidated
691 """
692 return CacheInvalidation.query()\
693 .filter(CacheInvalidation.cache_key == self.repo_name)\
694 .filter(CacheInvalidation.cache_active == False)\
695 .scalar()
696 647
697 def set_invalidate(self): 648 def set_invalidate(self):
698 """ 649 """
699 set a cache for invalidation for this instance 650 set a cache for invalidation for this instance
700 """ 651 """
701 inv = CacheInvalidation.query()\ 652 CacheInvalidation.set_invalidate(self.repo_name)
702 .filter(CacheInvalidation.cache_key == self.repo_name)\
703 .scalar()
704
705 if inv is None:
706 inv = CacheInvalidation(self.repo_name)
707 inv.cache_active = True
708 Session.add(inv)
709 Session.commit()
710 653
711 @LazyProperty 654 @LazyProperty
712 def scm_instance(self): 655 def scm_instance(self):
713 return self.__get_instance() 656 return self.__get_instance()
714 657
715 @property 658 @property
716 def scm_instance_cached(self): 659 def scm_instance_cached(self):
717 @cache_region('long_term') 660 @cache_region('long_term')
718 def _c(repo_name): 661 def _c(repo_name):
719 return self.__get_instance() 662 return self.__get_instance()
720 663 rn = self.repo_name
721 # TODO: remove this trick when beaker 1.6 is released 664 log.debug('Getting cached instance of repo')
722 # and have fixed this issue with not supporting unicode keys
723 rn = safe_str(self.repo_name)
724
725 inv = self.invalidate 665 inv = self.invalidate
726 if inv is not None: 666 if inv is not None:
727 region_invalidate(_c, None, rn) 667 region_invalidate(_c, None, rn)
728 # update our cache 668 # update our cache
729 inv.cache_active = True 669 CacheInvalidation.set_valid(inv.cache_key)
730 Session.add(inv)
731 Session.commit()
732
733 return _c(rn) 670 return _c(rn)
734 671
735 def __get_instance(self): 672 def __get_instance(self):
736
737 repo_full_path = self.repo_full_path 673 repo_full_path = self.repo_full_path
738
739 try: 674 try:
740 alias = get_scm(repo_full_path)[0] 675 alias = get_scm(repo_full_path)[0]
741 log.debug('Creating instance of %s repository', alias) 676 log.debug('Creating instance of %s repository' % alias)
742 backend = get_backend(alias) 677 backend = get_backend(alias)
743 except VCSError: 678 except VCSError:
744 log.error(traceback.format_exc()) 679 log.error(traceback.format_exc())
745 log.error('Perhaps this repository is in db and not in ' 680 log.error('Perhaps this repository is in db and not in '
746 'filesystem run rescan repositories with ' 681 'filesystem run rescan repositories with '
749 684
750 if alias == 'hg': 685 if alias == 'hg':
751 686
752 repo = backend(safe_str(repo_full_path), create=False, 687 repo = backend(safe_str(repo_full_path), create=False,
753 baseui=self._ui) 688 baseui=self._ui)
754 #skip hidden web repository 689 # skip hidden web repository
755 if repo._get_hidden(): 690 if repo._get_hidden():
756 return 691 return
757 else: 692 else:
758 repo = backend(repo_full_path, create=False) 693 repo = backend(repo_full_path, create=False)
759 694
760 return repo 695 return repo
761 696
762 697
763 class Group(Base, BaseModel): 698 class RepoGroup(Base, BaseModel):
764 __tablename__ = 'groups' 699 __tablename__ = 'groups'
765 __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'), 700 __table_args__ = (
766 CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},) 701 UniqueConstraint('group_name', 'group_parent_id'),
767 __mapper_args__ = {'order_by':'group_name'} 702 CheckConstraint('group_id != group_parent_id'),
703 {'extend_existing': True},
704 )
705 __mapper_args__ = {'order_by': 'group_name'}
768 706
769 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) 707 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
770 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None) 708 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
771 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None) 709 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
772 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 710 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
773 711
774 parent_group = relationship('Group', remote_side=group_id) 712 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
775 713 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
714
715 parent_group = relationship('RepoGroup', remote_side=group_id)
776 716
777 def __init__(self, group_name='', parent_group=None): 717 def __init__(self, group_name='', parent_group=None):
778 self.group_name = group_name 718 self.group_name = group_name
779 self.parent_group = parent_group 719 self.parent_group = parent_group
780 720
836 groups.insert(0, gr) 776 groups.insert(0, gr)
837 return groups 777 return groups
838 778
839 @property 779 @property
840 def children(self): 780 def children(self):
841 return Group.query().filter(Group.parent_group == self) 781 return RepoGroup.query().filter(RepoGroup.parent_group == self)
842 782
843 @property 783 @property
844 def name(self): 784 def name(self):
845 return self.group_name.split(Group.url_sep())[-1] 785 return self.group_name.split(RepoGroup.url_sep())[-1]
846 786
847 @property 787 @property
848 def full_path(self): 788 def full_path(self):
849 return self.group_name 789 return self.group_name
850 790
851 @property 791 @property
852 def full_path_splitted(self): 792 def full_path_splitted(self):
853 return self.group_name.split(Group.url_sep()) 793 return self.group_name.split(RepoGroup.url_sep())
854 794
855 @property 795 @property
856 def repositories(self): 796 def repositories(self):
857 return Repository.query().filter(Repository.group == self) 797 return Repository.query().filter(Repository.group == self)
858 798
867 cnt += children_count(child) 807 cnt += children_count(child)
868 return cnt 808 return cnt
869 809
870 return cnt + children_count(self) 810 return cnt + children_count(self)
871 811
872
873 def get_new_name(self, group_name): 812 def get_new_name(self, group_name):
874 """ 813 """
875 returns new full group name based on parent and new name 814 returns new full group name based on parent and new name
876 815
877 :param group_name: 816 :param group_name:
878 """ 817 """
879 path_prefix = (self.parent_group.full_path_splitted if 818 path_prefix = (self.parent_group.full_path_splitted if
880 self.parent_group else []) 819 self.parent_group else [])
881 return Group.url_sep().join(path_prefix + [group_name]) 820 return RepoGroup.url_sep().join(path_prefix + [group_name])
882 821
883 822
884 class Permission(Base, BaseModel): 823 class Permission(Base, BaseModel):
885 __tablename__ = 'permissions' 824 __tablename__ = 'permissions'
886 __table_args__ = {'extend_existing':True} 825 __table_args__ = {'extend_existing': True}
887 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) 826 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
888 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 827 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
889 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 828 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
890 829
891 def __repr__(self): 830 def __repr__(self):
892 return "<%s('%s:%s')>" % (self.__class__.__name__, 831 return "<%s('%s:%s')>" % (
893 self.permission_id, self.permission_name) 832 self.__class__.__name__, self.permission_id, self.permission_name
833 )
894 834
895 @classmethod 835 @classmethod
896 def get_by_key(cls, key): 836 def get_by_key(cls, key):
897 return cls.query().filter(cls.permission_name == key).scalar() 837 return cls.query().filter(cls.permission_name == key).scalar()
898 838
899 class RepoToPerm(Base, BaseModel): 839 @classmethod
840 def get_default_perms(cls, default_user_id):
841 q = Session.query(UserRepoToPerm, Repository, cls)\
842 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
843 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
844 .filter(UserRepoToPerm.user_id == default_user_id)
845
846 return q.all()
847
848 @classmethod
849 def get_default_group_perms(cls, default_user_id):
850 q = Session.query(UserRepoGroupToPerm, RepoGroup, cls)\
851 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
852 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
853 .filter(UserRepoGroupToPerm.user_id == default_user_id)
854
855 return q.all()
856
857
858 class UserRepoToPerm(Base, BaseModel):
900 __tablename__ = 'repo_to_perm' 859 __tablename__ = 'repo_to_perm'
901 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True}) 860 __table_args__ = (
861 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
862 {'extend_existing': True}
863 )
902 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) 864 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
903 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None) 865 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
904 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None) 866 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
905 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None) 867 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
906 868
907 user = relationship('User') 869 user = relationship('User')
870 repository = relationship('Repository')
908 permission = relationship('Permission') 871 permission = relationship('Permission')
909 repository = relationship('Repository') 872
873 @classmethod
874 def create(cls, user, repository, permission):
875 n = cls()
876 n.user = user
877 n.repository = repository
878 n.permission = permission
879 Session.add(n)
880 return n
881
882 def __repr__(self):
883 return '<user:%s => %s >' % (self.user, self.repository)
884
910 885
911 class UserToPerm(Base, BaseModel): 886 class UserToPerm(Base, BaseModel):
912 __tablename__ = 'user_to_perm' 887 __tablename__ = 'user_to_perm'
913 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True}) 888 __table_args__ = (
889 UniqueConstraint('user_id', 'permission_id'),
890 {'extend_existing': True}
891 )
914 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) 892 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
915 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None) 893 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
916 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None) 894 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
917 895
918 user = relationship('User') 896 user = relationship('User')
919 permission = relationship('Permission') 897 permission = relationship('Permission', lazy='joined')
920 898
921 @classmethod
922 def has_perm(cls, user_id, perm):
923 if not isinstance(perm, Permission):
924 raise Exception('perm needs to be an instance of Permission class')
925
926 return cls.query().filter(cls.user_id == user_id)\
927 .filter(cls.permission == perm).scalar() is not None
928
929 @classmethod
930 def grant_perm(cls, user_id, perm):
931 if not isinstance(perm, Permission):
932 raise Exception('perm needs to be an instance of Permission class')
933
934 new = cls()
935 new.user_id = user_id
936 new.permission = perm
937 try:
938 Session.add(new)
939 Session.commit()
940 except:
941 Session.rollback()
942
943
944 @classmethod
945 def revoke_perm(cls, user_id, perm):
946 if not isinstance(perm, Permission):
947 raise Exception('perm needs to be an instance of Permission class')
948
949 try:
950 cls.query().filter(cls.user_id == user_id)\
951 .filter(cls.permission == perm).delete()
952 Session.commit()
953 except:
954 Session.rollback()
955 899
956 class UsersGroupRepoToPerm(Base, BaseModel): 900 class UsersGroupRepoToPerm(Base, BaseModel):
957 __tablename__ = 'users_group_repo_to_perm' 901 __tablename__ = 'users_group_repo_to_perm'
958 __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True}) 902 __table_args__ = (
903 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
904 {'extend_existing': True}
905 )
959 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) 906 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
960 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None) 907 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
961 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None) 908 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
962 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None) 909 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
963 910
964 users_group = relationship('UsersGroup') 911 users_group = relationship('UsersGroup')
965 permission = relationship('Permission') 912 permission = relationship('Permission')
966 repository = relationship('Repository') 913 repository = relationship('Repository')
967 914
915 @classmethod
916 def create(cls, users_group, repository, permission):
917 n = cls()
918 n.users_group = users_group
919 n.repository = repository
920 n.permission = permission
921 Session.add(n)
922 return n
923
968 def __repr__(self): 924 def __repr__(self):
969 return '<userGroup:%s => %s >' % (self.users_group, self.repository) 925 return '<userGroup:%s => %s >' % (self.users_group, self.repository)
970 926
927
971 class UsersGroupToPerm(Base, BaseModel): 928 class UsersGroupToPerm(Base, BaseModel):
972 __tablename__ = 'users_group_to_perm' 929 __tablename__ = 'users_group_to_perm'
930 __table_args__ = (
931 UniqueConstraint('users_group_id', 'permission_id',),
932 {'extend_existing': True}
933 )
973 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) 934 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
974 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None) 935 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
975 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None) 936 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
976 937
977 users_group = relationship('UsersGroup') 938 users_group = relationship('UsersGroup')
978 permission = relationship('Permission') 939 permission = relationship('Permission')
979 940
980 941
981 @classmethod 942 class UserRepoGroupToPerm(Base, BaseModel):
982 def has_perm(cls, users_group_id, perm): 943 __tablename__ = 'user_repo_group_to_perm'
983 if not isinstance(perm, Permission): 944 __table_args__ = (
984 raise Exception('perm needs to be an instance of Permission class') 945 UniqueConstraint('user_id', 'group_id', 'permission_id'),
985 946 {'extend_existing': True}
986 return cls.query().filter(cls.users_group_id == 947 )
987 users_group_id)\
988 .filter(cls.permission == perm)\
989 .scalar() is not None
990
991 @classmethod
992 def grant_perm(cls, users_group_id, perm):
993 if not isinstance(perm, Permission):
994 raise Exception('perm needs to be an instance of Permission class')
995
996 new = cls()
997 new.users_group_id = users_group_id
998 new.permission = perm
999 try:
1000 Session.add(new)
1001 Session.commit()
1002 except:
1003 Session.rollback()
1004
1005
1006 @classmethod
1007 def revoke_perm(cls, users_group_id, perm):
1008 if not isinstance(perm, Permission):
1009 raise Exception('perm needs to be an instance of Permission class')
1010
1011 try:
1012 cls.query().filter(cls.users_group_id == users_group_id)\
1013 .filter(cls.permission == perm).delete()
1014 Session.commit()
1015 except:
1016 Session.rollback()
1017
1018
1019 class GroupToPerm(Base, BaseModel):
1020 __tablename__ = 'group_to_perm'
1021 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
1022 948
1023 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) 949 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1024 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None) 950 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
951 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1025 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None) 952 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
953
954 user = relationship('User')
955 group = relationship('RepoGroup')
956 permission = relationship('Permission')
957
958
959 class UsersGroupRepoGroupToPerm(Base, BaseModel):
960 __tablename__ = 'users_group_repo_group_to_perm'
961 __table_args__ = (
962 UniqueConstraint('users_group_id', 'group_id'),
963 {'extend_existing': True}
964 )
965
966 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
967 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1026 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None) 968 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1027 969 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1028 user = relationship('User') 970
971 users_group = relationship('UsersGroup')
1029 permission = relationship('Permission') 972 permission = relationship('Permission')
1030 group = relationship('Group') 973 group = relationship('RepoGroup')
974
1031 975
1032 class Statistics(Base, BaseModel): 976 class Statistics(Base, BaseModel):
1033 __tablename__ = 'statistics' 977 __tablename__ = 'statistics'
1034 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True}) 978 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing': True})
1035 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) 979 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1036 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None) 980 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1037 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False) 981 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1038 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data 982 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1039 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data 983 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1040 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data 984 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1041 985
1042 repository = relationship('Repository', single_parent=True) 986 repository = relationship('Repository', single_parent=True)
1043 987
988
1044 class UserFollowing(Base, BaseModel): 989 class UserFollowing(Base, BaseModel):
1045 __tablename__ = 'user_followings' 990 __tablename__ = 'user_followings'
1046 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'), 991 __table_args__ = (
1047 UniqueConstraint('user_id', 'follows_user_id') 992 UniqueConstraint('user_id', 'follows_repository_id'),
1048 , {'extend_existing':True}) 993 UniqueConstraint('user_id', 'follows_user_id'),
994 {'extend_existing': True}
995 )
1049 996
1050 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) 997 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1051 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None) 998 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1052 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None) 999 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1053 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None) 1000 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1056 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id') 1003 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1057 1004
1058 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id') 1005 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1059 follows_repository = relationship('Repository', order_by='Repository.repo_name') 1006 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1060 1007
1061
1062 @classmethod 1008 @classmethod
1063 def get_repo_followers(cls, repo_id): 1009 def get_repo_followers(cls, repo_id):
1064 return cls.query().filter(cls.follows_repo_id == repo_id) 1010 return cls.query().filter(cls.follows_repo_id == repo_id)
1065 1011
1012
1066 class CacheInvalidation(Base, BaseModel): 1013 class CacheInvalidation(Base, BaseModel):
1067 __tablename__ = 'cache_invalidation' 1014 __tablename__ = 'cache_invalidation'
1068 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True}) 1015 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing': True})
1069 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) 1016 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1070 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 1017 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1071 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 1018 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1072 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False) 1019 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1073 1020
1074
1075 def __init__(self, cache_key, cache_args=''): 1021 def __init__(self, cache_key, cache_args=''):
1076 self.cache_key = cache_key 1022 self.cache_key = cache_key
1077 self.cache_args = cache_args 1023 self.cache_args = cache_args
1078 self.cache_active = False 1024 self.cache_active = False
1079 1025
1080 def __repr__(self): 1026 def __repr__(self):
1081 return "<%s('%s:%s')>" % (self.__class__.__name__, 1027 return "<%s('%s:%s')>" % (self.__class__.__name__,
1082 self.cache_id, self.cache_key) 1028 self.cache_id, self.cache_key)
1083 1029
1030 @classmethod
1031 def _get_key(cls, key):
1032 """
1033 Wrapper for generating a key
1034
1035 :param key:
1036 """
1037 import rhodecode
1038 prefix = ''
1039 iid = rhodecode.CONFIG.get('instance_id')
1040 if iid:
1041 prefix = iid
1042 return "%s%s" % (prefix, key)
1043
1044 @classmethod
1045 def get_by_key(cls, key):
1046 return cls.query().filter(cls.cache_key == key).scalar()
1047
1048 @classmethod
1049 def invalidate(cls, key):
1050 """
1051 Returns Invalidation object if this given key should be invalidated
1052 None otherwise. `cache_active = False` means that this cache
1053 state is not valid and needs to be invalidated
1054
1055 :param key:
1056 """
1057 return cls.query()\
1058 .filter(CacheInvalidation.cache_key == key)\
1059 .filter(CacheInvalidation.cache_active == False)\
1060 .scalar()
1061
1062 @classmethod
1063 def set_invalidate(cls, key):
1064 """
1065 Mark this Cache key for invalidation
1066
1067 :param key:
1068 """
1069
1070 log.debug('marking %s for invalidation' % key)
1071 inv_obj = Session.query(cls)\
1072 .filter(cls.cache_key == key).scalar()
1073 if inv_obj:
1074 inv_obj.cache_active = False
1075 else:
1076 log.debug('cache key not found in invalidation db -> creating one')
1077 inv_obj = CacheInvalidation(key)
1078
1079 try:
1080 Session.add(inv_obj)
1081 Session.commit()
1082 except Exception:
1083 log.error(traceback.format_exc())
1084 Session.rollback()
1085
1086 @classmethod
1087 def set_valid(cls, key):
1088 """
1089 Mark this cache key as active and currently cached
1090
1091 :param key:
1092 """
1093 inv_obj = cls.get_by_key(key)
1094 inv_obj.cache_active = True
1095 Session.add(inv_obj)
1096 Session.commit()
1097
1098
1099 class ChangesetComment(Base, BaseModel):
1100 __tablename__ = 'changeset_comments'
1101 __table_args__ = ({'extend_existing': True},)
1102 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1103 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1104 revision = Column('revision', String(40), nullable=False)
1105 line_no = Column('line_no', Unicode(10), nullable=True)
1106 f_path = Column('f_path', Unicode(1000), nullable=True)
1107 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1108 text = Column('text', Unicode(25000), nullable=False)
1109 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1110
1111 author = relationship('User', lazy='joined')
1112 repo = relationship('Repository')
1113
1114 @classmethod
1115 def get_users(cls, revision):
1116 """
1117 Returns user associated with this changesetComment. ie those
1118 who actually commented
1119
1120 :param cls:
1121 :param revision:
1122 """
1123 return Session.query(User)\
1124 .filter(cls.revision == revision)\
1125 .join(ChangesetComment.author).all()
1126
1127
1128 class Notification(Base, BaseModel):
1129 __tablename__ = 'notifications'
1130 __table_args__ = ({'extend_existing': True},)
1131
1132 TYPE_CHANGESET_COMMENT = u'cs_comment'
1133 TYPE_MESSAGE = u'message'
1134 TYPE_MENTION = u'mention'
1135 TYPE_REGISTRATION = u'registration'
1136
1137 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1138 subject = Column('subject', Unicode(512), nullable=True)
1139 body = Column('body', Unicode(50000), nullable=True)
1140 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1141 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1142 type_ = Column('type', Unicode(256))
1143
1144 created_by_user = relationship('User')
1145 notifications_to_users = relationship('UserNotification', lazy='joined',
1146 cascade="all, delete, delete-orphan")
1147
1148 @property
1149 def recipients(self):
1150 return [x.user for x in UserNotification.query()\
1151 .filter(UserNotification.notification == self).all()]
1152
1153 @classmethod
1154 def create(cls, created_by, subject, body, recipients, type_=None):
1155 if type_ is None:
1156 type_ = Notification.TYPE_MESSAGE
1157
1158 notification = cls()
1159 notification.created_by_user = created_by
1160 notification.subject = subject
1161 notification.body = body
1162 notification.type_ = type_
1163 notification.created_on = datetime.datetime.now()
1164
1165 for u in recipients:
1166 assoc = UserNotification()
1167 assoc.notification = notification
1168 u.notifications.append(assoc)
1169 Session.add(notification)
1170 return notification
1171
1172 @property
1173 def description(self):
1174 from rhodecode.model.notification import NotificationModel
1175 return NotificationModel().make_description(self)
1176
1177
1178 class UserNotification(Base, BaseModel):
1179 __tablename__ = 'user_to_notification'
1180 __table_args__ = (
1181 UniqueConstraint('user_id', 'notification_id'),
1182 {'extend_existing': True}
1183 )
1184 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1185 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1186 read = Column('read', Boolean, default=False)
1187 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1188
1189 user = relationship('User', lazy="joined")
1190 notification = relationship('Notification', lazy="joined",
1191 order_by=lambda: Notification.created_on.desc(),)
1192
1193 def mark_as_read(self):
1194 self.read = True
1195 Session.add(self)
1196
1197
1084 class DbMigrateVersion(Base, BaseModel): 1198 class DbMigrateVersion(Base, BaseModel):
1085 __tablename__ = 'db_migrate_version' 1199 __tablename__ = 'db_migrate_version'
1086 __table_args__ = {'extend_existing':True} 1200 __table_args__ = {'extend_existing': True}
1087 repository_id = Column('repository_id', String(250), primary_key=True) 1201 repository_id = Column('repository_id', String(250), primary_key=True)
1088 repository_path = Column('repository_path', Text) 1202 repository_path = Column('repository_path', Text)
1089 version = Column('version', Integer) 1203 version = Column('version', Integer)
1090