comparison rhodecode/model/db.py @ 2776:63e58ef80ef1

Merge beta branch into stable
author Marcin Kuzminski <marcin@python-works.com>
date Sun, 02 Sep 2012 21:19:54 +0200
parents a437a986d399 66493675dd5a
children 9ae95fdeca18
comparison
equal deleted inserted replaced
2301:9d097c2592d3 2776:63e58ef80ef1
25 25
26 import os 26 import os
27 import logging 27 import logging
28 import datetime 28 import datetime
29 import traceback 29 import traceback
30 import hashlib
31 import time
30 from collections import defaultdict 32 from collections import defaultdict
31 33
32 from sqlalchemy import * 34 from sqlalchemy import *
33 from sqlalchemy.ext.hybrid import hybrid_property 35 from sqlalchemy.ext.hybrid import hybrid_property
34 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates 36 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
37 from sqlalchemy.exc import DatabaseError
35 from beaker.cache import cache_region, region_invalidate 38 from beaker.cache import cache_region, region_invalidate
39 from webob.exc import HTTPNotFound
40
41 from pylons.i18n.translation import lazy_ugettext as _
36 42
37 from rhodecode.lib.vcs import get_backend 43 from rhodecode.lib.vcs import get_backend
38 from rhodecode.lib.vcs.utils.helpers import get_scm 44 from rhodecode.lib.vcs.utils.helpers import get_scm
39 from rhodecode.lib.vcs.exceptions import VCSError 45 from rhodecode.lib.vcs.exceptions import VCSError
40 from rhodecode.lib.vcs.utils.lazy import LazyProperty 46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
43 safe_unicode 49 safe_unicode
44 from rhodecode.lib.compat import json 50 from rhodecode.lib.compat import json
45 from rhodecode.lib.caching_query import FromCache 51 from rhodecode.lib.caching_query import FromCache
46 52
47 from rhodecode.model.meta import Base, Session 53 from rhodecode.model.meta import Base, Session
48 import hashlib 54
49 55 URL_SEP = '/'
50
51 log = logging.getLogger(__name__) 56 log = logging.getLogger(__name__)
52 57
53 #============================================================================== 58 #==============================================================================
54 # BASE CLASSES 59 # BASE CLASSES
55 #============================================================================== 60 #==============================================================================
56 61
57 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest() 62 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
58
59
60 class ModelSerializer(json.JSONEncoder):
61 """
62 Simple Serializer for JSON,
63
64 usage::
65
66 to make object customized for serialization implement a __json__
67 method that will return a dict for serialization into json
68
69 example::
70
71 class Task(object):
72
73 def __init__(self, name, value):
74 self.name = name
75 self.value = value
76
77 def __json__(self):
78 return dict(name=self.name,
79 value=self.value)
80
81 """
82
83 def default(self, obj):
84
85 if hasattr(obj, '__json__'):
86 return obj.__json__()
87 else:
88 return json.JSONEncoder.default(self, obj)
89 63
90 64
91 class BaseModel(object): 65 class BaseModel(object):
92 """ 66 """
93 Base Model for all classess 67 Base Model for all classess
106 d = {} 80 d = {}
107 for k in self._get_keys(): 81 for k in self._get_keys():
108 d[k] = getattr(self, k) 82 d[k] = getattr(self, k)
109 83
110 # also use __json__() if present to get additional fields 84 # also use __json__() if present to get additional fields
111 for k, val in getattr(self, '__json__', lambda: {})().iteritems(): 85 _json_attr = getattr(self, '__json__', None)
112 d[k] = val 86 if _json_attr:
87 # update with attributes from __json__
88 if callable(_json_attr):
89 _json_attr = _json_attr()
90 for k, val in _json_attr.iteritems():
91 d[k] = val
113 return d 92 return d
114 93
115 def get_appstruct(self): 94 def get_appstruct(self):
116 """return list with keys and values tupples corresponding 95 """return list with keys and values tupples corresponding
117 to this model data """ 96 to this model data """
128 if k in populate_dict: 107 if k in populate_dict:
129 setattr(self, k, populate_dict[k]) 108 setattr(self, k, populate_dict[k])
130 109
131 @classmethod 110 @classmethod
132 def query(cls): 111 def query(cls):
133 return Session.query(cls) 112 return Session().query(cls)
134 113
135 @classmethod 114 @classmethod
136 def get(cls, id_): 115 def get(cls, id_):
137 if id_: 116 if id_:
138 return cls.query().get(id_) 117 return cls.query().get(id_)
139 118
140 @classmethod 119 @classmethod
120 def get_or_404(cls, id_):
121 if id_:
122 res = cls.query().get(id_)
123 if not res:
124 raise HTTPNotFound
125 return res
126
127 @classmethod
141 def getAll(cls): 128 def getAll(cls):
142 return cls.query().all() 129 return cls.query().all()
143 130
144 @classmethod 131 @classmethod
145 def delete(cls, id_): 132 def delete(cls, id_):
146 obj = cls.query().get(id_) 133 obj = cls.query().get(id_)
147 Session.delete(obj) 134 Session().delete(obj)
148 135
149 def __repr__(self): 136 def __repr__(self):
150 if hasattr(self, '__unicode__'): 137 if hasattr(self, '__unicode__'):
151 # python repr needs to return str 138 # python repr needs to return str
152 return safe_str(self.__unicode__()) 139 return safe_str(self.__unicode__())
153 return '<DB:%s>' % (self.__class__.__name__) 140 return '<DB:%s>' % (self.__class__.__name__)
154 141
142
155 class RhodeCodeSetting(Base, BaseModel): 143 class RhodeCodeSetting(Base, BaseModel):
156 __tablename__ = 'rhodecode_settings' 144 __tablename__ = 'rhodecode_settings'
157 __table_args__ = ( 145 __table_args__ = (
158 UniqueConstraint('app_settings_name'), 146 UniqueConstraint('app_settings_name'),
159 {'extend_existing': True, 'mysql_engine':'InnoDB', 147 {'extend_existing': True, 'mysql_engine': 'InnoDB',
160 'mysql_charset': 'utf8'} 148 'mysql_charset': 'utf8'}
161 ) 149 )
162 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) 150 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
163 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 151 app_settings_name = Column("app_settings_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
164 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 152 _app_settings_value = Column("app_settings_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
165 153
166 def __init__(self, k='', v=''): 154 def __init__(self, k='', v=''):
167 self.app_settings_name = k 155 self.app_settings_name = k
168 self.app_settings_value = v 156 self.app_settings_value = v
169 157
193 self.__class__.__name__, 181 self.__class__.__name__,
194 self.app_settings_name, self.app_settings_value 182 self.app_settings_name, self.app_settings_value
195 ) 183 )
196 184
197 @classmethod 185 @classmethod
198 def get_by_name(cls, ldap_key): 186 def get_by_name(cls, key):
199 return cls.query()\ 187 return cls.query()\
200 .filter(cls.app_settings_name == ldap_key).scalar() 188 .filter(cls.app_settings_name == key).scalar()
189
190 @classmethod
191 def get_by_name_or_create(cls, key):
192 res = cls.get_by_name(key)
193 if not res:
194 res = cls(key)
195 return res
201 196
202 @classmethod 197 @classmethod
203 def get_app_settings(cls, cache=False): 198 def get_app_settings(cls, cache=False):
204 199
205 ret = cls.query() 200 ret = cls.query()
220 def get_ldap_settings(cls, cache=False): 215 def get_ldap_settings(cls, cache=False):
221 ret = cls.query()\ 216 ret = cls.query()\
222 .filter(cls.app_settings_name.startswith('ldap_')).all() 217 .filter(cls.app_settings_name.startswith('ldap_')).all()
223 fd = {} 218 fd = {}
224 for row in ret: 219 for row in ret:
225 fd.update({row.app_settings_name:row.app_settings_value}) 220 fd.update({row.app_settings_name: row.app_settings_value})
226 221
227 return fd 222 return fd
228 223
229 224
230 class RhodeCodeUi(Base, BaseModel): 225 class RhodeCodeUi(Base, BaseModel):
231 __tablename__ = 'rhodecode_ui' 226 __tablename__ = 'rhodecode_ui'
232 __table_args__ = ( 227 __table_args__ = (
233 UniqueConstraint('ui_key'), 228 UniqueConstraint('ui_key'),
234 {'extend_existing': True, 'mysql_engine':'InnoDB', 229 {'extend_existing': True, 'mysql_engine': 'InnoDB',
235 'mysql_charset': 'utf8'} 230 'mysql_charset': 'utf8'}
236 ) 231 )
237 232
238 HOOK_UPDATE = 'changegroup.update' 233 HOOK_UPDATE = 'changegroup.update'
239 HOOK_REPO_SIZE = 'changegroup.repo_size' 234 HOOK_REPO_SIZE = 'changegroup.repo_size'
240 HOOK_PUSH = 'pretxnchangegroup.push_logger' 235 HOOK_PUSH = 'changegroup.push_logger'
241 HOOK_PULL = 'preoutgoing.pull_logger' 236 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
237 HOOK_PULL = 'outgoing.pull_logger'
238 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
242 239
243 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) 240 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
244 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 241 ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
245 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 242 ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
246 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 243 ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
247 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True) 244 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
248 245
249 @classmethod 246 @classmethod
250 def get_by_key(cls, key): 247 def get_by_key(cls, key):
251 return cls.query().filter(cls.ui_key == key) 248 return cls.query().filter(cls.ui_key == key).scalar()
252 249
253 @classmethod 250 @classmethod
254 def get_builtin_hooks(cls): 251 def get_builtin_hooks(cls):
255 q = cls.query() 252 q = cls.query()
256 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, 253 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
257 cls.HOOK_REPO_SIZE, 254 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
258 cls.HOOK_PUSH, cls.HOOK_PULL])) 255 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
259 return q.all() 256 return q.all()
260 257
261 @classmethod 258 @classmethod
262 def get_custom_hooks(cls): 259 def get_custom_hooks(cls):
263 q = cls.query() 260 q = cls.query()
264 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE, 261 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
265 cls.HOOK_REPO_SIZE, 262 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
266 cls.HOOK_PUSH, cls.HOOK_PULL])) 263 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
267 q = q.filter(cls.ui_section == 'hooks') 264 q = q.filter(cls.ui_section == 'hooks')
268 return q.all() 265 return q.all()
269 266
270 @classmethod 267 @classmethod
268 def get_repos_location(cls):
269 return cls.get_by_key('/').ui_value
270
271 @classmethod
271 def create_or_update_hook(cls, key, val): 272 def create_or_update_hook(cls, key, val):
272 new_ui = cls.get_by_key(key).scalar() or cls() 273 new_ui = cls.get_by_key(key) or cls()
273 new_ui.ui_section = 'hooks' 274 new_ui.ui_section = 'hooks'
274 new_ui.ui_active = True 275 new_ui.ui_active = True
275 new_ui.ui_key = key 276 new_ui.ui_key = key
276 new_ui.ui_value = val 277 new_ui.ui_value = val
277 278
278 Session.add(new_ui) 279 Session().add(new_ui)
279 280
280 281
281 class User(Base, BaseModel): 282 class User(Base, BaseModel):
282 __tablename__ = 'users' 283 __tablename__ = 'users'
283 __table_args__ = ( 284 __table_args__ = (
284 UniqueConstraint('username'), UniqueConstraint('email'), 285 UniqueConstraint('username'), UniqueConstraint('email'),
285 {'extend_existing': True, 'mysql_engine':'InnoDB', 286 Index('u_username_idx', 'username'),
287 Index('u_email_idx', 'email'),
288 {'extend_existing': True, 'mysql_engine': 'InnoDB',
286 'mysql_charset': 'utf8'} 289 'mysql_charset': 'utf8'}
287 ) 290 )
291 DEFAULT_USER = 'default'
292
288 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) 293 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
289 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 294 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
290 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 295 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
291 active = Column("active", Boolean(), nullable=True, unique=None, default=None) 296 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
292 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False) 297 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
293 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 298 name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
294 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 299 lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
295 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 300 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
296 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None) 301 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
297 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 302 ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
298 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 303 api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
304 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
299 305
300 user_log = relationship('UserLog', cascade='all') 306 user_log = relationship('UserLog', cascade='all')
301 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all') 307 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
302 308
303 repositories = relationship('Repository') 309 repositories = relationship('Repository')
310 notifications = relationship('UserNotification', cascade='all') 316 notifications = relationship('UserNotification', cascade='all')
311 # notifications assigned to this user 317 # notifications assigned to this user
312 user_created_notifications = relationship('Notification', cascade='all') 318 user_created_notifications = relationship('Notification', cascade='all')
313 # comments created by this user 319 # comments created by this user
314 user_comments = relationship('ChangesetComment', cascade='all') 320 user_comments = relationship('ChangesetComment', cascade='all')
321 #extra emails for this user
322 user_emails = relationship('UserEmailMap', cascade='all')
315 323
316 @hybrid_property 324 @hybrid_property
317 def email(self): 325 def email(self):
318 return self._email 326 return self._email
319 327
320 @email.setter 328 @email.setter
321 def email(self, val): 329 def email(self, val):
322 self._email = val.lower() if val else None 330 self._email = val.lower() if val else None
323 331
324 @property 332 @property
333 def firstname(self):
334 # alias for future
335 return self.name
336
337 @property
338 def emails(self):
339 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
340 return [self.email] + [x.email for x in other]
341
342 @property
343 def username_and_name(self):
344 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
345
346 @property
325 def full_name(self): 347 def full_name(self):
326 return '%s %s' % (self.name, self.lastname) 348 return '%s %s' % (self.firstname, self.lastname)
327 349
328 @property 350 @property
329 def full_name_or_username(self): 351 def full_name_or_username(self):
330 return ('%s %s' % (self.name, self.lastname) 352 return ('%s %s' % (self.firstname, self.lastname)
331 if (self.name and self.lastname) else self.username) 353 if (self.firstname and self.lastname) else self.username)
332 354
333 @property 355 @property
334 def full_contact(self): 356 def full_contact(self):
335 return '%s %s <%s>' % (self.name, self.lastname, self.email) 357 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
336 358
337 @property 359 @property
338 def short_contact(self): 360 def short_contact(self):
339 return '%s %s' % (self.name, self.lastname) 361 return '%s %s' % (self.firstname, self.lastname)
340 362
341 @property 363 @property
342 def is_admin(self): 364 def is_admin(self):
343 return self.admin 365 return self.admin
344 366
377 else: 399 else:
378 q = cls.query().filter(cls.email == email) 400 q = cls.query().filter(cls.email == email)
379 401
380 if cache: 402 if cache:
381 q = q.options(FromCache("sql_cache_short", 403 q = q.options(FromCache("sql_cache_short",
382 "get_api_key_%s" % email)) 404 "get_email_key_%s" % email))
383 return q.scalar() 405
406 ret = q.scalar()
407 if ret is None:
408 q = UserEmailMap.query()
409 # try fetching in alternate email map
410 if case_insensitive:
411 q = q.filter(UserEmailMap.email.ilike(email))
412 else:
413 q = q.filter(UserEmailMap.email == email)
414 q = q.options(joinedload(UserEmailMap.user))
415 if cache:
416 q = q.options(FromCache("sql_cache_short",
417 "get_email_map_key_%s" % email))
418 ret = getattr(q.scalar(), 'user', None)
419
420 return ret
384 421
385 def update_lastlogin(self): 422 def update_lastlogin(self):
386 """Update user lastlogin""" 423 """Update user lastlogin"""
387 self.last_login = datetime.datetime.now() 424 self.last_login = datetime.datetime.now()
388 Session.add(self) 425 Session().add(self)
389 log.debug('updated user %s lastlogin' % self.username) 426 log.debug('updated user %s lastlogin' % self.username)
390 427
428 def get_api_data(self):
429 """
430 Common function for generating user related data for API
431 """
432 user = self
433 data = dict(
434 user_id=user.user_id,
435 username=user.username,
436 firstname=user.name,
437 lastname=user.lastname,
438 email=user.email,
439 emails=user.emails,
440 api_key=user.api_key,
441 active=user.active,
442 admin=user.admin,
443 ldap_dn=user.ldap_dn,
444 last_login=user.last_login,
445 )
446 return data
447
391 def __json__(self): 448 def __json__(self):
392 return dict( 449 data = dict(
393 user_id=self.user_id,
394 first_name=self.name,
395 last_name=self.lastname,
396 email=self.email,
397 full_name=self.full_name, 450 full_name=self.full_name,
398 full_name_or_username=self.full_name_or_username, 451 full_name_or_username=self.full_name_or_username,
399 short_contact=self.short_contact, 452 short_contact=self.short_contact,
400 full_contact=self.full_contact 453 full_contact=self.full_contact
401 ) 454 )
455 data.update(self.get_api_data())
456 return data
457
458
459 class UserEmailMap(Base, BaseModel):
460 __tablename__ = 'user_email_map'
461 __table_args__ = (
462 Index('uem_email_idx', 'email'),
463 UniqueConstraint('email'),
464 {'extend_existing': True, 'mysql_engine': 'InnoDB',
465 'mysql_charset': 'utf8'}
466 )
467 __mapper_args__ = {}
468
469 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
470 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
471 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
472 user = relationship('User', lazy='joined')
473
474 @validates('_email')
475 def validate_email(self, key, email):
476 # check if this email is not main one
477 main_email = Session().query(User).filter(User.email == email).scalar()
478 if main_email is not None:
479 raise AttributeError('email %s is present is user table' % email)
480 return email
481
482 @hybrid_property
483 def email(self):
484 return self._email
485
486 @email.setter
487 def email(self, val):
488 self._email = val.lower() if val else None
402 489
403 490
404 class UserLog(Base, BaseModel): 491 class UserLog(Base, BaseModel):
405 __tablename__ = 'user_logs' 492 __tablename__ = 'user_logs'
406 __table_args__ = ( 493 __table_args__ = (
407 {'extend_existing': True, 'mysql_engine':'InnoDB', 494 {'extend_existing': True, 'mysql_engine': 'InnoDB',
408 'mysql_charset': 'utf8'}, 495 'mysql_charset': 'utf8'},
409 ) 496 )
410 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) 497 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
411 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None) 498 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
412 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True) 499 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
413 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 500 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
414 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 501 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
415 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 502 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
416 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None) 503 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
417 504
418 @property 505 @property
419 def action_as_day(self): 506 def action_as_day(self):
420 return datetime.date(*self.action_date.timetuple()[:3]) 507 return datetime.date(*self.action_date.timetuple()[:3])
424 511
425 512
426 class UsersGroup(Base, BaseModel): 513 class UsersGroup(Base, BaseModel):
427 __tablename__ = 'users_groups' 514 __tablename__ = 'users_groups'
428 __table_args__ = ( 515 __table_args__ = (
429 {'extend_existing': True, 'mysql_engine':'InnoDB', 516 {'extend_existing': True, 'mysql_engine': 'InnoDB',
430 'mysql_charset': 'utf8'}, 517 'mysql_charset': 'utf8'},
431 ) 518 )
432 519
433 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) 520 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
434 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None) 521 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
435 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None) 522 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
523 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
436 524
437 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined") 525 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
438 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all') 526 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
439 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all') 527 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
440 528
462 if cache: 550 if cache:
463 users_group = users_group.options(FromCache("sql_cache_short", 551 users_group = users_group.options(FromCache("sql_cache_short",
464 "get_users_group_%s" % users_group_id)) 552 "get_users_group_%s" % users_group_id))
465 return users_group.get(users_group_id) 553 return users_group.get(users_group_id)
466 554
555 def get_api_data(self):
556 users_group = self
557
558 data = dict(
559 users_group_id=users_group.users_group_id,
560 group_name=users_group.users_group_name,
561 active=users_group.users_group_active,
562 )
563
564 return data
565
467 566
468 class UsersGroupMember(Base, BaseModel): 567 class UsersGroupMember(Base, BaseModel):
469 __tablename__ = 'users_groups_members' 568 __tablename__ = 'users_groups_members'
470 __table_args__ = ( 569 __table_args__ = (
471 {'extend_existing': True, 'mysql_engine':'InnoDB', 570 {'extend_existing': True, 'mysql_engine': 'InnoDB',
472 'mysql_charset': 'utf8'}, 571 'mysql_charset': 'utf8'},
473 ) 572 )
474 573
475 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) 574 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
476 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None) 575 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
486 585
487 class Repository(Base, BaseModel): 586 class Repository(Base, BaseModel):
488 __tablename__ = 'repositories' 587 __tablename__ = 'repositories'
489 __table_args__ = ( 588 __table_args__ = (
490 UniqueConstraint('repo_name'), 589 UniqueConstraint('repo_name'),
491 {'extend_existing': True, 'mysql_engine':'InnoDB', 590 Index('r_repo_name_idx', 'repo_name'),
591 {'extend_existing': True, 'mysql_engine': 'InnoDB',
492 'mysql_charset': 'utf8'}, 592 'mysql_charset': 'utf8'},
493 ) 593 )
494 594
495 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) 595 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
496 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None) 596 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
497 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None) 597 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
498 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg') 598 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
499 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None) 599 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
500 private = Column("private", Boolean(), nullable=True, unique=None, default=None) 600 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
501 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True) 601 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
502 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True) 602 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
503 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 603 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
504 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now) 604 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
605 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
606 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
607 _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
505 608
506 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None) 609 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
507 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None) 610 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
508 611
509 user = relationship('User') 612 user = relationship('User')
511 group = relationship('RepoGroup') 614 group = relationship('RepoGroup')
512 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id') 615 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
513 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all') 616 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
514 stats = relationship('Statistics', cascade='all', uselist=False) 617 stats = relationship('Statistics', cascade='all', uselist=False)
515 618
516 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all') 619 followers = relationship('UserFollowing',
620 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
621 cascade='all')
517 622
518 logs = relationship('UserLog') 623 logs = relationship('UserLog')
624 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
625
626 pull_requests_org = relationship('PullRequest',
627 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
628 cascade="all, delete, delete-orphan")
629
630 pull_requests_other = relationship('PullRequest',
631 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
632 cascade="all, delete, delete-orphan")
519 633
520 def __unicode__(self): 634 def __unicode__(self):
521 return u"<%s('%s:%s')>" % (self.__class__.__name__,self.repo_id, 635 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
522 self.repo_name) 636 self.repo_name)
523 637
638 @hybrid_property
639 def locked(self):
640 # always should return [user_id, timelocked]
641 if self._locked:
642 _lock_info = self._locked.split(':')
643 return int(_lock_info[0]), _lock_info[1]
644 return [None, None]
645
646 @locked.setter
647 def locked(self, val):
648 if val and isinstance(val, (list, tuple)):
649 self._locked = ':'.join(map(str, val))
650 else:
651 self._locked = None
652
524 @classmethod 653 @classmethod
525 def url_sep(cls): 654 def url_sep(cls):
526 return '/' 655 return URL_SEP
527 656
528 @classmethod 657 @classmethod
529 def get_by_repo_name(cls, repo_name): 658 def get_by_repo_name(cls, repo_name):
530 q = Session.query(cls).filter(cls.repo_name == repo_name) 659 q = Session().query(cls).filter(cls.repo_name == repo_name)
531 q = q.options(joinedload(Repository.fork))\ 660 q = q.options(joinedload(Repository.fork))\
532 .options(joinedload(Repository.user))\ 661 .options(joinedload(Repository.user))\
533 .options(joinedload(Repository.group)) 662 .options(joinedload(Repository.group))
534 return q.scalar() 663 return q.scalar()
535 664
536 @classmethod 665 @classmethod
666 def get_by_full_path(cls, repo_full_path):
667 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
668 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
669
670 @classmethod
537 def get_repo_forks(cls, repo_id): 671 def get_repo_forks(cls, repo_id):
538 return cls.query().filter(Repository.fork_id == repo_id) 672 return cls.query().filter(Repository.fork_id == repo_id)
539 673
540 @classmethod 674 @classmethod
541 def base_path(cls): 675 def base_path(cls):
542 """ 676 """
543 Returns base path when all repos are stored 677 Returns base path when all repos are stored
544 678
545 :param cls: 679 :param cls:
546 """ 680 """
547 q = Session.query(RhodeCodeUi)\ 681 q = Session().query(RhodeCodeUi)\
548 .filter(RhodeCodeUi.ui_key == cls.url_sep()) 682 .filter(RhodeCodeUi.ui_key == cls.url_sep())
549 q = q.options(FromCache("sql_cache_short", "repository_repo_path")) 683 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
550 return q.one().ui_value 684 return q.one().ui_value
685
686 @property
687 def forks(self):
688 """
689 Return forks of this repo
690 """
691 return Repository.get_repo_forks(self.repo_id)
692
693 @property
694 def parent(self):
695 """
696 Returns fork parent
697 """
698 return self.fork
551 699
552 @property 700 @property
553 def just_name(self): 701 def just_name(self):
554 return self.repo_name.split(Repository.url_sep())[-1] 702 return self.repo_name.split(Repository.url_sep())[-1]
555 703
578 def repo_path(self): 726 def repo_path(self):
579 """ 727 """
580 Returns base full path for that repository means where it actually 728 Returns base full path for that repository means where it actually
581 exists on a filesystem 729 exists on a filesystem
582 """ 730 """
583 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == 731 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
584 Repository.url_sep()) 732 Repository.url_sep())
585 q = q.options(FromCache("sql_cache_short", "repository_repo_path")) 733 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
586 return q.one().ui_value 734 return q.one().ui_value
587 735
588 @property 736 @property
624 for ui_ in hg_ui: 772 for ui_ in hg_ui:
625 if ui_.ui_active: 773 if ui_.ui_active:
626 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section, 774 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
627 ui_.ui_key, ui_.ui_value) 775 ui_.ui_key, ui_.ui_value)
628 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value) 776 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
777 if ui_.ui_key == 'push_ssl':
778 # force set push_ssl requirement to False, rhodecode
779 # handles that
780 baseui.setconfig(ui_.ui_section, ui_.ui_key, False)
629 781
630 return baseui 782 return baseui
783
784 @classmethod
785 def inject_ui(cls, repo, extras={}):
786 from rhodecode.lib.vcs.backends.hg import MercurialRepository
787 from rhodecode.lib.vcs.backends.git import GitRepository
788 required = (MercurialRepository, GitRepository)
789 if not isinstance(repo, required):
790 raise Exception('repo must be instance of %s' % required)
791
792 # inject ui extra param to log this action via push logger
793 for k, v in extras.items():
794 repo._repo.ui.setconfig('rhodecode_extras', k, v)
631 795
632 @classmethod 796 @classmethod
633 def is_valid(cls, repo_name): 797 def is_valid(cls, repo_name):
634 """ 798 """
635 returns True if given repo name is a valid filesystem repository 799 returns True if given repo name is a valid filesystem repository
638 :param repo_name: 802 :param repo_name:
639 """ 803 """
640 from rhodecode.lib.utils import is_valid_repo 804 from rhodecode.lib.utils import is_valid_repo
641 805
642 return is_valid_repo(repo_name, cls.base_path()) 806 return is_valid_repo(repo_name, cls.base_path())
807
808 def get_api_data(self):
809 """
810 Common function for generating repo api data
811
812 """
813 repo = self
814 data = dict(
815 repo_id=repo.repo_id,
816 repo_name=repo.repo_name,
817 repo_type=repo.repo_type,
818 clone_uri=repo.clone_uri,
819 private=repo.private,
820 created_on=repo.created_on,
821 description=repo.description,
822 landing_rev=repo.landing_rev,
823 owner=repo.user.username,
824 fork_of=repo.fork.repo_name if repo.fork else None
825 )
826
827 return data
828
829 @classmethod
830 def lock(cls, repo, user_id):
831 repo.locked = [user_id, time.time()]
832 Session().add(repo)
833 Session().commit()
834
835 @classmethod
836 def unlock(cls, repo):
837 repo.locked = None
838 Session().add(repo)
839 Session().commit()
643 840
644 #========================================================================== 841 #==========================================================================
645 # SCM PROPERTIES 842 # SCM PROPERTIES
646 #========================================================================== 843 #==========================================================================
647 844
648 def get_changeset(self, rev=None): 845 def get_changeset(self, rev=None):
649 return get_changeset_safe(self.scm_instance, rev) 846 return get_changeset_safe(self.scm_instance, rev)
650 847
848 def get_landing_changeset(self):
849 """
850 Returns landing changeset, or if that doesn't exist returns the tip
851 """
852 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
853 return cs
854
651 @property 855 @property
652 def tip(self): 856 def tip(self):
653 return self.get_changeset('tip') 857 return self.get_changeset('tip')
654 858
655 @property 859 @property
658 862
659 @property 863 @property
660 def last_change(self): 864 def last_change(self):
661 return self.scm_instance.last_change 865 return self.scm_instance.last_change
662 866
663 def comments(self, revisions=None): 867 def get_comments(self, revisions=None):
664 """ 868 """
665 Returns comments for this repository grouped by revisions 869 Returns comments for this repository grouped by revisions
666 870
667 :param revisions: filter query by revisions only 871 :param revisions: filter query by revisions only
668 """ 872 """
673 grouped = defaultdict(list) 877 grouped = defaultdict(list)
674 for cmt in cmts.all(): 878 for cmt in cmts.all():
675 grouped[cmt.revision].append(cmt) 879 grouped[cmt.revision].append(cmt)
676 return grouped 880 return grouped
677 881
882 def statuses(self, revisions=None):
883 """
884 Returns statuses for this repository
885
886 :param revisions: list of revisions to get statuses for
887 :type revisions: list
888 """
889
890 statuses = ChangesetStatus.query()\
891 .filter(ChangesetStatus.repo == self)\
892 .filter(ChangesetStatus.version == 0)
893 if revisions:
894 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
895 grouped = {}
896
897 #maybe we have open new pullrequest without a status ?
898 stat = ChangesetStatus.STATUS_UNDER_REVIEW
899 status_lbl = ChangesetStatus.get_status_lbl(stat)
900 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
901 for rev in pr.revisions:
902 pr_id = pr.pull_request_id
903 pr_repo = pr.other_repo.repo_name
904 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
905
906 for stat in statuses.all():
907 pr_id = pr_repo = None
908 if stat.pull_request:
909 pr_id = stat.pull_request.pull_request_id
910 pr_repo = stat.pull_request.other_repo.repo_name
911 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
912 pr_id, pr_repo]
913 return grouped
914
678 #========================================================================== 915 #==========================================================================
679 # SCM CACHE INSTANCE 916 # SCM CACHE INSTANCE
680 #========================================================================== 917 #==========================================================================
681 918
682 @property 919 @property
691 928
692 @LazyProperty 929 @LazyProperty
693 def scm_instance(self): 930 def scm_instance(self):
694 return self.__get_instance() 931 return self.__get_instance()
695 932
696 @property 933 def scm_instance_cached(self, cache_map=None):
697 def scm_instance_cached(self):
698 @cache_region('long_term') 934 @cache_region('long_term')
699 def _c(repo_name): 935 def _c(repo_name):
700 return self.__get_instance() 936 return self.__get_instance()
701 rn = self.repo_name 937 rn = self.repo_name
702 log.debug('Getting cached instance of repo') 938 log.debug('Getting cached instance of repo')
703 inv = self.invalidate 939
704 if inv is not None: 940 if cache_map:
941 # get using prefilled cache_map
942 invalidate_repo = cache_map[self.repo_name]
943 if invalidate_repo:
944 invalidate_repo = (None if invalidate_repo.cache_active
945 else invalidate_repo)
946 else:
947 # get from invalidate
948 invalidate_repo = self.invalidate
949
950 if invalidate_repo is not None:
705 region_invalidate(_c, None, rn) 951 region_invalidate(_c, None, rn)
706 # update our cache 952 # update our cache
707 CacheInvalidation.set_valid(inv.cache_key) 953 CacheInvalidation.set_valid(invalidate_repo.cache_key)
708 return _c(rn) 954 return _c(rn)
709 955
710 def __get_instance(self): 956 def __get_instance(self):
711 repo_full_path = self.repo_full_path 957 repo_full_path = self.repo_full_path
712 try: 958 try:
736 class RepoGroup(Base, BaseModel): 982 class RepoGroup(Base, BaseModel):
737 __tablename__ = 'groups' 983 __tablename__ = 'groups'
738 __table_args__ = ( 984 __table_args__ = (
739 UniqueConstraint('group_name', 'group_parent_id'), 985 UniqueConstraint('group_name', 'group_parent_id'),
740 CheckConstraint('group_id != group_parent_id'), 986 CheckConstraint('group_id != group_parent_id'),
741 {'extend_existing': True, 'mysql_engine':'InnoDB', 987 {'extend_existing': True, 'mysql_engine': 'InnoDB',
742 'mysql_charset': 'utf8'}, 988 'mysql_charset': 'utf8'},
743 ) 989 )
744 __mapper_args__ = {'order_by': 'group_name'} 990 __mapper_args__ = {'order_by': 'group_name'}
745 991
746 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) 992 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
747 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None) 993 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
748 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None) 994 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
749 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 995 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
996 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
750 997
751 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id') 998 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
752 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all') 999 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
753 1000
754 parent_group = relationship('RepoGroup', remote_side=group_id) 1001 parent_group = relationship('RepoGroup', remote_side=group_id)
774 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0]) 1021 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
775 return repo_groups 1022 return repo_groups
776 1023
777 @classmethod 1024 @classmethod
778 def url_sep(cls): 1025 def url_sep(cls):
779 return '/' 1026 return URL_SEP
780 1027
781 @classmethod 1028 @classmethod
782 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False): 1029 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
783 if case_insensitive: 1030 if case_insensitive:
784 gr = cls.query()\ 1031 gr = cls.query()\
851 cnt += children_count(child) 1098 cnt += children_count(child)
852 return cnt 1099 return cnt
853 1100
854 return cnt + children_count(self) 1101 return cnt + children_count(self)
855 1102
1103 def recursive_groups_and_repos(self):
1104 """
1105 Recursive return all groups, with repositories in those groups
1106 """
1107 all_ = []
1108
1109 def _get_members(root_gr):
1110 for r in root_gr.repositories:
1111 all_.append(r)
1112 childs = root_gr.children.all()
1113 if childs:
1114 for gr in childs:
1115 all_.append(gr)
1116 _get_members(gr)
1117
1118 _get_members(self)
1119 return [self] + all_
1120
856 def get_new_name(self, group_name): 1121 def get_new_name(self, group_name):
857 """ 1122 """
858 returns new full group name based on parent and new name 1123 returns new full group name based on parent and new name
859 1124
860 :param group_name: 1125 :param group_name:
865 1130
866 1131
867 class Permission(Base, BaseModel): 1132 class Permission(Base, BaseModel):
868 __tablename__ = 'permissions' 1133 __tablename__ = 'permissions'
869 __table_args__ = ( 1134 __table_args__ = (
870 {'extend_existing': True, 'mysql_engine':'InnoDB', 1135 Index('p_perm_name_idx', 'permission_name'),
1136 {'extend_existing': True, 'mysql_engine': 'InnoDB',
871 'mysql_charset': 'utf8'}, 1137 'mysql_charset': 'utf8'},
872 ) 1138 )
1139 PERMS = [
1140 ('repository.none', _('Repository no access')),
1141 ('repository.read', _('Repository read access')),
1142 ('repository.write', _('Repository write access')),
1143 ('repository.admin', _('Repository admin access')),
1144
1145 ('group.none', _('Repositories Group no access')),
1146 ('group.read', _('Repositories Group read access')),
1147 ('group.write', _('Repositories Group write access')),
1148 ('group.admin', _('Repositories Group admin access')),
1149
1150 ('hg.admin', _('RhodeCode Administrator')),
1151 ('hg.create.none', _('Repository creation disabled')),
1152 ('hg.create.repository', _('Repository creation enabled')),
1153 ('hg.fork.none', _('Repository forking disabled')),
1154 ('hg.fork.repository', _('Repository forking enabled')),
1155 ('hg.register.none', _('Register disabled')),
1156 ('hg.register.manual_activate', _('Register new user with RhodeCode '
1157 'with manual activation')),
1158
1159 ('hg.register.auto_activate', _('Register new user with RhodeCode '
1160 'with auto activation')),
1161 ]
1162
1163 # defines which permissions are more important higher the more important
1164 PERM_WEIGHTS = {
1165 'repository.none': 0,
1166 'repository.read': 1,
1167 'repository.write': 3,
1168 'repository.admin': 4,
1169
1170 'group.none': 0,
1171 'group.read': 1,
1172 'group.write': 3,
1173 'group.admin': 4,
1174
1175 'hg.fork.none': 0,
1176 'hg.fork.repository': 1,
1177 'hg.create.none': 0,
1178 'hg.create.repository':1
1179 }
1180
873 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) 1181 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
874 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 1182 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
875 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 1183 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
876 1184
877 def __unicode__(self): 1185 def __unicode__(self):
878 return u"<%s('%s:%s')>" % ( 1186 return u"<%s('%s:%s')>" % (
879 self.__class__.__name__, self.permission_id, self.permission_name 1187 self.__class__.__name__, self.permission_id, self.permission_name
880 ) 1188 )
883 def get_by_key(cls, key): 1191 def get_by_key(cls, key):
884 return cls.query().filter(cls.permission_name == key).scalar() 1192 return cls.query().filter(cls.permission_name == key).scalar()
885 1193
886 @classmethod 1194 @classmethod
887 def get_default_perms(cls, default_user_id): 1195 def get_default_perms(cls, default_user_id):
888 q = Session.query(UserRepoToPerm, Repository, cls)\ 1196 q = Session().query(UserRepoToPerm, Repository, cls)\
889 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\ 1197 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
890 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\ 1198 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
891 .filter(UserRepoToPerm.user_id == default_user_id) 1199 .filter(UserRepoToPerm.user_id == default_user_id)
892 1200
893 return q.all() 1201 return q.all()
894 1202
895 @classmethod 1203 @classmethod
896 def get_default_group_perms(cls, default_user_id): 1204 def get_default_group_perms(cls, default_user_id):
897 q = Session.query(UserRepoGroupToPerm, RepoGroup, cls)\ 1205 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
898 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\ 1206 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
899 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\ 1207 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
900 .filter(UserRepoGroupToPerm.user_id == default_user_id) 1208 .filter(UserRepoGroupToPerm.user_id == default_user_id)
901 1209
902 return q.all() 1210 return q.all()
904 1212
905 class UserRepoToPerm(Base, BaseModel): 1213 class UserRepoToPerm(Base, BaseModel):
906 __tablename__ = 'repo_to_perm' 1214 __tablename__ = 'repo_to_perm'
907 __table_args__ = ( 1215 __table_args__ = (
908 UniqueConstraint('user_id', 'repository_id', 'permission_id'), 1216 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
909 {'extend_existing': True, 'mysql_engine':'InnoDB', 1217 {'extend_existing': True, 'mysql_engine': 'InnoDB',
910 'mysql_charset': 'utf8'} 1218 'mysql_charset': 'utf8'}
911 ) 1219 )
912 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) 1220 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
913 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None) 1221 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
914 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None) 1222 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
922 def create(cls, user, repository, permission): 1230 def create(cls, user, repository, permission):
923 n = cls() 1231 n = cls()
924 n.user = user 1232 n.user = user
925 n.repository = repository 1233 n.repository = repository
926 n.permission = permission 1234 n.permission = permission
927 Session.add(n) 1235 Session().add(n)
928 return n 1236 return n
929 1237
930 def __unicode__(self): 1238 def __unicode__(self):
931 return u'<user:%s => %s >' % (self.user, self.repository) 1239 return u'<user:%s => %s >' % (self.user, self.repository)
932 1240
933 1241
934 class UserToPerm(Base, BaseModel): 1242 class UserToPerm(Base, BaseModel):
935 __tablename__ = 'user_to_perm' 1243 __tablename__ = 'user_to_perm'
936 __table_args__ = ( 1244 __table_args__ = (
937 UniqueConstraint('user_id', 'permission_id'), 1245 UniqueConstraint('user_id', 'permission_id'),
938 {'extend_existing': True, 'mysql_engine':'InnoDB', 1246 {'extend_existing': True, 'mysql_engine': 'InnoDB',
939 'mysql_charset': 'utf8'} 1247 'mysql_charset': 'utf8'}
940 ) 1248 )
941 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) 1249 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
942 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None) 1250 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
943 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None) 1251 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
948 1256
949 class UsersGroupRepoToPerm(Base, BaseModel): 1257 class UsersGroupRepoToPerm(Base, BaseModel):
950 __tablename__ = 'users_group_repo_to_perm' 1258 __tablename__ = 'users_group_repo_to_perm'
951 __table_args__ = ( 1259 __table_args__ = (
952 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), 1260 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
953 {'extend_existing': True, 'mysql_engine':'InnoDB', 1261 {'extend_existing': True, 'mysql_engine': 'InnoDB',
954 'mysql_charset': 'utf8'} 1262 'mysql_charset': 'utf8'}
955 ) 1263 )
956 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) 1264 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
957 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None) 1265 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
958 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None) 1266 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
966 def create(cls, users_group, repository, permission): 1274 def create(cls, users_group, repository, permission):
967 n = cls() 1275 n = cls()
968 n.users_group = users_group 1276 n.users_group = users_group
969 n.repository = repository 1277 n.repository = repository
970 n.permission = permission 1278 n.permission = permission
971 Session.add(n) 1279 Session().add(n)
972 return n 1280 return n
973 1281
974 def __unicode__(self): 1282 def __unicode__(self):
975 return u'<userGroup:%s => %s >' % (self.users_group, self.repository) 1283 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
976 1284
977 1285
978 class UsersGroupToPerm(Base, BaseModel): 1286 class UsersGroupToPerm(Base, BaseModel):
979 __tablename__ = 'users_group_to_perm' 1287 __tablename__ = 'users_group_to_perm'
980 __table_args__ = ( 1288 __table_args__ = (
981 UniqueConstraint('users_group_id', 'permission_id',), 1289 UniqueConstraint('users_group_id', 'permission_id',),
982 {'extend_existing': True, 'mysql_engine':'InnoDB', 1290 {'extend_existing': True, 'mysql_engine': 'InnoDB',
983 'mysql_charset': 'utf8'} 1291 'mysql_charset': 'utf8'}
984 ) 1292 )
985 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) 1293 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
986 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None) 1294 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
987 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None) 1295 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
992 1300
993 class UserRepoGroupToPerm(Base, BaseModel): 1301 class UserRepoGroupToPerm(Base, BaseModel):
994 __tablename__ = 'user_repo_group_to_perm' 1302 __tablename__ = 'user_repo_group_to_perm'
995 __table_args__ = ( 1303 __table_args__ = (
996 UniqueConstraint('user_id', 'group_id', 'permission_id'), 1304 UniqueConstraint('user_id', 'group_id', 'permission_id'),
997 {'extend_existing': True, 'mysql_engine':'InnoDB', 1305 {'extend_existing': True, 'mysql_engine': 'InnoDB',
998 'mysql_charset': 'utf8'} 1306 'mysql_charset': 'utf8'}
999 ) 1307 )
1000 1308
1001 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) 1309 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1002 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None) 1310 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1010 1318
1011 class UsersGroupRepoGroupToPerm(Base, BaseModel): 1319 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1012 __tablename__ = 'users_group_repo_group_to_perm' 1320 __tablename__ = 'users_group_repo_group_to_perm'
1013 __table_args__ = ( 1321 __table_args__ = (
1014 UniqueConstraint('users_group_id', 'group_id'), 1322 UniqueConstraint('users_group_id', 'group_id'),
1015 {'extend_existing': True, 'mysql_engine':'InnoDB', 1323 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1016 'mysql_charset': 'utf8'} 1324 'mysql_charset': 'utf8'}
1017 ) 1325 )
1018 1326
1019 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) 1327 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)
1020 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None) 1328 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1028 1336
1029 class Statistics(Base, BaseModel): 1337 class Statistics(Base, BaseModel):
1030 __tablename__ = 'statistics' 1338 __tablename__ = 'statistics'
1031 __table_args__ = ( 1339 __table_args__ = (
1032 UniqueConstraint('repository_id'), 1340 UniqueConstraint('repository_id'),
1033 {'extend_existing': True, 'mysql_engine':'InnoDB', 1341 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1034 'mysql_charset': 'utf8'} 1342 'mysql_charset': 'utf8'}
1035 ) 1343 )
1036 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) 1344 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1037 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None) 1345 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1038 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False) 1346 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1046 class UserFollowing(Base, BaseModel): 1354 class UserFollowing(Base, BaseModel):
1047 __tablename__ = 'user_followings' 1355 __tablename__ = 'user_followings'
1048 __table_args__ = ( 1356 __table_args__ = (
1049 UniqueConstraint('user_id', 'follows_repository_id'), 1357 UniqueConstraint('user_id', 'follows_repository_id'),
1050 UniqueConstraint('user_id', 'follows_user_id'), 1358 UniqueConstraint('user_id', 'follows_user_id'),
1051 {'extend_existing': True, 'mysql_engine':'InnoDB', 1359 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1052 'mysql_charset': 'utf8'} 1360 'mysql_charset': 'utf8'}
1053 ) 1361 )
1054 1362
1055 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) 1363 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1056 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None) 1364 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1070 1378
1071 class CacheInvalidation(Base, BaseModel): 1379 class CacheInvalidation(Base, BaseModel):
1072 __tablename__ = 'cache_invalidation' 1380 __tablename__ = 'cache_invalidation'
1073 __table_args__ = ( 1381 __table_args__ = (
1074 UniqueConstraint('cache_key'), 1382 UniqueConstraint('cache_key'),
1075 {'extend_existing': True, 'mysql_engine':'InnoDB', 1383 Index('key_idx', 'cache_key'),
1384 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1076 'mysql_charset': 'utf8'}, 1385 'mysql_charset': 'utf8'},
1077 ) 1386 )
1078 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) 1387 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1079 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 1388 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1080 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) 1389 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1081 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False) 1390 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1082 1391
1083 def __init__(self, cache_key, cache_args=''): 1392 def __init__(self, cache_key, cache_args=''):
1084 self.cache_key = cache_key 1393 self.cache_key = cache_key
1085 self.cache_args = cache_args 1394 self.cache_args = cache_args
1086 self.cache_active = False 1395 self.cache_active = False
1087 1396
1088 def __unicode__(self): 1397 def __unicode__(self):
1089 return u"<%s('%s:%s')>" % (self.__class__.__name__, 1398 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1090 self.cache_id, self.cache_key) 1399 self.cache_id, self.cache_key)
1400
1091 @classmethod 1401 @classmethod
1092 def clear_cache(cls): 1402 def clear_cache(cls):
1093 cls.query().delete() 1403 cls.query().delete()
1094 1404
1095 @classmethod 1405 @classmethod
1110 def get_by_key(cls, key): 1420 def get_by_key(cls, key):
1111 return cls.query().filter(cls.cache_key == key).scalar() 1421 return cls.query().filter(cls.cache_key == key).scalar()
1112 1422
1113 @classmethod 1423 @classmethod
1114 def _get_or_create_key(cls, key, prefix, org_key): 1424 def _get_or_create_key(cls, key, prefix, org_key):
1115 inv_obj = Session.query(cls).filter(cls.cache_key == key).scalar() 1425 inv_obj = Session().query(cls).filter(cls.cache_key == key).scalar()
1116 if not inv_obj: 1426 if not inv_obj:
1117 try: 1427 try:
1118 inv_obj = CacheInvalidation(key, org_key) 1428 inv_obj = CacheInvalidation(key, org_key)
1119 Session.add(inv_obj) 1429 Session().add(inv_obj)
1120 Session.commit() 1430 Session().commit()
1121 except Exception: 1431 except Exception:
1122 log.error(traceback.format_exc()) 1432 log.error(traceback.format_exc())
1123 Session.rollback() 1433 Session().rollback()
1124 return inv_obj 1434 return inv_obj
1125 1435
1126 @classmethod 1436 @classmethod
1127 def invalidate(cls, key): 1437 def invalidate(cls, key):
1128 """ 1438 """
1146 1456
1147 :param key: 1457 :param key:
1148 """ 1458 """
1149 1459
1150 key, _prefix, _org_key = cls._get_key(key) 1460 key, _prefix, _org_key = cls._get_key(key)
1151 inv_objs = Session.query(cls).filter(cls.cache_args == _org_key).all() 1461 inv_objs = Session().query(cls).filter(cls.cache_args == _org_key).all()
1152 log.debug('marking %s key[s] %s for invalidation' % (len(inv_objs), 1462 log.debug('marking %s key[s] %s for invalidation' % (len(inv_objs),
1153 _org_key)) 1463 _org_key))
1154 try: 1464 try:
1155 for inv_obj in inv_objs: 1465 for inv_obj in inv_objs:
1156 if inv_obj: 1466 if inv_obj:
1157 inv_obj.cache_active = False 1467 inv_obj.cache_active = False
1158 1468
1159 Session.add(inv_obj) 1469 Session().add(inv_obj)
1160 Session.commit() 1470 Session().commit()
1161 except Exception: 1471 except Exception:
1162 log.error(traceback.format_exc()) 1472 log.error(traceback.format_exc())
1163 Session.rollback() 1473 Session().rollback()
1164 1474
1165 @classmethod 1475 @classmethod
1166 def set_valid(cls, key): 1476 def set_valid(cls, key):
1167 """ 1477 """
1168 Mark this cache key as active and currently cached 1478 Mark this cache key as active and currently cached
1169 1479
1170 :param key: 1480 :param key:
1171 """ 1481 """
1172 inv_obj = cls.get_by_key(key) 1482 inv_obj = cls.get_by_key(key)
1173 inv_obj.cache_active = True 1483 inv_obj.cache_active = True
1174 Session.add(inv_obj) 1484 Session().add(inv_obj)
1175 Session.commit() 1485 Session().commit()
1486
1487 @classmethod
1488 def get_cache_map(cls):
1489
1490 class cachemapdict(dict):
1491
1492 def __init__(self, *args, **kwargs):
1493 fixkey = kwargs.get('fixkey')
1494 if fixkey:
1495 del kwargs['fixkey']
1496 self.fixkey = fixkey
1497 super(cachemapdict, self).__init__(*args, **kwargs)
1498
1499 def __getattr__(self, name):
1500 key = name
1501 if self.fixkey:
1502 key, _prefix, _org_key = cls._get_key(key)
1503 if key in self.__dict__:
1504 return self.__dict__[key]
1505 else:
1506 return self[key]
1507
1508 def __getitem__(self, key):
1509 if self.fixkey:
1510 key, _prefix, _org_key = cls._get_key(key)
1511 try:
1512 return super(cachemapdict, self).__getitem__(key)
1513 except KeyError:
1514 return
1515
1516 cache_map = cachemapdict(fixkey=True)
1517 for obj in cls.query().all():
1518 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1519 return cache_map
1176 1520
1177 1521
1178 class ChangesetComment(Base, BaseModel): 1522 class ChangesetComment(Base, BaseModel):
1179 __tablename__ = 'changeset_comments' 1523 __tablename__ = 'changeset_comments'
1180 __table_args__ = ( 1524 __table_args__ = (
1181 {'extend_existing': True, 'mysql_engine':'InnoDB', 1525 Index('cc_revision_idx', 'revision'),
1526 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1182 'mysql_charset': 'utf8'}, 1527 'mysql_charset': 'utf8'},
1183 ) 1528 )
1184 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True) 1529 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1185 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False) 1530 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1186 revision = Column('revision', String(40), nullable=False) 1531 revision = Column('revision', String(40), nullable=True)
1532 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1187 line_no = Column('line_no', Unicode(10), nullable=True) 1533 line_no = Column('line_no', Unicode(10), nullable=True)
1534 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1188 f_path = Column('f_path', Unicode(1000), nullable=True) 1535 f_path = Column('f_path', Unicode(1000), nullable=True)
1189 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False) 1536 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1190 text = Column('text', Unicode(25000), nullable=False) 1537 text = Column('text', Unicode(25000), nullable=False)
1191 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now) 1538 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1539 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1192 1540
1193 author = relationship('User', lazy='joined') 1541 author = relationship('User', lazy='joined')
1194 repo = relationship('Repository') 1542 repo = relationship('Repository')
1195 1543 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1196 @classmethod 1544 pull_request = relationship('PullRequest', lazy='joined')
1197 def get_users(cls, revision): 1545
1198 """ 1546 @classmethod
1199 Returns user associated with this changesetComment. ie those 1547 def get_users(cls, revision=None, pull_request_id=None):
1548 """
1549 Returns user associated with this ChangesetComment. ie those
1200 who actually commented 1550 who actually commented
1201 1551
1202 :param cls: 1552 :param cls:
1203 :param revision: 1553 :param revision:
1204 """ 1554 """
1205 return Session.query(User)\ 1555 q = Session().query(User)\
1206 .filter(cls.revision == revision)\ 1556 .join(ChangesetComment.author)
1207 .join(ChangesetComment.author).all() 1557 if revision:
1558 q = q.filter(cls.revision == revision)
1559 elif pull_request_id:
1560 q = q.filter(cls.pull_request_id == pull_request_id)
1561 return q.all()
1562
1563
1564 class ChangesetStatus(Base, BaseModel):
1565 __tablename__ = 'changeset_statuses'
1566 __table_args__ = (
1567 Index('cs_revision_idx', 'revision'),
1568 Index('cs_version_idx', 'version'),
1569 UniqueConstraint('repo_id', 'revision', 'version'),
1570 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1571 'mysql_charset': 'utf8'}
1572 )
1573 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1574 STATUS_APPROVED = 'approved'
1575 STATUS_REJECTED = 'rejected'
1576 STATUS_UNDER_REVIEW = 'under_review'
1577
1578 STATUSES = [
1579 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1580 (STATUS_APPROVED, _("Approved")),
1581 (STATUS_REJECTED, _("Rejected")),
1582 (STATUS_UNDER_REVIEW, _("Under Review")),
1583 ]
1584
1585 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1586 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1587 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1588 revision = Column('revision', String(40), nullable=False)
1589 status = Column('status', String(128), nullable=False, default=DEFAULT)
1590 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1591 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1592 version = Column('version', Integer(), nullable=False, default=0)
1593 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1594
1595 author = relationship('User', lazy='joined')
1596 repo = relationship('Repository')
1597 comment = relationship('ChangesetComment', lazy='joined')
1598 pull_request = relationship('PullRequest', lazy='joined')
1599
1600 def __unicode__(self):
1601 return u"<%s('%s:%s')>" % (
1602 self.__class__.__name__,
1603 self.status, self.author
1604 )
1605
1606 @classmethod
1607 def get_status_lbl(cls, value):
1608 return dict(cls.STATUSES).get(value)
1609
1610 @property
1611 def status_lbl(self):
1612 return ChangesetStatus.get_status_lbl(self.status)
1613
1614
1615 class PullRequest(Base, BaseModel):
1616 __tablename__ = 'pull_requests'
1617 __table_args__ = (
1618 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1619 'mysql_charset': 'utf8'},
1620 )
1621
1622 STATUS_NEW = u'new'
1623 STATUS_OPEN = u'open'
1624 STATUS_CLOSED = u'closed'
1625
1626 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1627 title = Column('title', Unicode(256), nullable=True)
1628 description = Column('description', UnicodeText(10240), nullable=True)
1629 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1630 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1631 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1632 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1633 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1634 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1635 org_ref = Column('org_ref', Unicode(256), nullable=False)
1636 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1637 other_ref = Column('other_ref', Unicode(256), nullable=False)
1638
1639 @hybrid_property
1640 def revisions(self):
1641 return self._revisions.split(':')
1642
1643 @revisions.setter
1644 def revisions(self, val):
1645 self._revisions = ':'.join(val)
1646
1647 author = relationship('User', lazy='joined')
1648 reviewers = relationship('PullRequestReviewers',
1649 cascade="all, delete, delete-orphan")
1650 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1651 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1652 statuses = relationship('ChangesetStatus')
1653 comments = relationship('ChangesetComment',
1654 cascade="all, delete, delete-orphan")
1655
1656 def is_closed(self):
1657 return self.status == self.STATUS_CLOSED
1658
1659 def __json__(self):
1660 return dict(
1661 revisions=self.revisions
1662 )
1663
1664
1665 class PullRequestReviewers(Base, BaseModel):
1666 __tablename__ = 'pull_request_reviewers'
1667 __table_args__ = (
1668 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1669 'mysql_charset': 'utf8'},
1670 )
1671
1672 def __init__(self, user=None, pull_request=None):
1673 self.user = user
1674 self.pull_request = pull_request
1675
1676 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1677 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1678 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1679
1680 user = relationship('User')
1681 pull_request = relationship('PullRequest')
1208 1682
1209 1683
1210 class Notification(Base, BaseModel): 1684 class Notification(Base, BaseModel):
1211 __tablename__ = 'notifications' 1685 __tablename__ = 'notifications'
1212 __table_args__ = ( 1686 __table_args__ = (
1213 {'extend_existing': True, 'mysql_engine':'InnoDB', 1687 Index('notification_type_idx', 'type'),
1688 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1214 'mysql_charset': 'utf8'}, 1689 'mysql_charset': 'utf8'},
1215 ) 1690 )
1216 1691
1217 TYPE_CHANGESET_COMMENT = u'cs_comment' 1692 TYPE_CHANGESET_COMMENT = u'cs_comment'
1218 TYPE_MESSAGE = u'message' 1693 TYPE_MESSAGE = u'message'
1219 TYPE_MENTION = u'mention' 1694 TYPE_MENTION = u'mention'
1220 TYPE_REGISTRATION = u'registration' 1695 TYPE_REGISTRATION = u'registration'
1696 TYPE_PULL_REQUEST = u'pull_request'
1697 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1221 1698
1222 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True) 1699 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1223 subject = Column('subject', Unicode(512), nullable=True) 1700 subject = Column('subject', Unicode(512), nullable=True)
1224 body = Column('body', Unicode(50000), nullable=True) 1701 body = Column('body', UnicodeText(50000), nullable=True)
1225 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True) 1702 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1226 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now) 1703 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1227 type_ = Column('type', Unicode(256)) 1704 type_ = Column('type', Unicode(256))
1228 1705
1229 created_by_user = relationship('User') 1706 created_by_user = relationship('User')
1232 1709
1233 @property 1710 @property
1234 def recipients(self): 1711 def recipients(self):
1235 return [x.user for x in UserNotification.query()\ 1712 return [x.user for x in UserNotification.query()\
1236 .filter(UserNotification.notification == self)\ 1713 .filter(UserNotification.notification == self)\
1237 .order_by(UserNotification.user).all()] 1714 .order_by(UserNotification.user_id.asc()).all()]
1238 1715
1239 @classmethod 1716 @classmethod
1240 def create(cls, created_by, subject, body, recipients, type_=None): 1717 def create(cls, created_by, subject, body, recipients, type_=None):
1241 if type_ is None: 1718 if type_ is None:
1242 type_ = Notification.TYPE_MESSAGE 1719 type_ = Notification.TYPE_MESSAGE
1250 1727
1251 for u in recipients: 1728 for u in recipients:
1252 assoc = UserNotification() 1729 assoc = UserNotification()
1253 assoc.notification = notification 1730 assoc.notification = notification
1254 u.notifications.append(assoc) 1731 u.notifications.append(assoc)
1255 Session.add(notification) 1732 Session().add(notification)
1256 return notification 1733 return notification
1257 1734
1258 @property 1735 @property
1259 def description(self): 1736 def description(self):
1260 from rhodecode.model.notification import NotificationModel 1737 from rhodecode.model.notification import NotificationModel
1263 1740
1264 class UserNotification(Base, BaseModel): 1741 class UserNotification(Base, BaseModel):
1265 __tablename__ = 'user_to_notification' 1742 __tablename__ = 'user_to_notification'
1266 __table_args__ = ( 1743 __table_args__ = (
1267 UniqueConstraint('user_id', 'notification_id'), 1744 UniqueConstraint('user_id', 'notification_id'),
1268 {'extend_existing': True, 'mysql_engine':'InnoDB', 1745 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1269 'mysql_charset': 'utf8'} 1746 'mysql_charset': 'utf8'}
1270 ) 1747 )
1271 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True) 1748 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1272 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True) 1749 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1273 read = Column('read', Boolean, default=False) 1750 read = Column('read', Boolean, default=False)
1277 notification = relationship('Notification', lazy="joined", 1754 notification = relationship('Notification', lazy="joined",
1278 order_by=lambda: Notification.created_on.desc(),) 1755 order_by=lambda: Notification.created_on.desc(),)
1279 1756
1280 def mark_as_read(self): 1757 def mark_as_read(self):
1281 self.read = True 1758 self.read = True
1282 Session.add(self) 1759 Session().add(self)
1283 1760
1284 1761
1285 class DbMigrateVersion(Base, BaseModel): 1762 class DbMigrateVersion(Base, BaseModel):
1286 __tablename__ = 'db_migrate_version' 1763 __tablename__ = 'db_migrate_version'
1287 __table_args__ = ( 1764 __table_args__ = (
1288 {'extend_existing': True, 'mysql_engine':'InnoDB', 1765 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1289 'mysql_charset': 'utf8'}, 1766 'mysql_charset': 'utf8'},
1290 ) 1767 )
1291 repository_id = Column('repository_id', String(250), primary_key=True) 1768 repository_id = Column('repository_id', String(250), primary_key=True)
1292 repository_path = Column('repository_path', Text) 1769 repository_path = Column('repository_path', Text)
1293 version = Column('version', Integer) 1770 version = Column('version', Integer)