Mercurial > kallithea
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) |