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