##// END OF EJS Templates
database: dropped CheckConstraint for auto-increment field for mysql/mariadb compatability....
marcink -
r3486:118155b6 default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,1044 +1,1044 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import os
22 22 import logging
23 23 import datetime
24 24 import traceback
25 25 from datetime import date
26 26
27 27 from sqlalchemy import *
28 28 from sqlalchemy.ext.hybrid import hybrid_property
29 29 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
30 30 from beaker.cache import cache_region, region_invalidate
31 31 from pyramid import compat
32 32
33 33 from rhodecode.lib.vcs import get_backend
34 34 from rhodecode.lib.vcs.utils.helpers import get_scm
35 35 from rhodecode.lib.vcs.exceptions import VCSError
36 36 from zope.cachedescriptors.property import Lazy as LazyProperty
37 37 from rhodecode.lib.auth import generate_auth_token
38 38 from rhodecode.lib.utils2 import str2bool, safe_str, get_commit_safe, safe_unicode
39 39 from rhodecode.lib.exceptions import UserGroupAssignedException
40 40 from rhodecode.lib.ext_json import json
41 41
42 42 from rhodecode.model.meta import Base, Session
43 43 from rhodecode.lib.caching_query import FromCache
44 44
45 45
46 46 log = logging.getLogger(__name__)
47 47
48 48 #==============================================================================
49 49 # BASE CLASSES
50 50 #==============================================================================
51 51
52 52 class ModelSerializer(json.JSONEncoder):
53 53 """
54 54 Simple Serializer for JSON,
55 55
56 56 usage::
57 57
58 58 to make object customized for serialization implement a __json__
59 59 method that will return a dict for serialization into json
60 60
61 61 example::
62 62
63 63 class Task(object):
64 64
65 65 def __init__(self, name, value):
66 66 self.name = name
67 67 self.value = value
68 68
69 69 def __json__(self):
70 70 return dict(name=self.name,
71 71 value=self.value)
72 72
73 73 """
74 74
75 75 def default(self, obj):
76 76
77 77 if hasattr(obj, '__json__'):
78 78 return obj.__json__()
79 79 else:
80 80 return json.JSONEncoder.default(self, obj)
81 81
82 82 class BaseModel(object):
83 83 """Base Model for all classess
84 84
85 85 """
86 86
87 87 @classmethod
88 88 def _get_keys(cls):
89 89 """return column names for this model """
90 90 return class_mapper(cls).c.keys()
91 91
92 92 def get_dict(self):
93 93 """return dict with keys and values corresponding
94 94 to this model data """
95 95
96 96 d = {}
97 97 for k in self._get_keys():
98 98 d[k] = getattr(self, k)
99 99 return d
100 100
101 101 def get_appstruct(self):
102 102 """return list with keys and values tupples corresponding
103 103 to this model data """
104 104
105 105 l = []
106 106 for k in self._get_keys():
107 107 l.append((k, getattr(self, k),))
108 108 return l
109 109
110 110 def populate_obj(self, populate_dict):
111 111 """populate model with data from given populate_dict"""
112 112
113 113 for k in self._get_keys():
114 114 if k in populate_dict:
115 115 setattr(self, k, populate_dict[k])
116 116
117 117 @classmethod
118 118 def query(cls):
119 119 return Session.query(cls)
120 120
121 121 @classmethod
122 122 def get(cls, id_):
123 123 if id_:
124 124 return cls.query().get(id_)
125 125
126 126 @classmethod
127 127 def getAll(cls):
128 128 return cls.query().all()
129 129
130 130 @classmethod
131 131 def delete(cls, id_):
132 132 obj = cls.query().get(id_)
133 133 Session.delete(obj)
134 134 Session.commit()
135 135
136 136
137 137 class RhodeCodeSetting(Base, BaseModel):
138 138 __tablename__ = 'rhodecode_settings'
139 139 __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
140 140 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
141 141 app_settings_name = Column("app_settings_name", String(255), nullable=True, unique=None, default=None)
142 142 _app_settings_value = Column("app_settings_value", String(255), nullable=True, unique=None, default=None)
143 143
144 144 def __init__(self, k='', v=''):
145 145 self.app_settings_name = k
146 146 self.app_settings_value = v
147 147
148 148
149 149 @validates('_app_settings_value')
150 150 def validate_settings_value(self, key, val):
151 151 assert type(val) == unicode
152 152 return val
153 153
154 154 @hybrid_property
155 155 def app_settings_value(self):
156 156 v = self._app_settings_value
157 157 if v == 'ldap_active':
158 158 v = str2bool(v)
159 159 return v
160 160
161 161 @app_settings_value.setter
162 162 def app_settings_value(self, val):
163 163 """
164 164 Setter that will always make sure we use unicode in app_settings_value
165 165
166 166 :param val:
167 167 """
168 168 self._app_settings_value = safe_unicode(val)
169 169
170 170 def __repr__(self):
171 171 return "<%s('%s:%s')>" % (self.__class__.__name__,
172 172 self.app_settings_name, self.app_settings_value)
173 173
174 174
175 175 @classmethod
176 176 def get_by_name(cls, ldap_key):
177 177 return cls.query()\
178 178 .filter(cls.app_settings_name == ldap_key).scalar()
179 179
180 180 @classmethod
181 181 def get_app_settings(cls, cache=False):
182 182
183 183 ret = cls.query()
184 184
185 185 if cache:
186 186 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
187 187
188 188 if not ret:
189 189 raise Exception('Could not get application settings !')
190 190 settings = {}
191 191 for each in ret:
192 192 settings['rhodecode_' + each.app_settings_name] = \
193 193 each.app_settings_value
194 194
195 195 return settings
196 196
197 197 @classmethod
198 198 def get_ldap_settings(cls, cache=False):
199 199 ret = cls.query()\
200 200 .filter(cls.app_settings_name.startswith('ldap_')).all()
201 201 fd = {}
202 202 for row in ret:
203 203 fd.update({row.app_settings_name:row.app_settings_value})
204 204
205 205 return fd
206 206
207 207
208 208 class RhodeCodeUi(Base, BaseModel):
209 209 __tablename__ = 'rhodecode_ui'
210 210 __table_args__ = (UniqueConstraint('ui_key'), {'extend_existing':True})
211 211
212 212 HOOK_REPO_SIZE = 'changegroup.repo_size'
213 213 HOOK_PUSH = 'pretxnchangegroup.push_logger'
214 214 HOOK_PULL = 'preoutgoing.pull_logger'
215 215
216 216 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
217 217 ui_section = Column("ui_section", String(255), nullable=True, unique=None, default=None)
218 218 ui_key = Column("ui_key", String(255), nullable=True, unique=None, default=None)
219 219 ui_value = Column("ui_value", String(255), nullable=True, unique=None, default=None)
220 220 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
221 221
222 222
223 223 @classmethod
224 224 def get_by_key(cls, key):
225 225 return cls.query().filter(cls.ui_key == key)
226 226
227 227
228 228 @classmethod
229 229 def get_builtin_hooks(cls):
230 230 q = cls.query()
231 231 q = q.filter(cls.ui_key.in_([cls.HOOK_REPO_SIZE,
232 232 cls.HOOK_PUSH, cls.HOOK_PULL]))
233 233 return q.all()
234 234
235 235 @classmethod
236 236 def get_custom_hooks(cls):
237 237 q = cls.query()
238 238 q = q.filter(~cls.ui_key.in_([cls.HOOK_REPO_SIZE,
239 239 cls.HOOK_PUSH, cls.HOOK_PULL]))
240 240 q = q.filter(cls.ui_section == 'hooks')
241 241 return q.all()
242 242
243 243 @classmethod
244 244 def create_or_update_hook(cls, key, val):
245 245 new_ui = cls.get_by_key(key).scalar() or cls()
246 246 new_ui.ui_section = 'hooks'
247 247 new_ui.ui_active = True
248 248 new_ui.ui_key = key
249 249 new_ui.ui_value = val
250 250
251 251 Session.add(new_ui)
252 252 Session.commit()
253 253
254 254
255 255 class User(Base, BaseModel):
256 256 __tablename__ = 'users'
257 257 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True})
258 258 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
259 259 username = Column("username", String(255), nullable=True, unique=None, default=None)
260 260 password = Column("password", String(255), nullable=True, unique=None, default=None)
261 261 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
262 262 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
263 263 name = Column("name", String(255), nullable=True, unique=None, default=None)
264 264 lastname = Column("lastname", String(255), nullable=True, unique=None, default=None)
265 265 email = Column("email", String(255), nullable=True, unique=None, default=None)
266 266 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
267 267 ldap_dn = Column("ldap_dn", String(255), nullable=True, unique=None, default=None)
268 268 api_key = Column("api_key", String(255), nullable=True, unique=None, default=None)
269 269
270 270 user_log = relationship('UserLog', cascade='all')
271 271 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
272 272
273 273 repositories = relationship('Repository')
274 274 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
275 275 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
276 276
277 277 group_member = relationship('UserGroupMember', cascade='all')
278 278
279 279 @property
280 280 def full_contact(self):
281 281 return '%s %s <%s>' % (self.name, self.lastname, self.email)
282 282
283 283 @property
284 284 def short_contact(self):
285 285 return '%s %s' % (self.name, self.lastname)
286 286
287 287 @property
288 288 def is_admin(self):
289 289 return self.admin
290 290
291 291 def __repr__(self):
292 292 try:
293 293 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
294 294 self.user_id, self.username)
295 295 except:
296 296 return self.__class__.__name__
297 297
298 298 @classmethod
299 299 def get_by_username(cls, username, case_insensitive=False):
300 300 if case_insensitive:
301 301 return Session.query(cls).filter(cls.username.ilike(username)).scalar()
302 302 else:
303 303 return Session.query(cls).filter(cls.username == username).scalar()
304 304
305 305 @classmethod
306 306 def get_by_auth_token(cls, auth_token):
307 307 return cls.query().filter(cls.api_key == auth_token).one()
308 308
309 309 def update_lastlogin(self):
310 310 """Update user lastlogin"""
311 311
312 312 self.last_login = datetime.datetime.now()
313 313 Session.add(self)
314 314 Session.commit()
315 315 log.debug('updated user %s lastlogin', self.username)
316 316
317 317 @classmethod
318 318 def create(cls, form_data):
319 319 from rhodecode.lib.auth import get_crypt_password
320 320
321 321 try:
322 322 new_user = cls()
323 323 for k, v in form_data.items():
324 324 if k == 'password':
325 325 v = get_crypt_password(v)
326 326 setattr(new_user, k, v)
327 327
328 328 new_user.api_key = generate_auth_token(form_data['username'])
329 329 Session.add(new_user)
330 330 Session.commit()
331 331 return new_user
332 332 except:
333 333 log.error(traceback.format_exc())
334 334 Session.rollback()
335 335 raise
336 336
337 337 class UserLog(Base, BaseModel):
338 338 __tablename__ = 'user_logs'
339 339 __table_args__ = {'extend_existing':True}
340 340 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
341 341 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
342 342 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
343 343 repository_name = Column("repository_name", String(255), nullable=True, unique=None, default=None)
344 344 user_ip = Column("user_ip", String(255), nullable=True, unique=None, default=None)
345 345 action = Column("action", String(1200000), nullable=True, unique=None, default=None)
346 346 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
347 347
348 348 @property
349 349 def action_as_day(self):
350 350 return date(*self.action_date.timetuple()[:3])
351 351
352 352 user = relationship('User')
353 353 repository = relationship('Repository')
354 354
355 355
356 356 class UserGroup(Base, BaseModel):
357 357 __tablename__ = 'users_groups'
358 358 __table_args__ = {'extend_existing':True}
359 359
360 360 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
361 361 users_group_name = Column("users_group_name", String(255), nullable=False, unique=True, default=None)
362 362 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
363 363
364 364 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
365 365
366 366 def __repr__(self):
367 367 return '<userGroup(%s)>' % (self.users_group_name)
368 368
369 369 @classmethod
370 370 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
371 371 if case_insensitive:
372 372 gr = cls.query()\
373 373 .filter(cls.users_group_name.ilike(group_name))
374 374 else:
375 375 gr = cls.query()\
376 376 .filter(cls.users_group_name == group_name)
377 377 if cache:
378 378 gr = gr.options(FromCache("sql_cache_short",
379 379 "get_user_%s" % group_name))
380 380 return gr.scalar()
381 381
382 382 @classmethod
383 383 def get(cls, users_group_id, cache=False):
384 384 users_group = cls.query()
385 385 if cache:
386 386 users_group = users_group.options(FromCache("sql_cache_short",
387 387 "get_users_group_%s" % users_group_id))
388 388 return users_group.get(users_group_id)
389 389
390 390 @classmethod
391 391 def create(cls, form_data):
392 392 try:
393 393 new_user_group = cls()
394 394 for k, v in form_data.items():
395 395 setattr(new_user_group, k, v)
396 396
397 397 Session.add(new_user_group)
398 398 Session.commit()
399 399 return new_user_group
400 400 except:
401 401 log.error(traceback.format_exc())
402 402 Session.rollback()
403 403 raise
404 404
405 405 @classmethod
406 406 def update(cls, users_group_id, form_data):
407 407
408 408 try:
409 409 users_group = cls.get(users_group_id, cache=False)
410 410
411 411 for k, v in form_data.items():
412 412 if k == 'users_group_members':
413 413 users_group.members = []
414 414 Session.flush()
415 415 members_list = []
416 416 if v:
417 417 v = [v] if isinstance(v, compat.string_types) else v
418 418 for u_id in set(v):
419 419 member = UserGroupMember(users_group_id, u_id)
420 420 members_list.append(member)
421 421 setattr(users_group, 'members', members_list)
422 422 setattr(users_group, k, v)
423 423
424 424 Session.add(users_group)
425 425 Session.commit()
426 426 except:
427 427 log.error(traceback.format_exc())
428 428 Session.rollback()
429 429 raise
430 430
431 431 @classmethod
432 432 def delete(cls, user_group_id):
433 433 try:
434 434
435 435 # check if this group is not assigned to repo
436 436 assigned_groups = UserGroupRepoToPerm.query()\
437 437 .filter(UserGroupRepoToPerm.users_group_id ==
438 438 user_group_id).all()
439 439
440 440 if assigned_groups:
441 441 raise UserGroupAssignedException(
442 442 'UserGroup assigned to %s' % assigned_groups)
443 443
444 444 users_group = cls.get(user_group_id, cache=False)
445 445 Session.delete(users_group)
446 446 Session.commit()
447 447 except:
448 448 log.error(traceback.format_exc())
449 449 Session.rollback()
450 450 raise
451 451
452 452 class UserGroupMember(Base, BaseModel):
453 453 __tablename__ = 'users_groups_members'
454 454 __table_args__ = {'extend_existing':True}
455 455
456 456 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
457 457 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
458 458 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
459 459
460 460 user = relationship('User', lazy='joined')
461 461 users_group = relationship('UserGroup')
462 462
463 463 def __init__(self, gr_id='', u_id=''):
464 464 self.users_group_id = gr_id
465 465 self.user_id = u_id
466 466
467 467 @staticmethod
468 468 def add_user_to_group(group, user):
469 469 ugm = UserGroupMember()
470 470 ugm.users_group = group
471 471 ugm.user = user
472 472 Session.add(ugm)
473 473 Session.commit()
474 474 return ugm
475 475
476 476 class Repository(Base, BaseModel):
477 477 __tablename__ = 'repositories'
478 478 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
479 479
480 480 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
481 481 repo_name = Column("repo_name", String(255), nullable=False, unique=True, default=None)
482 482 clone_uri = Column("clone_uri", String(255), nullable=True, unique=False, default=None)
483 483 repo_type = Column("repo_type", String(255), nullable=False, unique=False, default='hg')
484 484 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
485 485 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
486 486 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
487 487 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
488 488 description = Column("description", String(10000), nullable=True, unique=None, default=None)
489 489 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
490 490
491 491 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
492 492 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
493 493
494 494
495 495 user = relationship('User')
496 496 fork = relationship('Repository', remote_side=repo_id)
497 497 group = relationship('RepoGroup')
498 498 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
499 499 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
500 500 stats = relationship('Statistics', cascade='all', uselist=False)
501 501
502 502 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
503 503
504 504 logs = relationship('UserLog', cascade='all')
505 505
506 506 def __repr__(self):
507 507 return "<%s('%s:%s')>" % (self.__class__.__name__,
508 508 self.repo_id, self.repo_name)
509 509
510 510 @classmethod
511 511 def url_sep(cls):
512 512 return '/'
513 513
514 514 @classmethod
515 515 def get_by_repo_name(cls, repo_name):
516 516 q = Session.query(cls).filter(cls.repo_name == repo_name)
517 517 q = q.options(joinedload(Repository.fork))\
518 518 .options(joinedload(Repository.user))\
519 519 .options(joinedload(Repository.group))
520 520 return q.one()
521 521
522 522 @classmethod
523 523 def get_repo_forks(cls, repo_id):
524 524 return cls.query().filter(Repository.fork_id == repo_id)
525 525
526 526 @classmethod
527 527 def base_path(cls):
528 528 """
529 529 Returns base path when all repos are stored
530 530
531 531 :param cls:
532 532 """
533 533 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
534 534 cls.url_sep())
535 535 q.options(FromCache("sql_cache_short", "repository_repo_path"))
536 536 return q.one().ui_value
537 537
538 538 @property
539 539 def just_name(self):
540 540 return self.repo_name.split(Repository.url_sep())[-1]
541 541
542 542 @property
543 543 def groups_with_parents(self):
544 544 groups = []
545 545 if self.group is None:
546 546 return groups
547 547
548 548 cur_gr = self.group
549 549 groups.insert(0, cur_gr)
550 550 while 1:
551 551 gr = getattr(cur_gr, 'parent_group', None)
552 552 cur_gr = cur_gr.parent_group
553 553 if gr is None:
554 554 break
555 555 groups.insert(0, gr)
556 556
557 557 return groups
558 558
559 559 @property
560 560 def groups_and_repo(self):
561 561 return self.groups_with_parents, self.just_name
562 562
563 563 @LazyProperty
564 564 def repo_path(self):
565 565 """
566 566 Returns base full path for that repository means where it actually
567 567 exists on a filesystem
568 568 """
569 569 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
570 570 Repository.url_sep())
571 571 q.options(FromCache("sql_cache_short", "repository_repo_path"))
572 572 return q.one().ui_value
573 573
574 574 @property
575 575 def repo_full_path(self):
576 576 p = [self.repo_path]
577 577 # we need to split the name by / since this is how we store the
578 578 # names in the database, but that eventually needs to be converted
579 579 # into a valid system path
580 580 p += self.repo_name.split(Repository.url_sep())
581 581 return os.path.join(*p)
582 582
583 583 def get_new_name(self, repo_name):
584 584 """
585 585 returns new full repository name based on assigned group and new new
586 586
587 587 :param group_name:
588 588 """
589 589 path_prefix = self.group.full_path_splitted if self.group else []
590 590 return Repository.url_sep().join(path_prefix + [repo_name])
591 591
592 592 @property
593 593 def _config(self):
594 594 """
595 595 Returns db based config object.
596 596 """
597 597 from rhodecode.lib.utils import make_db_config
598 598 return make_db_config(clear_session=False)
599 599
600 600 @classmethod
601 601 def is_valid(cls, repo_name):
602 602 """
603 603 returns True if given repo name is a valid filesystem repository
604 604
605 605 :param cls:
606 606 :param repo_name:
607 607 """
608 608 from rhodecode.lib.utils import is_valid_repo
609 609
610 610 return is_valid_repo(repo_name, cls.base_path())
611 611
612 612
613 613 #==========================================================================
614 614 # SCM PROPERTIES
615 615 #==========================================================================
616 616
617 617 def get_commit(self, rev):
618 618 return get_commit_safe(self.scm_instance, rev)
619 619
620 620 @property
621 621 def tip(self):
622 622 return self.get_commit('tip')
623 623
624 624 @property
625 625 def author(self):
626 626 return self.tip.author
627 627
628 628 @property
629 629 def last_change(self):
630 630 return self.scm_instance.last_change
631 631
632 632 #==========================================================================
633 633 # SCM CACHE INSTANCE
634 634 #==========================================================================
635 635
636 636 @property
637 637 def invalidate(self):
638 638 return CacheInvalidation.invalidate(self.repo_name)
639 639
640 640 def set_invalidate(self):
641 641 """
642 642 set a cache for invalidation for this instance
643 643 """
644 644 CacheInvalidation.set_invalidate(self.repo_name)
645 645
646 646 @LazyProperty
647 647 def scm_instance(self):
648 648 return self.__get_instance()
649 649
650 650 @property
651 651 def scm_instance_cached(self):
652 652 return self.__get_instance()
653 653
654 654 def __get_instance(self):
655 655
656 656 repo_full_path = self.repo_full_path
657 657
658 658 try:
659 659 alias = get_scm(repo_full_path)[0]
660 660 log.debug('Creating instance of %s repository', alias)
661 661 backend = get_backend(alias)
662 662 except VCSError:
663 663 log.error(traceback.format_exc())
664 664 log.error('Perhaps this repository is in db and not in '
665 665 'filesystem run rescan repositories with '
666 666 '"destroy old data " option from admin panel')
667 667 return
668 668
669 669 if alias == 'hg':
670 670
671 671 repo = backend(safe_str(repo_full_path), create=False,
672 672 config=self._config)
673 673
674 674 else:
675 675 repo = backend(repo_full_path, create=False)
676 676
677 677 return repo
678 678
679 679
680 680 class Group(Base, BaseModel):
681 681 __tablename__ = 'groups'
682 682 __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'),
683 CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},)
683 {'extend_existing':True},)
684 684 __mapper_args__ = {'order_by':'group_name'}
685 685
686 686 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
687 687 group_name = Column("group_name", String(255), nullable=False, unique=True, default=None)
688 688 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
689 689 group_description = Column("group_description", String(10000), nullable=True, unique=None, default=None)
690 690
691 691 parent_group = relationship('Group', remote_side=group_id)
692 692
693 693 def __init__(self, group_name='', parent_group=None):
694 694 self.group_name = group_name
695 695 self.parent_group = parent_group
696 696
697 697 def __repr__(self):
698 698 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
699 699 self.group_name)
700 700
701 701 @classmethod
702 702 def url_sep(cls):
703 703 return '/'
704 704
705 705 @classmethod
706 706 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
707 707 if case_insensitive:
708 708 gr = cls.query()\
709 709 .filter(cls.group_name.ilike(group_name))
710 710 else:
711 711 gr = cls.query()\
712 712 .filter(cls.group_name == group_name)
713 713 if cache:
714 714 gr = gr.options(FromCache("sql_cache_short",
715 715 "get_group_%s" % group_name))
716 716 return gr.scalar()
717 717
718 718 @property
719 719 def parents(self):
720 720 parents_recursion_limit = 5
721 721 groups = []
722 722 if self.parent_group is None:
723 723 return groups
724 724 cur_gr = self.parent_group
725 725 groups.insert(0, cur_gr)
726 726 cnt = 0
727 727 while 1:
728 728 cnt += 1
729 729 gr = getattr(cur_gr, 'parent_group', None)
730 730 cur_gr = cur_gr.parent_group
731 731 if gr is None:
732 732 break
733 733 if cnt == parents_recursion_limit:
734 734 # this will prevent accidental infinit loops
735 735 log.error('group nested more than %s',
736 736 parents_recursion_limit)
737 737 break
738 738
739 739 groups.insert(0, gr)
740 740 return groups
741 741
742 742 @property
743 743 def children(self):
744 744 return Group.query().filter(Group.parent_group == self)
745 745
746 746 @property
747 747 def name(self):
748 748 return self.group_name.split(Group.url_sep())[-1]
749 749
750 750 @property
751 751 def full_path(self):
752 752 return self.group_name
753 753
754 754 @property
755 755 def full_path_splitted(self):
756 756 return self.group_name.split(Group.url_sep())
757 757
758 758 @property
759 759 def repositories(self):
760 760 return Repository.query().filter(Repository.group == self)
761 761
762 762 @property
763 763 def repositories_recursive_count(self):
764 764 cnt = self.repositories.count()
765 765
766 766 def children_count(group):
767 767 cnt = 0
768 768 for child in group.children:
769 769 cnt += child.repositories.count()
770 770 cnt += children_count(child)
771 771 return cnt
772 772
773 773 return cnt + children_count(self)
774 774
775 775
776 776 def get_new_name(self, group_name):
777 777 """
778 778 returns new full group name based on parent and new name
779 779
780 780 :param group_name:
781 781 """
782 782 path_prefix = (self.parent_group.full_path_splitted if
783 783 self.parent_group else [])
784 784 return Group.url_sep().join(path_prefix + [group_name])
785 785
786 786
787 787 class Permission(Base, BaseModel):
788 788 __tablename__ = 'permissions'
789 789 __table_args__ = {'extend_existing':True}
790 790 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
791 791 permission_name = Column("permission_name", String(255), nullable=True, unique=None, default=None)
792 792 permission_longname = Column("permission_longname", String(255), nullable=True, unique=None, default=None)
793 793
794 794 def __repr__(self):
795 795 return "<%s('%s:%s')>" % (self.__class__.__name__,
796 796 self.permission_id, self.permission_name)
797 797
798 798 @classmethod
799 799 def get_by_key(cls, key):
800 800 return cls.query().filter(cls.permission_name == key).scalar()
801 801
802 802 class UserRepoToPerm(Base, BaseModel):
803 803 __tablename__ = 'repo_to_perm'
804 804 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True})
805 805 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
806 806 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
807 807 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
808 808 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
809 809
810 810 user = relationship('User')
811 811 permission = relationship('Permission')
812 812 repository = relationship('Repository')
813 813
814 814 class UserToPerm(Base, BaseModel):
815 815 __tablename__ = 'user_to_perm'
816 816 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
817 817 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
818 818 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
819 819 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
820 820
821 821 user = relationship('User')
822 822 permission = relationship('Permission')
823 823
824 824 @classmethod
825 825 def has_perm(cls, user_id, perm):
826 826 if not isinstance(perm, Permission):
827 827 raise Exception('perm needs to be an instance of Permission class')
828 828
829 829 return cls.query().filter(cls.user_id == user_id)\
830 830 .filter(cls.permission == perm).scalar() is not None
831 831
832 832 @classmethod
833 833 def grant_perm(cls, user_id, perm):
834 834 if not isinstance(perm, Permission):
835 835 raise Exception('perm needs to be an instance of Permission class')
836 836
837 837 new = cls()
838 838 new.user_id = user_id
839 839 new.permission = perm
840 840 try:
841 841 Session.add(new)
842 842 Session.commit()
843 843 except:
844 844 Session.rollback()
845 845
846 846
847 847 @classmethod
848 848 def revoke_perm(cls, user_id, perm):
849 849 if not isinstance(perm, Permission):
850 850 raise Exception('perm needs to be an instance of Permission class')
851 851
852 852 try:
853 853 cls.query().filter(cls.user_id == user_id) \
854 854 .filter(cls.permission == perm).delete()
855 855 Session.commit()
856 856 except:
857 857 Session.rollback()
858 858
859 859 class UserGroupRepoToPerm(Base, BaseModel):
860 860 __tablename__ = 'users_group_repo_to_perm'
861 861 __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True})
862 862 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
863 863 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
864 864 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
865 865 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
866 866
867 867 users_group = relationship('UserGroup')
868 868 permission = relationship('Permission')
869 869 repository = relationship('Repository')
870 870
871 871 def __repr__(self):
872 872 return '<userGroup:%s => %s >' % (self.users_group, self.repository)
873 873
874 874 class UserGroupToPerm(Base, BaseModel):
875 875 __tablename__ = 'users_group_to_perm'
876 876 __table_args__ = {'extend_existing':True}
877 877 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
878 878 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
879 879 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
880 880
881 881 users_group = relationship('UserGroup')
882 882 permission = relationship('Permission')
883 883
884 884
885 885 @classmethod
886 886 def has_perm(cls, users_group_id, perm):
887 887 if not isinstance(perm, Permission):
888 888 raise Exception('perm needs to be an instance of Permission class')
889 889
890 890 return cls.query().filter(cls.users_group_id ==
891 891 users_group_id)\
892 892 .filter(cls.permission == perm)\
893 893 .scalar() is not None
894 894
895 895 @classmethod
896 896 def grant_perm(cls, users_group_id, perm):
897 897 if not isinstance(perm, Permission):
898 898 raise Exception('perm needs to be an instance of Permission class')
899 899
900 900 new = cls()
901 901 new.users_group_id = users_group_id
902 902 new.permission = perm
903 903 try:
904 904 Session.add(new)
905 905 Session.commit()
906 906 except:
907 907 Session.rollback()
908 908
909 909
910 910 @classmethod
911 911 def revoke_perm(cls, users_group_id, perm):
912 912 if not isinstance(perm, Permission):
913 913 raise Exception('perm needs to be an instance of Permission class')
914 914
915 915 try:
916 916 cls.query().filter(cls.users_group_id == users_group_id) \
917 917 .filter(cls.permission == perm).delete()
918 918 Session.commit()
919 919 except:
920 920 Session.rollback()
921 921
922 922
923 923 class UserRepoGroupToPerm(Base, BaseModel):
924 924 __tablename__ = 'group_to_perm'
925 925 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
926 926
927 927 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
928 928 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
929 929 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
930 930 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
931 931
932 932 user = relationship('User')
933 933 permission = relationship('Permission')
934 934 group = relationship('RepoGroup')
935 935
936 936 class Statistics(Base, BaseModel):
937 937 __tablename__ = 'statistics'
938 938 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True})
939 939 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
940 940 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
941 941 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
942 942 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
943 943 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
944 944 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
945 945
946 946 repository = relationship('Repository', single_parent=True)
947 947
948 948 class UserFollowing(Base, BaseModel):
949 949 __tablename__ = 'user_followings'
950 950 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
951 951 UniqueConstraint('user_id', 'follows_user_id')
952 952 , {'extend_existing':True})
953 953
954 954 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
955 955 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
956 956 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
957 957 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
958 958 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
959 959
960 960 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
961 961
962 962 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
963 963 follows_repository = relationship('Repository', order_by='Repository.repo_name')
964 964
965 965
966 966 @classmethod
967 967 def get_repo_followers(cls, repo_id):
968 968 return cls.query().filter(cls.follows_repo_id == repo_id)
969 969
970 970 class CacheInvalidation(Base, BaseModel):
971 971 __tablename__ = 'cache_invalidation'
972 972 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True})
973 973 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
974 974 cache_key = Column("cache_key", String(255), nullable=True, unique=None, default=None)
975 975 cache_args = Column("cache_args", String(255), nullable=True, unique=None, default=None)
976 976 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
977 977
978 978
979 979 def __init__(self, cache_key, cache_args=''):
980 980 self.cache_key = cache_key
981 981 self.cache_args = cache_args
982 982 self.cache_active = False
983 983
984 984 def __repr__(self):
985 985 return "<%s('%s:%s')>" % (self.__class__.__name__,
986 986 self.cache_id, self.cache_key)
987 987
988 988 @classmethod
989 989 def invalidate(cls, key):
990 990 """
991 991 Returns Invalidation object if this given key should be invalidated
992 992 None otherwise. `cache_active = False` means that this cache
993 993 state is not valid and needs to be invalidated
994 994
995 995 :param key:
996 996 """
997 997 return cls.query()\
998 998 .filter(CacheInvalidation.cache_key == key)\
999 999 .filter(CacheInvalidation.cache_active == False)\
1000 1000 .scalar()
1001 1001
1002 1002 @classmethod
1003 1003 def set_invalidate(cls, key):
1004 1004 """
1005 1005 Mark this Cache key for invalidation
1006 1006
1007 1007 :param key:
1008 1008 """
1009 1009
1010 1010 log.debug('marking %s for invalidation', key)
1011 1011 inv_obj = Session.query(cls)\
1012 1012 .filter(cls.cache_key == key).scalar()
1013 1013 if inv_obj:
1014 1014 inv_obj.cache_active = False
1015 1015 else:
1016 1016 log.debug('cache key not found in invalidation db -> creating one')
1017 1017 inv_obj = CacheInvalidation(key)
1018 1018
1019 1019 try:
1020 1020 Session.add(inv_obj)
1021 1021 Session.commit()
1022 1022 except Exception:
1023 1023 log.error(traceback.format_exc())
1024 1024 Session.rollback()
1025 1025
1026 1026 @classmethod
1027 1027 def set_valid(cls, key):
1028 1028 """
1029 1029 Mark this cache key as active and currently cached
1030 1030
1031 1031 :param key:
1032 1032 """
1033 1033 inv_obj = Session.query(CacheInvalidation)\
1034 1034 .filter(CacheInvalidation.cache_key == key).scalar()
1035 1035 inv_obj.cache_active = True
1036 1036 Session.add(inv_obj)
1037 1037 Session.commit()
1038 1038
1039 1039 class DbMigrateVersion(Base, BaseModel):
1040 1040 __tablename__ = 'db_migrate_version'
1041 1041 __table_args__ = {'extend_existing':True}
1042 1042 repository_id = Column('repository_id', String(250), primary_key=True)
1043 1043 repository_path = Column('repository_path', Text)
1044 1044 version = Column('version', Integer)
@@ -1,1264 +1,1263 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import os
22 22 import logging
23 23 import datetime
24 24 import traceback
25 25 from collections import defaultdict
26 26
27 27 from sqlalchemy import *
28 28 from sqlalchemy.ext.hybrid import hybrid_property
29 29 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
30 30 from beaker.cache import cache_region, region_invalidate
31 31
32 32 from rhodecode.lib.vcs import get_backend
33 33 from rhodecode.lib.vcs.utils.helpers import get_scm
34 34 from rhodecode.lib.vcs.exceptions import VCSError
35 35 from zope.cachedescriptors.property import Lazy as LazyProperty
36 36
37 37 from rhodecode.lib.utils2 import str2bool, safe_str, get_commit_safe, \
38 38 safe_unicode
39 39 from rhodecode.lib.ext_json import json
40 40 from rhodecode.lib.caching_query import FromCache
41 41
42 42 from rhodecode.model.meta import Base, Session
43 43 import hashlib
44 44
45 45
46 46 log = logging.getLogger(__name__)
47 47
48 48 #==============================================================================
49 49 # BASE CLASSES
50 50 #==============================================================================
51 51
52 52 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
53 53
54 54
55 55 class ModelSerializer(json.JSONEncoder):
56 56 """
57 57 Simple Serializer for JSON,
58 58
59 59 usage::
60 60
61 61 to make object customized for serialization implement a __json__
62 62 method that will return a dict for serialization into json
63 63
64 64 example::
65 65
66 66 class Task(object):
67 67
68 68 def __init__(self, name, value):
69 69 self.name = name
70 70 self.value = value
71 71
72 72 def __json__(self):
73 73 return dict(name=self.name,
74 74 value=self.value)
75 75
76 76 """
77 77
78 78 def default(self, obj):
79 79
80 80 if hasattr(obj, '__json__'):
81 81 return obj.__json__()
82 82 else:
83 83 return json.JSONEncoder.default(self, obj)
84 84
85 85
86 86 class BaseModel(object):
87 87 """
88 88 Base Model for all classess
89 89 """
90 90
91 91 @classmethod
92 92 def _get_keys(cls):
93 93 """return column names for this model """
94 94 return class_mapper(cls).c.keys()
95 95
96 96 def get_dict(self):
97 97 """
98 98 return dict with keys and values corresponding
99 99 to this model data """
100 100
101 101 d = {}
102 102 for k in self._get_keys():
103 103 d[k] = getattr(self, k)
104 104
105 105 # also use __json__() if present to get additional fields
106 106 for k, val in getattr(self, '__json__', lambda: {})().iteritems():
107 107 d[k] = val
108 108 return d
109 109
110 110 def get_appstruct(self):
111 111 """return list with keys and values tupples corresponding
112 112 to this model data """
113 113
114 114 l = []
115 115 for k in self._get_keys():
116 116 l.append((k, getattr(self, k),))
117 117 return l
118 118
119 119 def populate_obj(self, populate_dict):
120 120 """populate model with data from given populate_dict"""
121 121
122 122 for k in self._get_keys():
123 123 if k in populate_dict:
124 124 setattr(self, k, populate_dict[k])
125 125
126 126 @classmethod
127 127 def query(cls):
128 128 return Session.query(cls)
129 129
130 130 @classmethod
131 131 def get(cls, id_):
132 132 if id_:
133 133 return cls.query().get(id_)
134 134
135 135 @classmethod
136 136 def getAll(cls):
137 137 return cls.query().all()
138 138
139 139 @classmethod
140 140 def delete(cls, id_):
141 141 obj = cls.query().get(id_)
142 142 Session.delete(obj)
143 143
144 144 def __repr__(self):
145 145 if hasattr(self, '__unicode__'):
146 146 # python repr needs to return str
147 147 return safe_str(self.__unicode__())
148 148 return '<DB:%s>' % (self.__class__.__name__)
149 149
150 150
151 151 class RhodeCodeSetting(Base, BaseModel):
152 152 __tablename__ = 'rhodecode_settings'
153 153 __table_args__ = (
154 154 UniqueConstraint('app_settings_name'),
155 155 {'extend_existing': True, 'mysql_engine':'InnoDB',
156 156 'mysql_charset': 'utf8'}
157 157 )
158 158 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
159 159 app_settings_name = Column("app_settings_name", String(255), nullable=True, unique=None, default=None)
160 160 _app_settings_value = Column("app_settings_value", String(255), nullable=True, unique=None, default=None)
161 161
162 162 def __init__(self, k='', v=''):
163 163 self.app_settings_name = k
164 164 self.app_settings_value = v
165 165
166 166 @validates('_app_settings_value')
167 167 def validate_settings_value(self, key, val):
168 168 assert type(val) == unicode
169 169 return val
170 170
171 171 @hybrid_property
172 172 def app_settings_value(self):
173 173 v = self._app_settings_value
174 174 if self.app_settings_name == 'ldap_active':
175 175 v = str2bool(v)
176 176 return v
177 177
178 178 @app_settings_value.setter
179 179 def app_settings_value(self, val):
180 180 """
181 181 Setter that will always make sure we use unicode in app_settings_value
182 182
183 183 :param val:
184 184 """
185 185 self._app_settings_value = safe_unicode(val)
186 186
187 187 def __unicode__(self):
188 188 return u"<%s('%s:%s')>" % (
189 189 self.__class__.__name__,
190 190 self.app_settings_name, self.app_settings_value
191 191 )
192 192
193 193 @classmethod
194 194 def get_by_name(cls, ldap_key):
195 195 return cls.query()\
196 196 .filter(cls.app_settings_name == ldap_key).scalar()
197 197
198 198 @classmethod
199 199 def get_app_settings(cls, cache=False):
200 200
201 201 ret = cls.query()
202 202
203 203 if cache:
204 204 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
205 205
206 206 if not ret:
207 207 raise Exception('Could not get application settings !')
208 208 settings = {}
209 209 for each in ret:
210 210 settings['rhodecode_' + each.app_settings_name] = \
211 211 each.app_settings_value
212 212
213 213 return settings
214 214
215 215 @classmethod
216 216 def get_ldap_settings(cls, cache=False):
217 217 ret = cls.query()\
218 218 .filter(cls.app_settings_name.startswith('ldap_')).all()
219 219 fd = {}
220 220 for row in ret:
221 221 fd.update({row.app_settings_name:row.app_settings_value})
222 222
223 223 return fd
224 224
225 225
226 226 class RhodeCodeUi(Base, BaseModel):
227 227 __tablename__ = 'rhodecode_ui'
228 228 __table_args__ = (
229 229 UniqueConstraint('ui_key'),
230 230 {'extend_existing': True, 'mysql_engine':'InnoDB',
231 231 'mysql_charset': 'utf8'}
232 232 )
233 233
234 234 HOOK_REPO_SIZE = 'changegroup.repo_size'
235 235 HOOK_PUSH = 'pretxnchangegroup.push_logger'
236 236 HOOK_PULL = 'preoutgoing.pull_logger'
237 237
238 238 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
239 239 ui_section = Column("ui_section", String(255), nullable=True, unique=None, default=None)
240 240 ui_key = Column("ui_key", String(255), nullable=True, unique=None, default=None)
241 241 ui_value = Column("ui_value", String(255), nullable=True, unique=None, default=None)
242 242 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
243 243
244 244 @classmethod
245 245 def get_by_key(cls, key):
246 246 return cls.query().filter(cls.ui_key == key)
247 247
248 248 @classmethod
249 249 def get_builtin_hooks(cls):
250 250 q = cls.query()
251 251 q = q.filter(cls.ui_key.in_([cls.HOOK_REPO_SIZE,
252 252 cls.HOOK_PUSH, cls.HOOK_PULL]))
253 253 return q.all()
254 254
255 255 @classmethod
256 256 def get_custom_hooks(cls):
257 257 q = cls.query()
258 258 q = q.filter(~cls.ui_key.in_([cls.HOOK_REPO_SIZE,
259 259 cls.HOOK_PUSH, cls.HOOK_PULL]))
260 260 q = q.filter(cls.ui_section == 'hooks')
261 261 return q.all()
262 262
263 263 @classmethod
264 264 def create_or_update_hook(cls, key, val):
265 265 new_ui = cls.get_by_key(key).scalar() or cls()
266 266 new_ui.ui_section = 'hooks'
267 267 new_ui.ui_active = True
268 268 new_ui.ui_key = key
269 269 new_ui.ui_value = val
270 270
271 271 Session.add(new_ui)
272 272
273 273
274 274 class User(Base, BaseModel):
275 275 __tablename__ = 'users'
276 276 __table_args__ = (
277 277 UniqueConstraint('username'), UniqueConstraint('email'),
278 278 {'extend_existing': True, 'mysql_engine':'InnoDB',
279 279 'mysql_charset': 'utf8'}
280 280 )
281 281 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
282 282 username = Column("username", String(255), nullable=True, unique=None, default=None)
283 283 password = Column("password", String(255), nullable=True, unique=None, default=None)
284 284 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
285 285 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
286 286 name = Column("name", String(255), nullable=True, unique=None, default=None)
287 287 lastname = Column("lastname", String(255), nullable=True, unique=None, default=None)
288 288 _email = Column("email", String(255), nullable=True, unique=None, default=None)
289 289 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
290 290 ldap_dn = Column("ldap_dn", String(255), nullable=True, unique=None, default=None)
291 291 api_key = Column("api_key", String(255), nullable=True, unique=None, default=None)
292 292
293 293 user_log = relationship('UserLog', cascade='all')
294 294 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
295 295
296 296 repositories = relationship('Repository')
297 297 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
298 298 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
299 299 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
300 300
301 301 group_member = relationship('UserGroupMember', cascade='all')
302 302
303 303 notifications = relationship('UserNotification', cascade='all')
304 304 # notifications assigned to this user
305 305 user_created_notifications = relationship('Notification', cascade='all')
306 306 # comments created by this user
307 307 user_comments = relationship('ChangesetComment', cascade='all')
308 308
309 309 @hybrid_property
310 310 def email(self):
311 311 return self._email
312 312
313 313 @email.setter
314 314 def email(self, val):
315 315 self._email = val.lower() if val else None
316 316
317 317 @property
318 318 def full_name(self):
319 319 return '%s %s' % (self.name, self.lastname)
320 320
321 321 @property
322 322 def full_name_or_username(self):
323 323 return ('%s %s' % (self.name, self.lastname)
324 324 if (self.name and self.lastname) else self.username)
325 325
326 326 @property
327 327 def full_contact(self):
328 328 return '%s %s <%s>' % (self.name, self.lastname, self.email)
329 329
330 330 @property
331 331 def short_contact(self):
332 332 return '%s %s' % (self.name, self.lastname)
333 333
334 334 @property
335 335 def is_admin(self):
336 336 return self.admin
337 337
338 338 def __unicode__(self):
339 339 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
340 340 self.user_id, self.username)
341 341
342 342 @classmethod
343 343 def get_by_username(cls, username, case_insensitive=False, cache=False):
344 344 if case_insensitive:
345 345 q = cls.query().filter(cls.username.ilike(username))
346 346 else:
347 347 q = cls.query().filter(cls.username == username)
348 348
349 349 if cache:
350 350 q = q.options(FromCache(
351 351 "sql_cache_short",
352 352 "get_user_%s" % _hash_key(username)
353 353 )
354 354 )
355 355 return q.scalar()
356 356
357 357 @classmethod
358 358 def get_by_auth_token(cls, auth_token, cache=False):
359 359 q = cls.query().filter(cls.api_key == auth_token)
360 360
361 361 if cache:
362 362 q = q.options(FromCache("sql_cache_short",
363 363 "get_auth_token_%s" % auth_token))
364 364 return q.scalar()
365 365
366 366 @classmethod
367 367 def get_by_email(cls, email, case_insensitive=False, cache=False):
368 368 if case_insensitive:
369 369 q = cls.query().filter(cls.email.ilike(email))
370 370 else:
371 371 q = cls.query().filter(cls.email == email)
372 372
373 373 if cache:
374 374 q = q.options(FromCache("sql_cache_short",
375 375 "get_auth_token_%s" % email))
376 376 return q.scalar()
377 377
378 378 def update_lastlogin(self):
379 379 """Update user lastlogin"""
380 380 self.last_login = datetime.datetime.now()
381 381 Session.add(self)
382 382 log.debug('updated user %s lastlogin', self.username)
383 383
384 384 def __json__(self):
385 385 return dict(
386 386 user_id=self.user_id,
387 387 first_name=self.name,
388 388 last_name=self.lastname,
389 389 email=self.email,
390 390 full_name=self.full_name,
391 391 full_name_or_username=self.full_name_or_username,
392 392 short_contact=self.short_contact,
393 393 full_contact=self.full_contact
394 394 )
395 395
396 396
397 397 class UserLog(Base, BaseModel):
398 398 __tablename__ = 'user_logs'
399 399 __table_args__ = (
400 400 {'extend_existing': True, 'mysql_engine':'InnoDB',
401 401 'mysql_charset': 'utf8'},
402 402 )
403 403 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
404 404 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
405 405 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
406 406 repository_name = Column("repository_name", String(255), nullable=True, unique=None, default=None)
407 407 user_ip = Column("user_ip", String(255), nullable=True, unique=None, default=None)
408 408 action = Column("action", String(1200000), nullable=True, unique=None, default=None)
409 409 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
410 410
411 411 @property
412 412 def action_as_day(self):
413 413 return datetime.date(*self.action_date.timetuple()[:3])
414 414
415 415 user = relationship('User')
416 416 repository = relationship('Repository', cascade='')
417 417
418 418
419 419 class UserGroup(Base, BaseModel):
420 420 __tablename__ = 'users_groups'
421 421 __table_args__ = (
422 422 {'extend_existing': True, 'mysql_engine':'InnoDB',
423 423 'mysql_charset': 'utf8'},
424 424 )
425 425
426 426 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
427 427 users_group_name = Column("users_group_name", String(255), nullable=False, unique=True, default=None)
428 428 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
429 429
430 430 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
431 431 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
432 432 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
433 433
434 434 def __unicode__(self):
435 435 return u'<userGroup(%s)>' % (self.users_group_name)
436 436
437 437 @classmethod
438 438 def get_by_group_name(cls, group_name, cache=False,
439 439 case_insensitive=False):
440 440 if case_insensitive:
441 441 q = cls.query().filter(cls.users_group_name.ilike(group_name))
442 442 else:
443 443 q = cls.query().filter(cls.users_group_name == group_name)
444 444 if cache:
445 445 q = q.options(FromCache(
446 446 "sql_cache_short",
447 447 "get_user_%s" % _hash_key(group_name)
448 448 )
449 449 )
450 450 return q.scalar()
451 451
452 452 @classmethod
453 453 def get(cls, users_group_id, cache=False):
454 454 users_group = cls.query()
455 455 if cache:
456 456 users_group = users_group.options(FromCache("sql_cache_short",
457 457 "get_users_group_%s" % users_group_id))
458 458 return users_group.get(users_group_id)
459 459
460 460
461 461 class UserGroupMember(Base, BaseModel):
462 462 __tablename__ = 'users_groups_members'
463 463 __table_args__ = (
464 464 {'extend_existing': True, 'mysql_engine':'InnoDB',
465 465 'mysql_charset': 'utf8'},
466 466 )
467 467
468 468 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
469 469 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
470 470 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
471 471
472 472 user = relationship('User', lazy='joined')
473 473 users_group = relationship('UserGroup')
474 474
475 475 def __init__(self, gr_id='', u_id=''):
476 476 self.users_group_id = gr_id
477 477 self.user_id = u_id
478 478
479 479
480 480 class Repository(Base, BaseModel):
481 481 __tablename__ = 'repositories'
482 482 __table_args__ = (
483 483 UniqueConstraint('repo_name'),
484 484 {'extend_existing': True, 'mysql_engine':'InnoDB',
485 485 'mysql_charset': 'utf8'},
486 486 )
487 487
488 488 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
489 489 repo_name = Column("repo_name", String(255), nullable=False, unique=True, default=None)
490 490 clone_uri = Column("clone_uri", String(255), nullable=True, unique=False, default=None)
491 491 repo_type = Column("repo_type", String(255), nullable=False, unique=False, default='hg')
492 492 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
493 493 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
494 494 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
495 495 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
496 496 description = Column("description", String(10000), nullable=True, unique=None, default=None)
497 497 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
498 498
499 499 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
500 500 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
501 501
502 502 user = relationship('User')
503 503 fork = relationship('Repository', remote_side=repo_id)
504 504 group = relationship('RepoGroup')
505 505 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
506 506 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
507 507 stats = relationship('Statistics', cascade='all', uselist=False)
508 508
509 509 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
510 510
511 511 logs = relationship('UserLog')
512 512
513 513 def __unicode__(self):
514 514 return u"<%s('%s:%s')>" % (self.__class__.__name__,self.repo_id,
515 515 self.repo_name)
516 516
517 517 @classmethod
518 518 def url_sep(cls):
519 519 return '/'
520 520
521 521 @classmethod
522 522 def get_by_repo_name(cls, repo_name):
523 523 q = Session.query(cls).filter(cls.repo_name == repo_name)
524 524 q = q.options(joinedload(Repository.fork))\
525 525 .options(joinedload(Repository.user))\
526 526 .options(joinedload(Repository.group))
527 527 return q.scalar()
528 528
529 529 @classmethod
530 530 def get_repo_forks(cls, repo_id):
531 531 return cls.query().filter(Repository.fork_id == repo_id)
532 532
533 533 @classmethod
534 534 def base_path(cls):
535 535 """
536 536 Returns base path when all repos are stored
537 537
538 538 :param cls:
539 539 """
540 540 q = Session.query(RhodeCodeUi)\
541 541 .filter(RhodeCodeUi.ui_key == cls.url_sep())
542 542 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
543 543 return q.one().ui_value
544 544
545 545 @property
546 546 def just_name(self):
547 547 return self.repo_name.split(Repository.url_sep())[-1]
548 548
549 549 @property
550 550 def groups_with_parents(self):
551 551 groups = []
552 552 if self.group is None:
553 553 return groups
554 554
555 555 cur_gr = self.group
556 556 groups.insert(0, cur_gr)
557 557 while 1:
558 558 gr = getattr(cur_gr, 'parent_group', None)
559 559 cur_gr = cur_gr.parent_group
560 560 if gr is None:
561 561 break
562 562 groups.insert(0, gr)
563 563
564 564 return groups
565 565
566 566 @property
567 567 def groups_and_repo(self):
568 568 return self.groups_with_parents, self.just_name
569 569
570 570 @LazyProperty
571 571 def repo_path(self):
572 572 """
573 573 Returns base full path for that repository means where it actually
574 574 exists on a filesystem
575 575 """
576 576 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
577 577 Repository.url_sep())
578 578 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
579 579 return q.one().ui_value
580 580
581 581 @property
582 582 def repo_full_path(self):
583 583 p = [self.repo_path]
584 584 # we need to split the name by / since this is how we store the
585 585 # names in the database, but that eventually needs to be converted
586 586 # into a valid system path
587 587 p += self.repo_name.split(Repository.url_sep())
588 588 return os.path.join(*p)
589 589
590 590 def get_new_name(self, repo_name):
591 591 """
592 592 returns new full repository name based on assigned group and new new
593 593
594 594 :param group_name:
595 595 """
596 596 path_prefix = self.group.full_path_splitted if self.group else []
597 597 return Repository.url_sep().join(path_prefix + [repo_name])
598 598
599 599 @property
600 600 def _config(self):
601 601 """
602 602 Returns db based config object.
603 603 """
604 604 from rhodecode.lib.utils import make_db_config
605 605 return make_db_config(clear_session=False)
606 606
607 607 @classmethod
608 608 def is_valid(cls, repo_name):
609 609 """
610 610 returns True if given repo name is a valid filesystem repository
611 611
612 612 :param cls:
613 613 :param repo_name:
614 614 """
615 615 from rhodecode.lib.utils import is_valid_repo
616 616
617 617 return is_valid_repo(repo_name, cls.base_path())
618 618
619 619 #==========================================================================
620 620 # SCM PROPERTIES
621 621 #==========================================================================
622 622
623 623 def get_commit(self, rev):
624 624 return get_commit_safe(self.scm_instance, rev)
625 625
626 626 @property
627 627 def tip(self):
628 628 return self.get_commit('tip')
629 629
630 630 @property
631 631 def author(self):
632 632 return self.tip.author
633 633
634 634 @property
635 635 def last_change(self):
636 636 return self.scm_instance.last_change
637 637
638 638 def comments(self, revisions=None):
639 639 """
640 640 Returns comments for this repository grouped by revisions
641 641
642 642 :param revisions: filter query by revisions only
643 643 """
644 644 cmts = ChangesetComment.query()\
645 645 .filter(ChangesetComment.repo == self)
646 646 if revisions:
647 647 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
648 648 grouped = defaultdict(list)
649 649 for cmt in cmts.all():
650 650 grouped[cmt.revision].append(cmt)
651 651 return grouped
652 652
653 653 #==========================================================================
654 654 # SCM CACHE INSTANCE
655 655 #==========================================================================
656 656
657 657 @property
658 658 def invalidate(self):
659 659 return CacheInvalidation.invalidate(self.repo_name)
660 660
661 661 def set_invalidate(self):
662 662 """
663 663 set a cache for invalidation for this instance
664 664 """
665 665 CacheInvalidation.set_invalidate(self.repo_name)
666 666
667 667 @LazyProperty
668 668 def scm_instance(self):
669 669 return self.__get_instance()
670 670
671 671 @property
672 672 def scm_instance_cached(self):
673 673 return self.__get_instance()
674 674
675 675 def __get_instance(self):
676 676 repo_full_path = self.repo_full_path
677 677 try:
678 678 alias = get_scm(repo_full_path)[0]
679 679 log.debug('Creating instance of %s repository', alias)
680 680 backend = get_backend(alias)
681 681 except VCSError:
682 682 log.error(traceback.format_exc())
683 683 log.error('Perhaps this repository is in db and not in '
684 684 'filesystem run rescan repositories with '
685 685 '"destroy old data " option from admin panel')
686 686 return
687 687
688 688 if alias == 'hg':
689 689
690 690 repo = backend(safe_str(repo_full_path), create=False,
691 691 config=self._config)
692 692 else:
693 693 repo = backend(repo_full_path, create=False)
694 694
695 695 return repo
696 696
697 697
698 698 class RepoGroup(Base, BaseModel):
699 699 __tablename__ = 'groups'
700 700 __table_args__ = (
701 701 UniqueConstraint('group_name', 'group_parent_id'),
702 CheckConstraint('group_id != group_parent_id'),
703 702 {'extend_existing': True, 'mysql_engine':'InnoDB',
704 703 'mysql_charset': 'utf8'},
705 704 )
706 705 __mapper_args__ = {'order_by': 'group_name'}
707 706
708 707 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
709 708 group_name = Column("group_name", String(255), nullable=False, unique=True, default=None)
710 709 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
711 710 group_description = Column("group_description", String(10000), nullable=True, unique=None, default=None)
712 711
713 712 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
714 713 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
715 714
716 715 parent_group = relationship('RepoGroup', remote_side=group_id)
717 716
718 717 def __init__(self, group_name='', parent_group=None):
719 718 self.group_name = group_name
720 719 self.parent_group = parent_group
721 720
722 721 def __unicode__(self):
723 722 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
724 723 self.group_name)
725 724
726 725 @classmethod
727 726 def url_sep(cls):
728 727 return '/'
729 728
730 729 @classmethod
731 730 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
732 731 if case_insensitive:
733 732 gr = cls.query()\
734 733 .filter(cls.group_name.ilike(group_name))
735 734 else:
736 735 gr = cls.query()\
737 736 .filter(cls.group_name == group_name)
738 737 if cache:
739 738 gr = gr.options(FromCache(
740 739 "sql_cache_short",
741 740 "get_group_%s" % _hash_key(group_name)
742 741 )
743 742 )
744 743 return gr.scalar()
745 744
746 745 @property
747 746 def parents(self):
748 747 parents_recursion_limit = 5
749 748 groups = []
750 749 if self.parent_group is None:
751 750 return groups
752 751 cur_gr = self.parent_group
753 752 groups.insert(0, cur_gr)
754 753 cnt = 0
755 754 while 1:
756 755 cnt += 1
757 756 gr = getattr(cur_gr, 'parent_group', None)
758 757 cur_gr = cur_gr.parent_group
759 758 if gr is None:
760 759 break
761 760 if cnt == parents_recursion_limit:
762 761 # this will prevent accidental infinit loops
763 762 log.error('group nested more than %s', parents_recursion_limit)
764 763 break
765 764
766 765 groups.insert(0, gr)
767 766 return groups
768 767
769 768 @property
770 769 def children(self):
771 770 return RepoGroup.query().filter(RepoGroup.parent_group == self)
772 771
773 772 @property
774 773 def name(self):
775 774 return self.group_name.split(RepoGroup.url_sep())[-1]
776 775
777 776 @property
778 777 def full_path(self):
779 778 return self.group_name
780 779
781 780 @property
782 781 def full_path_splitted(self):
783 782 return self.group_name.split(RepoGroup.url_sep())
784 783
785 784 @property
786 785 def repositories(self):
787 786 return Repository.query()\
788 787 .filter(Repository.group == self)\
789 788 .order_by(Repository.repo_name)
790 789
791 790 @property
792 791 def repositories_recursive_count(self):
793 792 cnt = self.repositories.count()
794 793
795 794 def children_count(group):
796 795 cnt = 0
797 796 for child in group.children:
798 797 cnt += child.repositories.count()
799 798 cnt += children_count(child)
800 799 return cnt
801 800
802 801 return cnt + children_count(self)
803 802
804 803 def get_new_name(self, group_name):
805 804 """
806 805 returns new full group name based on parent and new name
807 806
808 807 :param group_name:
809 808 """
810 809 path_prefix = (self.parent_group.full_path_splitted if
811 810 self.parent_group else [])
812 811 return RepoGroup.url_sep().join(path_prefix + [group_name])
813 812
814 813
815 814 class Permission(Base, BaseModel):
816 815 __tablename__ = 'permissions'
817 816 __table_args__ = (
818 817 {'extend_existing': True, 'mysql_engine':'InnoDB',
819 818 'mysql_charset': 'utf8'},
820 819 )
821 820 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
822 821 permission_name = Column("permission_name", String(255), nullable=True, unique=None, default=None)
823 822 permission_longname = Column("permission_longname", String(255), nullable=True, unique=None, default=None)
824 823
825 824 def __unicode__(self):
826 825 return u"<%s('%s:%s')>" % (
827 826 self.__class__.__name__, self.permission_id, self.permission_name
828 827 )
829 828
830 829 @classmethod
831 830 def get_by_key(cls, key):
832 831 return cls.query().filter(cls.permission_name == key).scalar()
833 832
834 833 @classmethod
835 834 def get_default_repo_perms(cls, default_user_id):
836 835 q = Session.query(UserRepoToPerm, Repository, cls)\
837 836 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
838 837 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
839 838 .filter(UserRepoToPerm.user_id == default_user_id)
840 839
841 840 return q.all()
842 841
843 842 @classmethod
844 843 def get_default_group_perms(cls, default_user_id):
845 844 q = Session.query(UserRepoGroupToPerm, RepoGroup, cls)\
846 845 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
847 846 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
848 847 .filter(UserRepoGroupToPerm.user_id == default_user_id)
849 848
850 849 return q.all()
851 850
852 851
853 852 class UserRepoToPerm(Base, BaseModel):
854 853 __tablename__ = 'repo_to_perm'
855 854 __table_args__ = (
856 855 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
857 856 {'extend_existing': True, 'mysql_engine':'InnoDB',
858 857 'mysql_charset': 'utf8'}
859 858 )
860 859 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
861 860 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
862 861 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
863 862 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
864 863
865 864 user = relationship('User')
866 865 repository = relationship('Repository')
867 866 permission = relationship('Permission')
868 867
869 868 @classmethod
870 869 def create(cls, user, repository, permission):
871 870 n = cls()
872 871 n.user = user
873 872 n.repository = repository
874 873 n.permission = permission
875 874 Session.add(n)
876 875 return n
877 876
878 877 def __unicode__(self):
879 878 return u'<user:%s => %s >' % (self.user, self.repository)
880 879
881 880
882 881 class UserToPerm(Base, BaseModel):
883 882 __tablename__ = 'user_to_perm'
884 883 __table_args__ = (
885 884 UniqueConstraint('user_id', 'permission_id'),
886 885 {'extend_existing': True, 'mysql_engine':'InnoDB',
887 886 'mysql_charset': 'utf8'}
888 887 )
889 888 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
890 889 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
891 890 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
892 891
893 892 user = relationship('User')
894 893 permission = relationship('Permission', lazy='joined')
895 894
896 895
897 896 class UserGroupRepoToPerm(Base, BaseModel):
898 897 __tablename__ = 'users_group_repo_to_perm'
899 898 __table_args__ = (
900 899 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
901 900 {'extend_existing': True, 'mysql_engine':'InnoDB',
902 901 'mysql_charset': 'utf8'}
903 902 )
904 903 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
905 904 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
906 905 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
907 906 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
908 907
909 908 users_group = relationship('UserGroup')
910 909 permission = relationship('Permission')
911 910 repository = relationship('Repository')
912 911
913 912 @classmethod
914 913 def create(cls, users_group, repository, permission):
915 914 n = cls()
916 915 n.users_group = users_group
917 916 n.repository = repository
918 917 n.permission = permission
919 918 Session.add(n)
920 919 return n
921 920
922 921 def __unicode__(self):
923 922 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
924 923
925 924
926 925 class UserGroupToPerm(Base, BaseModel):
927 926 __tablename__ = 'users_group_to_perm'
928 927 __table_args__ = (
929 928 UniqueConstraint('users_group_id', 'permission_id',),
930 929 {'extend_existing': True, 'mysql_engine':'InnoDB',
931 930 'mysql_charset': 'utf8'}
932 931 )
933 932 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
934 933 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
935 934 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
936 935
937 936 users_group = relationship('UserGroup')
938 937 permission = relationship('Permission')
939 938
940 939
941 940 class UserRepoGroupToPerm(Base, BaseModel):
942 941 __tablename__ = 'user_repo_group_to_perm'
943 942 __table_args__ = (
944 943 UniqueConstraint('user_id', 'group_id', 'permission_id'),
945 944 {'extend_existing': True, 'mysql_engine':'InnoDB',
946 945 'mysql_charset': 'utf8'}
947 946 )
948 947
949 948 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
950 949 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
951 950 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
952 951 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
953 952
954 953 user = relationship('User')
955 954 group = relationship('RepoGroup')
956 955 permission = relationship('Permission')
957 956
958 957
959 958 class UserGroupRepoGroupToPerm(Base, BaseModel):
960 959 __tablename__ = 'users_group_repo_group_to_perm'
961 960 __table_args__ = (
962 961 UniqueConstraint('users_group_id', 'group_id'),
963 962 {'extend_existing': True, 'mysql_engine':'InnoDB',
964 963 'mysql_charset': 'utf8'}
965 964 )
966 965
967 966 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
968 967 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
969 968 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
970 969 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
971 970
972 971 users_group = relationship('UserGroup')
973 972 permission = relationship('Permission')
974 973 group = relationship('RepoGroup')
975 974
976 975
977 976 class Statistics(Base, BaseModel):
978 977 __tablename__ = 'statistics'
979 978 __table_args__ = (
980 979 UniqueConstraint('repository_id'),
981 980 {'extend_existing': True, 'mysql_engine':'InnoDB',
982 981 'mysql_charset': 'utf8'}
983 982 )
984 983 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
985 984 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
986 985 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
987 986 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
988 987 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
989 988 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
990 989
991 990 repository = relationship('Repository', single_parent=True)
992 991
993 992
994 993 class UserFollowing(Base, BaseModel):
995 994 __tablename__ = 'user_followings'
996 995 __table_args__ = (
997 996 UniqueConstraint('user_id', 'follows_repository_id'),
998 997 UniqueConstraint('user_id', 'follows_user_id'),
999 998 {'extend_existing': True, 'mysql_engine':'InnoDB',
1000 999 'mysql_charset': 'utf8'}
1001 1000 )
1002 1001
1003 1002 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1004 1003 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1005 1004 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1006 1005 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1007 1006 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1008 1007
1009 1008 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1010 1009
1011 1010 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1012 1011 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1013 1012
1014 1013 @classmethod
1015 1014 def get_repo_followers(cls, repo_id):
1016 1015 return cls.query().filter(cls.follows_repo_id == repo_id)
1017 1016
1018 1017
1019 1018 class CacheInvalidation(Base, BaseModel):
1020 1019 __tablename__ = 'cache_invalidation'
1021 1020 __table_args__ = (
1022 1021 UniqueConstraint('cache_key'),
1023 1022 {'extend_existing': True, 'mysql_engine':'InnoDB',
1024 1023 'mysql_charset': 'utf8'},
1025 1024 )
1026 1025 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1027 1026 cache_key = Column("cache_key", String(255), nullable=True, unique=None, default=None)
1028 1027 cache_args = Column("cache_args", String(255), nullable=True, unique=None, default=None)
1029 1028 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1030 1029
1031 1030 def __init__(self, cache_key, cache_args=''):
1032 1031 self.cache_key = cache_key
1033 1032 self.cache_args = cache_args
1034 1033 self.cache_active = False
1035 1034
1036 1035 def __unicode__(self):
1037 1036 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1038 1037 self.cache_id, self.cache_key)
1039 1038
1040 1039 @classmethod
1041 1040 def _get_key(cls, key):
1042 1041 """
1043 1042 Wrapper for generating a key, together with a prefix
1044 1043
1045 1044 :param key:
1046 1045 """
1047 1046 import rhodecode
1048 1047 prefix = ''
1049 1048 iid = rhodecode.CONFIG.get('instance_id')
1050 1049 if iid:
1051 1050 prefix = iid
1052 1051 return "%s%s" % (prefix, key), prefix, key.rstrip('_README')
1053 1052
1054 1053 @classmethod
1055 1054 def get_by_key(cls, key):
1056 1055 return cls.query().filter(cls.cache_key == key).scalar()
1057 1056
1058 1057 @classmethod
1059 1058 def _get_or_create_key(cls, key, prefix, org_key):
1060 1059 inv_obj = Session.query(cls).filter(cls.cache_key == key).scalar()
1061 1060 if not inv_obj:
1062 1061 try:
1063 1062 inv_obj = CacheInvalidation(key, org_key)
1064 1063 Session.add(inv_obj)
1065 1064 Session.commit()
1066 1065 except Exception:
1067 1066 log.error(traceback.format_exc())
1068 1067 Session.rollback()
1069 1068 return inv_obj
1070 1069
1071 1070 @classmethod
1072 1071 def invalidate(cls, key):
1073 1072 """
1074 1073 Returns Invalidation object if this given key should be invalidated
1075 1074 None otherwise. `cache_active = False` means that this cache
1076 1075 state is not valid and needs to be invalidated
1077 1076
1078 1077 :param key:
1079 1078 """
1080 1079
1081 1080 key, _prefix, _org_key = cls._get_key(key)
1082 1081 inv = cls._get_or_create_key(key, _prefix, _org_key)
1083 1082
1084 1083 if inv and inv.cache_active is False:
1085 1084 return inv
1086 1085
1087 1086 @classmethod
1088 1087 def set_invalidate(cls, key):
1089 1088 """
1090 1089 Mark this Cache key for invalidation
1091 1090
1092 1091 :param key:
1093 1092 """
1094 1093
1095 1094 key, _prefix, _org_key = cls._get_key(key)
1096 1095 inv_objs = Session.query(cls).filter(cls.cache_args == _org_key).all()
1097 1096 log.debug('marking %s key[s] %s for invalidation', len(inv_objs), _org_key)
1098 1097 try:
1099 1098 for inv_obj in inv_objs:
1100 1099 if inv_obj:
1101 1100 inv_obj.cache_active = False
1102 1101
1103 1102 Session.add(inv_obj)
1104 1103 Session.commit()
1105 1104 except Exception:
1106 1105 log.error(traceback.format_exc())
1107 1106 Session.rollback()
1108 1107
1109 1108 @classmethod
1110 1109 def set_valid(cls, key):
1111 1110 """
1112 1111 Mark this cache key as active and currently cached
1113 1112
1114 1113 :param key:
1115 1114 """
1116 1115 inv_obj = cls.get_by_key(key)
1117 1116 inv_obj.cache_active = True
1118 1117 Session.add(inv_obj)
1119 1118 Session.commit()
1120 1119
1121 1120
1122 1121 class ChangesetComment(Base, BaseModel):
1123 1122 __tablename__ = 'changeset_comments'
1124 1123 __table_args__ = (
1125 1124 {'extend_existing': True, 'mysql_engine':'InnoDB',
1126 1125 'mysql_charset': 'utf8'},
1127 1126 )
1128 1127 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1129 1128 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1130 1129 revision = Column('revision', String(40), nullable=False)
1131 1130 line_no = Column('line_no', Unicode(10), nullable=True)
1132 1131 f_path = Column('f_path', Unicode(1000), nullable=True)
1133 1132 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1134 1133 text = Column('text', UnicodeText().with_variant(UnicodeText(25000), 'mysql'), nullable=False)
1135 1134 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1136 1135
1137 1136 author = relationship('User', lazy='joined')
1138 1137 repo = relationship('Repository')
1139 1138
1140 1139 @classmethod
1141 1140 def get_users(cls, revision):
1142 1141 """
1143 1142 Returns user associated with this changesetComment. ie those
1144 1143 who actually commented
1145 1144
1146 1145 :param cls:
1147 1146 :param revision:
1148 1147 """
1149 1148 return Session.query(User)\
1150 1149 .filter(cls.revision == revision)\
1151 1150 .join(ChangesetComment.author).all()
1152 1151
1153 1152
1154 1153 class Notification(Base, BaseModel):
1155 1154 __tablename__ = 'notifications'
1156 1155 __table_args__ = (
1157 1156 {'extend_existing': True, 'mysql_engine':'InnoDB',
1158 1157 'mysql_charset': 'utf8'},
1159 1158 )
1160 1159
1161 1160 TYPE_CHANGESET_COMMENT = u'cs_comment'
1162 1161 TYPE_MESSAGE = u'message'
1163 1162 TYPE_MENTION = u'mention'
1164 1163 TYPE_REGISTRATION = u'registration'
1165 1164
1166 1165 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1167 1166 subject = Column('subject', Unicode(512), nullable=True)
1168 1167 body = Column('body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True)
1169 1168 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1170 1169 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1171 1170 type_ = Column('type', Unicode(256))
1172 1171
1173 1172 created_by_user = relationship('User')
1174 1173 notifications_to_users = relationship('UserNotification', lazy='joined',
1175 1174 cascade="all, delete, delete-orphan")
1176 1175
1177 1176 @property
1178 1177 def recipients(self):
1179 1178 return [x.user for x in UserNotification.query()\
1180 1179 .filter(UserNotification.notification == self).all()]
1181 1180
1182 1181 @classmethod
1183 1182 def create(cls, created_by, subject, body, recipients, type_=None):
1184 1183 if type_ is None:
1185 1184 type_ = Notification.TYPE_MESSAGE
1186 1185
1187 1186 notification = cls()
1188 1187 notification.created_by_user = created_by
1189 1188 notification.subject = subject
1190 1189 notification.body = body
1191 1190 notification.type_ = type_
1192 1191 notification.created_on = datetime.datetime.now()
1193 1192
1194 1193 for u in recipients:
1195 1194 assoc = UserNotification()
1196 1195 assoc.notification = notification
1197 1196 u.notifications.append(assoc)
1198 1197 Session.add(notification)
1199 1198 return notification
1200 1199
1201 1200 @property
1202 1201 def description(self):
1203 1202 from rhodecode.model.notification import NotificationModel
1204 1203 return NotificationModel().make_description(self)
1205 1204
1206 1205
1207 1206 class UserNotification(Base, BaseModel):
1208 1207 __tablename__ = 'user_to_notification'
1209 1208 __table_args__ = (
1210 1209 UniqueConstraint('user_id', 'notification_id'),
1211 1210 {'extend_existing': True, 'mysql_engine':'InnoDB',
1212 1211 'mysql_charset': 'utf8'}
1213 1212 )
1214 1213 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1215 1214 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1216 1215 read = Column('read', Boolean, default=False)
1217 1216 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1218 1217
1219 1218 user = relationship('User', lazy="joined")
1220 1219 notification = relationship('Notification', lazy="joined",
1221 1220 order_by=lambda: Notification.created_on.desc(),)
1222 1221
1223 1222 def mark_as_read(self):
1224 1223 self.read = True
1225 1224 Session.add(self)
1226 1225
1227 1226
1228 1227 class DbMigrateVersion(Base, BaseModel):
1229 1228 __tablename__ = 'db_migrate_version'
1230 1229 __table_args__ = (
1231 1230 {'extend_existing': True, 'mysql_engine':'InnoDB',
1232 1231 'mysql_charset': 'utf8'},
1233 1232 )
1234 1233 repository_id = Column('repository_id', String(250), primary_key=True)
1235 1234 repository_path = Column('repository_path', Text)
1236 1235 version = Column('version', Integer)
1237 1236
1238 1237 ## this is migration from 1_4_0, but now it's here to overcome a problem of
1239 1238 ## attaching a FK to this from 1_3_0 !
1240 1239
1241 1240
1242 1241 class PullRequest(Base, BaseModel):
1243 1242 __tablename__ = 'pull_requests'
1244 1243 __table_args__ = (
1245 1244 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1246 1245 'mysql_charset': 'utf8'},
1247 1246 )
1248 1247
1249 1248 STATUS_NEW = u'new'
1250 1249 STATUS_OPEN = u'open'
1251 1250 STATUS_CLOSED = u'closed'
1252 1251
1253 1252 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1254 1253 title = Column('title', Unicode(256), nullable=True)
1255 1254 description = Column('description', UnicodeText().with_variant(UnicodeText(10240), 'mysql'), nullable=True)
1256 1255 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1257 1256 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1258 1257 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1259 1258 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1260 1259 _revisions = Column('revisions', UnicodeText().with_variant(UnicodeText(20500), 'mysql')) # 500 revisions max
1261 1260 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1262 1261 org_ref = Column('org_ref', Unicode(256), nullable=False)
1263 1262 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1264 1263 other_ref = Column('other_ref', Unicode(256), nullable=False)
@@ -1,972 +1,971 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21
22 22 import os
23 23 import time
24 24 import logging
25 25 import datetime
26 26 import traceback
27 27 import hashlib
28 28 import collections
29 29
30 30 from sqlalchemy import *
31 31 from sqlalchemy.ext.hybrid import hybrid_property
32 32 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
33 33 from sqlalchemy.exc import DatabaseError
34 34 from beaker.cache import cache_region, region_invalidate
35 35 from webob.exc import HTTPNotFound
36 36
37 37 from rhodecode.translation import _
38 38 from rhodecode.lib.vcs import get_backend
39 39 from rhodecode.lib.vcs.utils.helpers import get_scm
40 40 from rhodecode.lib.vcs.exceptions import VCSError
41 41 from zope.cachedescriptors.property import Lazy as LazyProperty
42 42
43 43 from rhodecode.lib.utils2 import str2bool, safe_str, get_commit_safe, \
44 44 safe_unicode, remove_suffix
45 45 from rhodecode.lib.ext_json import json
46 46 from rhodecode.lib.caching_query import FromCache
47 47
48 48 from rhodecode.model.meta import Base, Session
49 49
50 50 URL_SEP = '/'
51 51 log = logging.getLogger(__name__)
52 52
53 53 #==============================================================================
54 54 # BASE CLASSES
55 55 #==============================================================================
56 56
57 57 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
58 58
59 59
60 60 class BaseModel(object):
61 61 """
62 62 Base Model for all classes
63 63 """
64 64
65 65 @classmethod
66 66 def _get_keys(cls):
67 67 """return column names for this model """
68 68 return class_mapper(cls).c.keys()
69 69
70 70 def get_dict(self):
71 71 """
72 72 return dict with keys and values corresponding
73 73 to this model data """
74 74
75 75 d = {}
76 76 for k in self._get_keys():
77 77 d[k] = getattr(self, k)
78 78
79 79 # also use __json__() if present to get additional fields
80 80 _json_attr = getattr(self, '__json__', None)
81 81 if _json_attr:
82 82 # update with attributes from __json__
83 83 if callable(_json_attr):
84 84 _json_attr = _json_attr()
85 85 for k, val in _json_attr.iteritems():
86 86 d[k] = val
87 87 return d
88 88
89 89 def get_appstruct(self):
90 90 """return list with keys and values tupples corresponding
91 91 to this model data """
92 92
93 93 l = []
94 94 for k in self._get_keys():
95 95 l.append((k, getattr(self, k),))
96 96 return l
97 97
98 98 def populate_obj(self, populate_dict):
99 99 """populate model with data from given populate_dict"""
100 100
101 101 for k in self._get_keys():
102 102 if k in populate_dict:
103 103 setattr(self, k, populate_dict[k])
104 104
105 105 @classmethod
106 106 def query(cls):
107 107 return Session().query(cls)
108 108
109 109 @classmethod
110 110 def get(cls, id_):
111 111 if id_:
112 112 return cls.query().get(id_)
113 113
114 114 @classmethod
115 115 def get_or_404(cls, id_):
116 116 try:
117 117 id_ = int(id_)
118 118 except (TypeError, ValueError):
119 119 raise HTTPNotFound
120 120
121 121 res = cls.query().get(id_)
122 122 if not res:
123 123 raise HTTPNotFound
124 124 return res
125 125
126 126 @classmethod
127 127 def getAll(cls):
128 128 return cls.query().all()
129 129
130 130 @classmethod
131 131 def delete(cls, id_):
132 132 obj = cls.query().get(id_)
133 133 Session().delete(obj)
134 134
135 135 def __repr__(self):
136 136 if hasattr(self, '__unicode__'):
137 137 # python repr needs to return str
138 138 return safe_str(self.__unicode__())
139 139 return '<DB:%s>' % (self.__class__.__name__)
140 140
141 141
142 142 class RhodeCodeSetting(Base, BaseModel):
143 143 __tablename__ = 'rhodecode_settings'
144 144 __table_args__ = (
145 145 UniqueConstraint('app_settings_name'),
146 146 {'extend_existing': True, 'mysql_engine': 'InnoDB',
147 147 'mysql_charset': 'utf8'}
148 148 )
149 149 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
150 150 app_settings_name = Column("app_settings_name", String(255), nullable=True, unique=None, default=None)
151 151 _app_settings_value = Column("app_settings_value", String(255), nullable=True, unique=None, default=None)
152 152
153 153 def __init__(self, k='', v=''):
154 154 self.app_settings_name = k
155 155 self.app_settings_value = v
156 156
157 157 @validates('_app_settings_value')
158 158 def validate_settings_value(self, key, val):
159 159 assert type(val) == unicode
160 160 return val
161 161
162 162 @hybrid_property
163 163 def app_settings_value(self):
164 164 v = self._app_settings_value
165 165 if self.app_settings_name == 'ldap_active':
166 166 v = str2bool(v)
167 167 return v
168 168
169 169 @app_settings_value.setter
170 170 def app_settings_value(self, val):
171 171 """
172 172 Setter that will always make sure we use unicode in app_settings_value
173 173
174 174 :param val:
175 175 """
176 176 self._app_settings_value = safe_unicode(val)
177 177
178 178 def __unicode__(self):
179 179 return u"<%s('%s:%s')>" % (
180 180 self.__class__.__name__,
181 181 self.app_settings_name, self.app_settings_value
182 182 )
183 183
184 184
185 185 class RhodeCodeUi(Base, BaseModel):
186 186 __tablename__ = 'rhodecode_ui'
187 187 __table_args__ = (
188 188 UniqueConstraint('ui_key'),
189 189 {'extend_existing': True, 'mysql_engine': 'InnoDB',
190 190 'mysql_charset': 'utf8'}
191 191 )
192 192
193 193 HOOK_REPO_SIZE = 'changegroup.repo_size'
194 194 HOOK_PUSH = 'changegroup.push_logger'
195 195 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
196 196 HOOK_PULL = 'outgoing.pull_logger'
197 197 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
198 198
199 199 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
200 200 ui_section = Column("ui_section", String(255), nullable=True, unique=None, default=None)
201 201 ui_key = Column("ui_key", String(255), nullable=True, unique=None, default=None)
202 202 ui_value = Column("ui_value", String(255), nullable=True, unique=None, default=None)
203 203 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
204 204
205 205
206 206
207 207 class User(Base, BaseModel):
208 208 __tablename__ = 'users'
209 209 __table_args__ = (
210 210 UniqueConstraint('username'), UniqueConstraint('email'),
211 211 Index('u_username_idx', 'username'),
212 212 Index('u_email_idx', 'email'),
213 213 {'extend_existing': True, 'mysql_engine': 'InnoDB',
214 214 'mysql_charset': 'utf8'}
215 215 )
216 216 DEFAULT_USER = 'default'
217 217 DEFAULT_PERMISSIONS = [
218 218 'hg.register.manual_activate', 'hg.create.repository',
219 219 'hg.fork.repository', 'repository.read', 'group.read'
220 220 ]
221 221 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
222 222 username = Column("username", String(255), nullable=True, unique=None, default=None)
223 223 password = Column("password", String(255), nullable=True, unique=None, default=None)
224 224 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
225 225 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
226 226 name = Column("firstname", String(255), nullable=True, unique=None, default=None)
227 227 lastname = Column("lastname", String(255), nullable=True, unique=None, default=None)
228 228 _email = Column("email", String(255), nullable=True, unique=None, default=None)
229 229 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
230 230 ldap_dn = Column("ldap_dn", String(255), nullable=True, unique=None, default=None)
231 231 api_key = Column("api_key", String(255), nullable=True, unique=None, default=None)
232 232 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
233 233
234 234 user_log = relationship('UserLog', cascade='all')
235 235 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
236 236
237 237 repositories = relationship('Repository')
238 238 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
239 239 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
240 240 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
241 241
242 242 group_member = relationship('UserGroupMember', cascade='all')
243 243
244 244 notifications = relationship('UserNotification', cascade='all')
245 245 # notifications assigned to this user
246 246 user_created_notifications = relationship('Notification', cascade='all')
247 247 # comments created by this user
248 248 user_comments = relationship('ChangesetComment', cascade='all')
249 249 user_emails = relationship('UserEmailMap', cascade='all')
250 250
251 251 @hybrid_property
252 252 def email(self):
253 253 return self._email
254 254
255 255 @email.setter
256 256 def email(self, val):
257 257 self._email = val.lower() if val else None
258 258
259 259 @property
260 260 def firstname(self):
261 261 # alias for future
262 262 return self.name
263 263
264 264 @property
265 265 def username_and_name(self):
266 266 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
267 267
268 268 @property
269 269 def full_name(self):
270 270 return '%s %s' % (self.firstname, self.lastname)
271 271
272 272 @property
273 273 def full_contact(self):
274 274 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
275 275
276 276 @property
277 277 def short_contact(self):
278 278 return '%s %s' % (self.firstname, self.lastname)
279 279
280 280 @property
281 281 def is_admin(self):
282 282 return self.admin
283 283
284 284 @classmethod
285 285 def get_by_username(cls, username, case_insensitive=False, cache=False):
286 286 if case_insensitive:
287 287 q = cls.query().filter(cls.username.ilike(username))
288 288 else:
289 289 q = cls.query().filter(cls.username == username)
290 290
291 291 if cache:
292 292 q = q.options(FromCache(
293 293 "sql_cache_short",
294 294 "get_user_%s" % _hash_key(username)
295 295 )
296 296 )
297 297 return q.scalar()
298 298
299 299 @classmethod
300 300 def get_by_auth_token(cls, auth_token, cache=False):
301 301 q = cls.query().filter(cls.api_key == auth_token)
302 302
303 303 if cache:
304 304 q = q.options(FromCache("sql_cache_short",
305 305 "get_auth_token_%s" % auth_token))
306 306 return q.scalar()
307 307
308 308 @classmethod
309 309 def get_by_email(cls, email, case_insensitive=False, cache=False):
310 310 if case_insensitive:
311 311 q = cls.query().filter(cls.email.ilike(email))
312 312 else:
313 313 q = cls.query().filter(cls.email == email)
314 314
315 315 if cache:
316 316 q = q.options(FromCache("sql_cache_short",
317 317 "get_email_key_%s" % email))
318 318
319 319 ret = q.scalar()
320 320 if ret is None:
321 321 q = UserEmailMap.query()
322 322 # try fetching in alternate email map
323 323 if case_insensitive:
324 324 q = q.filter(UserEmailMap.email.ilike(email))
325 325 else:
326 326 q = q.filter(UserEmailMap.email == email)
327 327 q = q.options(joinedload(UserEmailMap.user))
328 328 if cache:
329 329 q = q.options(FromCache("sql_cache_short",
330 330 "get_email_map_key_%s" % email))
331 331 ret = getattr(q.scalar(), 'user', None)
332 332
333 333 return ret
334 334
335 335
336 336 class UserEmailMap(Base, BaseModel):
337 337 __tablename__ = 'user_email_map'
338 338 __table_args__ = (
339 339 Index('uem_email_idx', 'email'),
340 340 UniqueConstraint('email'),
341 341 {'extend_existing': True, 'mysql_engine': 'InnoDB',
342 342 'mysql_charset': 'utf8'}
343 343 )
344 344 __mapper_args__ = {}
345 345
346 346 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
347 347 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
348 348 _email = Column("email", String(255), nullable=True, unique=False, default=None)
349 349 user = relationship('User', lazy='joined')
350 350
351 351 @validates('_email')
352 352 def validate_email(self, key, email):
353 353 # check if this email is not main one
354 354 main_email = Session().query(User).filter(User.email == email).scalar()
355 355 if main_email is not None:
356 356 raise AttributeError('email %s is present is user table' % email)
357 357 return email
358 358
359 359 @hybrid_property
360 360 def email(self):
361 361 return self._email
362 362
363 363 @email.setter
364 364 def email(self, val):
365 365 self._email = val.lower() if val else None
366 366
367 367
368 368 class UserLog(Base, BaseModel):
369 369 __tablename__ = 'user_logs'
370 370 __table_args__ = (
371 371 {'extend_existing': True, 'mysql_engine': 'InnoDB',
372 372 'mysql_charset': 'utf8'},
373 373 )
374 374 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
375 375 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
376 376 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
377 377 repository_name = Column("repository_name", String(255), nullable=True, unique=None, default=None)
378 378 user_ip = Column("user_ip", String(255), nullable=True, unique=None, default=None)
379 379 action = Column("action", String(1200000), nullable=True, unique=None, default=None)
380 380 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
381 381
382 382
383 383 user = relationship('User')
384 384 repository = relationship('Repository', cascade='')
385 385
386 386
387 387 class UserGroup(Base, BaseModel):
388 388 __tablename__ = 'users_groups'
389 389 __table_args__ = (
390 390 {'extend_existing': True, 'mysql_engine': 'InnoDB',
391 391 'mysql_charset': 'utf8'},
392 392 )
393 393
394 394 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
395 395 users_group_name = Column("users_group_name", String(255), nullable=False, unique=True, default=None)
396 396 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
397 397 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
398 398
399 399 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
400 400 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
401 401 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
402 402
403 403 def __unicode__(self):
404 404 return u'<userGroup(%s)>' % (self.users_group_name)
405 405
406 406 @classmethod
407 407 def get_by_group_name(cls, group_name, cache=False,
408 408 case_insensitive=False):
409 409 if case_insensitive:
410 410 q = cls.query().filter(cls.users_group_name.ilike(group_name))
411 411 else:
412 412 q = cls.query().filter(cls.users_group_name == group_name)
413 413 if cache:
414 414 q = q.options(FromCache(
415 415 "sql_cache_short",
416 416 "get_user_%s" % _hash_key(group_name)
417 417 )
418 418 )
419 419 return q.scalar()
420 420
421 421 @classmethod
422 422 def get(cls, users_group_id, cache=False):
423 423 user_group = cls.query()
424 424 if cache:
425 425 user_group = user_group.options(FromCache("sql_cache_short",
426 426 "get_users_group_%s" % users_group_id))
427 427 return user_group.get(users_group_id)
428 428
429 429
430 430 class UserGroupMember(Base, BaseModel):
431 431 __tablename__ = 'users_groups_members'
432 432 __table_args__ = (
433 433 {'extend_existing': True, 'mysql_engine': 'InnoDB',
434 434 'mysql_charset': 'utf8'},
435 435 )
436 436
437 437 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
438 438 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
439 439 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
440 440
441 441 user = relationship('User', lazy='joined')
442 442 users_group = relationship('UserGroup')
443 443
444 444 def __init__(self, gr_id='', u_id=''):
445 445 self.users_group_id = gr_id
446 446 self.user_id = u_id
447 447
448 448
449 449 class Repository(Base, BaseModel):
450 450 __tablename__ = 'repositories'
451 451 __table_args__ = (
452 452 UniqueConstraint('repo_name'),
453 453 Index('r_repo_name_idx', 'repo_name'),
454 454 {'extend_existing': True, 'mysql_engine': 'InnoDB',
455 455 'mysql_charset': 'utf8'},
456 456 )
457 457
458 458 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
459 459 repo_name = Column("repo_name", String(255), nullable=False, unique=True, default=None)
460 460 clone_uri = Column("clone_uri", String(255), nullable=True, unique=False, default=None)
461 461 repo_type = Column("repo_type", String(255), nullable=False, unique=False, default=None)
462 462 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
463 463 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
464 464 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
465 465 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
466 466 description = Column("description", String(10000), nullable=True, unique=None, default=None)
467 467 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
468 468 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
469 469 landing_rev = Column("landing_revision", String(255), nullable=False, unique=False, default=None)
470 470 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
471 471 _locked = Column("locked", String(255), nullable=True, unique=False, default=None)
472 472
473 473 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
474 474 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
475 475
476 476 user = relationship('User')
477 477 fork = relationship('Repository', remote_side=repo_id)
478 478 group = relationship('RepoGroup')
479 479 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
480 480 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
481 481 stats = relationship('Statistics', cascade='all', uselist=False)
482 482
483 483 followers = relationship('UserFollowing',
484 484 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
485 485 cascade='all')
486 486
487 487 logs = relationship('UserLog')
488 488 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
489 489
490 490 pull_requests_org = relationship('PullRequest',
491 491 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
492 492 cascade="all, delete, delete-orphan")
493 493
494 494 pull_requests_other = relationship('PullRequest',
495 495 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
496 496 cascade="all, delete, delete-orphan")
497 497
498 498 def __unicode__(self):
499 499 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
500 500 self.repo_name)
501 501
502 502
503 503 @classmethod
504 504 def get_by_repo_name(cls, repo_name):
505 505 q = Session().query(cls).filter(cls.repo_name == repo_name)
506 506 q = q.options(joinedload(Repository.fork))\
507 507 .options(joinedload(Repository.user))\
508 508 .options(joinedload(Repository.group))
509 509 return q.scalar()
510 510
511 511
512 512 class RepoGroup(Base, BaseModel):
513 513 __tablename__ = 'groups'
514 514 __table_args__ = (
515 515 UniqueConstraint('group_name', 'group_parent_id'),
516 CheckConstraint('group_id != group_parent_id'),
517 516 {'extend_existing': True, 'mysql_engine': 'InnoDB',
518 517 'mysql_charset': 'utf8'},
519 518 )
520 519 __mapper_args__ = {'order_by': 'group_name'}
521 520
522 521 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
523 522 group_name = Column("group_name", String(255), nullable=False, unique=True, default=None)
524 523 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
525 524 group_description = Column("group_description", String(10000), nullable=True, unique=None, default=None)
526 525 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
527 526
528 527 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
529 528 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
530 529 parent_group = relationship('RepoGroup', remote_side=group_id)
531 530
532 531 def __init__(self, group_name='', parent_group=None):
533 532 self.group_name = group_name
534 533 self.parent_group = parent_group
535 534
536 535 def __unicode__(self):
537 536 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
538 537 self.group_name)
539 538
540 539 @classmethod
541 540 def url_sep(cls):
542 541 return URL_SEP
543 542
544 543 @classmethod
545 544 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
546 545 if case_insensitive:
547 546 gr = cls.query()\
548 547 .filter(cls.group_name.ilike(group_name))
549 548 else:
550 549 gr = cls.query()\
551 550 .filter(cls.group_name == group_name)
552 551 if cache:
553 552 gr = gr.options(FromCache(
554 553 "sql_cache_short",
555 554 "get_group_%s" % _hash_key(group_name)
556 555 )
557 556 )
558 557 return gr.scalar()
559 558
560 559
561 560 class Permission(Base, BaseModel):
562 561 __tablename__ = 'permissions'
563 562 __table_args__ = (
564 563 Index('p_perm_name_idx', 'permission_name'),
565 564 {'extend_existing': True, 'mysql_engine': 'InnoDB',
566 565 'mysql_charset': 'utf8'},
567 566 )
568 567 PERMS = [
569 568 ('repository.none', _('Repository no access')),
570 569 ('repository.read', _('Repository read access')),
571 570 ('repository.write', _('Repository write access')),
572 571 ('repository.admin', _('Repository admin access')),
573 572
574 573 ('group.none', _('Repositories Group no access')),
575 574 ('group.read', _('Repositories Group read access')),
576 575 ('group.write', _('Repositories Group write access')),
577 576 ('group.admin', _('Repositories Group admin access')),
578 577
579 578 ('hg.admin', _('RhodeCode Administrator')),
580 579 ('hg.create.none', _('Repository creation disabled')),
581 580 ('hg.create.repository', _('Repository creation enabled')),
582 581 ('hg.fork.none', _('Repository forking disabled')),
583 582 ('hg.fork.repository', _('Repository forking enabled')),
584 583 ('hg.register.none', _('Register disabled')),
585 584 ('hg.register.manual_activate', _('Register new user with RhodeCode '
586 585 'with manual activation')),
587 586
588 587 ('hg.register.auto_activate', _('Register new user with RhodeCode '
589 588 'with auto activation')),
590 589 ]
591 590
592 591 # defines which permissions are more important higher the more important
593 592 PERM_WEIGHTS = {
594 593 'repository.none': 0,
595 594 'repository.read': 1,
596 595 'repository.write': 3,
597 596 'repository.admin': 4,
598 597
599 598 'group.none': 0,
600 599 'group.read': 1,
601 600 'group.write': 3,
602 601 'group.admin': 4,
603 602
604 603 'hg.fork.none': 0,
605 604 'hg.fork.repository': 1,
606 605 'hg.create.none': 0,
607 606 'hg.create.repository':1
608 607 }
609 608
610 609 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
611 610 permission_name = Column("permission_name", String(255), nullable=True, unique=None, default=None)
612 611 permission_longname = Column("permission_longname", String(255), nullable=True, unique=None, default=None)
613 612
614 613 def __unicode__(self):
615 614 return u"<%s('%s:%s')>" % (
616 615 self.__class__.__name__, self.permission_id, self.permission_name
617 616 )
618 617
619 618 @classmethod
620 619 def get_by_key(cls, key):
621 620 return cls.query().filter(cls.permission_name == key).scalar()
622 621
623 622
624 623 class UserRepoToPerm(Base, BaseModel):
625 624 __tablename__ = 'repo_to_perm'
626 625 __table_args__ = (
627 626 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
628 627 {'extend_existing': True, 'mysql_engine': 'InnoDB',
629 628 'mysql_charset': 'utf8'}
630 629 )
631 630 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
632 631 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
633 632 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
634 633 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
635 634
636 635 user = relationship('User')
637 636 repository = relationship('Repository')
638 637 permission = relationship('Permission')
639 638
640 639 def __unicode__(self):
641 640 return u'<user:%s => %s >' % (self.user, self.repository)
642 641
643 642
644 643 class UserToPerm(Base, BaseModel):
645 644 __tablename__ = 'user_to_perm'
646 645 __table_args__ = (
647 646 UniqueConstraint('user_id', 'permission_id'),
648 647 {'extend_existing': True, 'mysql_engine': 'InnoDB',
649 648 'mysql_charset': 'utf8'}
650 649 )
651 650 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
652 651 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
653 652 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
654 653
655 654 user = relationship('User')
656 655 permission = relationship('Permission', lazy='joined')
657 656
658 657
659 658 class UserGroupRepoToPerm(Base, BaseModel):
660 659 __tablename__ = 'users_group_repo_to_perm'
661 660 __table_args__ = (
662 661 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
663 662 {'extend_existing': True, 'mysql_engine': 'InnoDB',
664 663 'mysql_charset': 'utf8'}
665 664 )
666 665 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
667 666 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
668 667 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
669 668 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
670 669
671 670 users_group = relationship('UserGroup')
672 671 permission = relationship('Permission')
673 672 repository = relationship('Repository')
674 673
675 674 def __unicode__(self):
676 675 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
677 676
678 677
679 678 class UserGroupToPerm(Base, BaseModel):
680 679 __tablename__ = 'users_group_to_perm'
681 680 __table_args__ = (
682 681 UniqueConstraint('users_group_id', 'permission_id',),
683 682 {'extend_existing': True, 'mysql_engine': 'InnoDB',
684 683 'mysql_charset': 'utf8'}
685 684 )
686 685 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
687 686 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
688 687 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
689 688
690 689 users_group = relationship('UserGroup')
691 690 permission = relationship('Permission')
692 691
693 692
694 693 class UserRepoGroupToPerm(Base, BaseModel):
695 694 __tablename__ = 'user_repo_group_to_perm'
696 695 __table_args__ = (
697 696 UniqueConstraint('user_id', 'group_id', 'permission_id'),
698 697 {'extend_existing': True, 'mysql_engine': 'InnoDB',
699 698 'mysql_charset': 'utf8'}
700 699 )
701 700
702 701 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
703 702 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
704 703 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
705 704 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
706 705
707 706 user = relationship('User')
708 707 group = relationship('RepoGroup')
709 708 permission = relationship('Permission')
710 709
711 710
712 711 class UserGroupRepoGroupToPerm(Base, BaseModel):
713 712 __tablename__ = 'users_group_repo_group_to_perm'
714 713 __table_args__ = (
715 714 UniqueConstraint('users_group_id', 'group_id'),
716 715 {'extend_existing': True, 'mysql_engine': 'InnoDB',
717 716 'mysql_charset': 'utf8'}
718 717 )
719 718
720 719 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)
721 720 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
722 721 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
723 722 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
724 723
725 724 users_group = relationship('UserGroup')
726 725 permission = relationship('Permission')
727 726 group = relationship('RepoGroup')
728 727
729 728
730 729 class Statistics(Base, BaseModel):
731 730 __tablename__ = 'statistics'
732 731 __table_args__ = (
733 732 UniqueConstraint('repository_id'),
734 733 {'extend_existing': True, 'mysql_engine': 'InnoDB',
735 734 'mysql_charset': 'utf8'}
736 735 )
737 736 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
738 737 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
739 738 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
740 739 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
741 740 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
742 741 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
743 742
744 743 repository = relationship('Repository', single_parent=True)
745 744
746 745
747 746 class UserFollowing(Base, BaseModel):
748 747 __tablename__ = 'user_followings'
749 748 __table_args__ = (
750 749 UniqueConstraint('user_id', 'follows_repository_id'),
751 750 UniqueConstraint('user_id', 'follows_user_id'),
752 751 {'extend_existing': True, 'mysql_engine': 'InnoDB',
753 752 'mysql_charset': 'utf8'}
754 753 )
755 754
756 755 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
757 756 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
758 757 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
759 758 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
760 759 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
761 760
762 761 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
763 762
764 763 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
765 764 follows_repository = relationship('Repository', order_by='Repository.repo_name')
766 765
767 766
768 767 class CacheInvalidation(Base, BaseModel):
769 768 __tablename__ = 'cache_invalidation'
770 769 __table_args__ = (
771 770 UniqueConstraint('cache_key'),
772 771 Index('key_idx', 'cache_key'),
773 772 {'extend_existing': True, 'mysql_engine': 'InnoDB',
774 773 'mysql_charset': 'utf8'},
775 774 )
776 775 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
777 776 cache_key = Column("cache_key", String(255), nullable=True, unique=None, default=None)
778 777 cache_args = Column("cache_args", String(255), nullable=True, unique=None, default=None)
779 778 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
780 779
781 780 def __init__(self, cache_key, cache_args=''):
782 781 self.cache_key = cache_key
783 782 self.cache_args = cache_args
784 783 self.cache_active = False
785 784
786 785
787 786 class ChangesetComment(Base, BaseModel):
788 787 __tablename__ = 'changeset_comments'
789 788 __table_args__ = (
790 789 Index('cc_revision_idx', 'revision'),
791 790 {'extend_existing': True, 'mysql_engine': 'InnoDB',
792 791 'mysql_charset': 'utf8'},
793 792 )
794 793 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
795 794 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
796 795 revision = Column('revision', String(40), nullable=True)
797 796 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
798 797 line_no = Column('line_no', Unicode(10), nullable=True)
799 798 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
800 799 f_path = Column('f_path', Unicode(1000), nullable=True)
801 800 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
802 801 text = Column('text', UnicodeText().with_variant(UnicodeText(25000), 'mysql'), nullable=False)
803 802 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
804 803 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
805 804
806 805 author = relationship('User', lazy='joined')
807 806 repo = relationship('Repository')
808 807 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
809 808 pull_request = relationship('PullRequest', lazy='joined')
810 809
811 810 @classmethod
812 811 def get_users(cls, revision=None, pull_request_id=None):
813 812 """
814 813 Returns user associated with this ChangesetComment. ie those
815 814 who actually commented
816 815
817 816 :param cls:
818 817 :param revision:
819 818 """
820 819 q = Session().query(User)\
821 820 .join(ChangesetComment.author)
822 821 if revision:
823 822 q = q.filter(cls.revision == revision)
824 823 elif pull_request_id:
825 824 q = q.filter(cls.pull_request_id == pull_request_id)
826 825 return q.all()
827 826
828 827
829 828 class ChangesetStatus(Base, BaseModel):
830 829 __tablename__ = 'changeset_statuses'
831 830 __table_args__ = (
832 831 Index('cs_revision_idx', 'revision'),
833 832 Index('cs_version_idx', 'version'),
834 833 UniqueConstraint('repo_id', 'revision', 'version'),
835 834 {'extend_existing': True, 'mysql_engine': 'InnoDB',
836 835 'mysql_charset': 'utf8'}
837 836 )
838 837 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
839 838 STATUS_APPROVED = 'approved'
840 839 STATUS_REJECTED = 'rejected'
841 840 STATUS_UNDER_REVIEW = 'under_review'
842 841
843 842 STATUSES = [
844 843 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
845 844 (STATUS_APPROVED, _("Approved")),
846 845 (STATUS_REJECTED, _("Rejected")),
847 846 (STATUS_UNDER_REVIEW, _("Under Review")),
848 847 ]
849 848
850 849 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
851 850 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
852 851 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
853 852 revision = Column('revision', String(40), nullable=False)
854 853 status = Column('status', String(128), nullable=False, default=DEFAULT)
855 854 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
856 855 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
857 856 version = Column('version', Integer(), nullable=False, default=0)
858 857 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
859 858
860 859 author = relationship('User', lazy='joined')
861 860 repo = relationship('Repository')
862 861 comment = relationship('ChangesetComment', lazy='joined')
863 862 pull_request = relationship('PullRequest', lazy='joined')
864 863
865 864
866 865
867 866 class PullRequest(Base, BaseModel):
868 867 __tablename__ = 'pull_requests'
869 868 __table_args__ = (
870 869 {'extend_existing': True, 'mysql_engine': 'InnoDB',
871 870 'mysql_charset': 'utf8'},
872 871 )
873 872
874 873 STATUS_NEW = u'new'
875 874 STATUS_OPEN = u'open'
876 875 STATUS_CLOSED = u'closed'
877 876
878 877 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
879 878 title = Column('title', Unicode(256), nullable=True)
880 879 description = Column('description', UnicodeText().with_variant(UnicodeText(10240), 'mysql'), nullable=True)
881 880 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
882 881 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
883 882 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
884 883 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
885 884 _revisions = Column('revisions', UnicodeText().with_variant(UnicodeText(20500), 'mysql'))
886 885 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
887 886 org_ref = Column('org_ref', Unicode(256), nullable=False)
888 887 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
889 888 other_ref = Column('other_ref', Unicode(256), nullable=False)
890 889
891 890 author = relationship('User', lazy='joined')
892 891 reviewers = relationship('PullRequestReviewers',
893 892 cascade="all, delete, delete-orphan")
894 893 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
895 894 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
896 895 statuses = relationship('ChangesetStatus')
897 896 comments = relationship('ChangesetComment',
898 897 cascade="all, delete, delete-orphan")
899 898
900 899
901 900 class PullRequestReviewers(Base, BaseModel):
902 901 __tablename__ = 'pull_request_reviewers'
903 902 __table_args__ = (
904 903 {'extend_existing': True, 'mysql_engine': 'InnoDB',
905 904 'mysql_charset': 'utf8'},
906 905 )
907 906
908 907 def __init__(self, user=None, pull_request=None):
909 908 self.user = user
910 909 self.pull_request = pull_request
911 910
912 911 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
913 912 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
914 913 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
915 914
916 915 user = relationship('User')
917 916 pull_request = relationship('PullRequest')
918 917
919 918
920 919 class Notification(Base, BaseModel):
921 920 __tablename__ = 'notifications'
922 921 __table_args__ = (
923 922 Index('notification_type_idx', 'type'),
924 923 {'extend_existing': True, 'mysql_engine': 'InnoDB',
925 924 'mysql_charset': 'utf8'},
926 925 )
927 926
928 927 TYPE_CHANGESET_COMMENT = u'cs_comment'
929 928 TYPE_MESSAGE = u'message'
930 929 TYPE_MENTION = u'mention'
931 930 TYPE_REGISTRATION = u'registration'
932 931 TYPE_PULL_REQUEST = u'pull_request'
933 932 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
934 933
935 934 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
936 935 subject = Column('subject', Unicode(512), nullable=True)
937 936 body = Column('body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True)
938 937 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
939 938 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
940 939 type_ = Column('type', Unicode(256))
941 940
942 941 created_by_user = relationship('User')
943 942 notifications_to_users = relationship('UserNotification', lazy='joined',
944 943 cascade="all, delete, delete-orphan")
945 944
946 945
947 946 class UserNotification(Base, BaseModel):
948 947 __tablename__ = 'user_to_notification'
949 948 __table_args__ = (
950 949 UniqueConstraint('user_id', 'notification_id'),
951 950 {'extend_existing': True, 'mysql_engine': 'InnoDB',
952 951 'mysql_charset': 'utf8'}
953 952 )
954 953 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
955 954 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
956 955 read = Column('read', Boolean, default=False)
957 956 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
958 957
959 958 user = relationship('User', lazy="joined")
960 959 notification = relationship('Notification', lazy="joined",
961 960 order_by=lambda: Notification.created_on.desc(),)
962 961
963 962
964 963 class DbMigrateVersion(Base, BaseModel):
965 964 __tablename__ = 'db_migrate_version'
966 965 __table_args__ = (
967 966 {'extend_existing': True, 'mysql_engine': 'InnoDB',
968 967 'mysql_charset': 'utf8'},
969 968 )
970 969 repository_id = Column('repository_id', String(250), primary_key=True)
971 970 repository_path = Column('repository_path', Text)
972 971 version = Column('version', Integer)
@@ -1,993 +1,992 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21
22 22 import os
23 23 import time
24 24 import logging
25 25 import datetime
26 26 import traceback
27 27 import hashlib
28 28 import collections
29 29
30 30 from sqlalchemy import *
31 31 from sqlalchemy.ext.hybrid import hybrid_property
32 32 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
33 33 from sqlalchemy.exc import DatabaseError
34 34 from beaker.cache import cache_region, region_invalidate
35 35 from webob.exc import HTTPNotFound
36 36
37 37 from rhodecode.translation import _
38 38
39 39 from rhodecode.lib.vcs import get_backend
40 40 from rhodecode.lib.vcs.utils.helpers import get_scm
41 41 from rhodecode.lib.vcs.exceptions import VCSError
42 42 from zope.cachedescriptors.property import Lazy as LazyProperty
43 43
44 44 from rhodecode.lib.utils2 import str2bool, safe_str, get_commit_safe, \
45 45 safe_unicode, remove_suffix, remove_prefix
46 46 from rhodecode.lib.ext_json import json
47 47 from rhodecode.lib.caching_query import FromCache
48 48
49 49 from rhodecode.model.meta import Base, Session
50 50
51 51 URL_SEP = '/'
52 52 log = logging.getLogger(__name__)
53 53
54 54 #==============================================================================
55 55 # BASE CLASSES
56 56 #==============================================================================
57 57
58 58 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
59 59
60 60
61 61 class BaseModel(object):
62 62 """
63 63 Base Model for all classes
64 64 """
65 65
66 66 @classmethod
67 67 def _get_keys(cls):
68 68 """return column names for this model """
69 69 return class_mapper(cls).c.keys()
70 70
71 71 def get_dict(self):
72 72 """
73 73 return dict with keys and values corresponding
74 74 to this model data """
75 75
76 76 d = {}
77 77 for k in self._get_keys():
78 78 d[k] = getattr(self, k)
79 79
80 80 # also use __json__() if present to get additional fields
81 81 _json_attr = getattr(self, '__json__', None)
82 82 if _json_attr:
83 83 # update with attributes from __json__
84 84 if callable(_json_attr):
85 85 _json_attr = _json_attr()
86 86 for k, val in _json_attr.iteritems():
87 87 d[k] = val
88 88 return d
89 89
90 90 def get_appstruct(self):
91 91 """return list with keys and values tupples corresponding
92 92 to this model data """
93 93
94 94 l = []
95 95 for k in self._get_keys():
96 96 l.append((k, getattr(self, k),))
97 97 return l
98 98
99 99 def populate_obj(self, populate_dict):
100 100 """populate model with data from given populate_dict"""
101 101
102 102 for k in self._get_keys():
103 103 if k in populate_dict:
104 104 setattr(self, k, populate_dict[k])
105 105
106 106 @classmethod
107 107 def query(cls):
108 108 return Session().query(cls)
109 109
110 110 @classmethod
111 111 def get(cls, id_):
112 112 if id_:
113 113 return cls.query().get(id_)
114 114
115 115 @classmethod
116 116 def get_or_404(cls, id_):
117 117 try:
118 118 id_ = int(id_)
119 119 except (TypeError, ValueError):
120 120 raise HTTPNotFound
121 121
122 122 res = cls.query().get(id_)
123 123 if not res:
124 124 raise HTTPNotFound
125 125 return res
126 126
127 127 @classmethod
128 128 def getAll(cls):
129 129 # deprecated and left for backward compatibility
130 130 return cls.get_all()
131 131
132 132 @classmethod
133 133 def get_all(cls):
134 134 return cls.query().all()
135 135
136 136 @classmethod
137 137 def delete(cls, id_):
138 138 obj = cls.query().get(id_)
139 139 Session().delete(obj)
140 140
141 141 def __repr__(self):
142 142 if hasattr(self, '__unicode__'):
143 143 # python repr needs to return str
144 144 return safe_str(self.__unicode__())
145 145 return '<DB:%s>' % (self.__class__.__name__)
146 146
147 147
148 148 class RhodeCodeSetting(Base, BaseModel):
149 149 __tablename__ = 'rhodecode_settings'
150 150 __table_args__ = (
151 151 UniqueConstraint('app_settings_name'),
152 152 {'extend_existing': True, 'mysql_engine': 'InnoDB',
153 153 'mysql_charset': 'utf8'}
154 154 )
155 155 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
156 156 app_settings_name = Column("app_settings_name", String(255), nullable=True, unique=None, default=None)
157 157 _app_settings_value = Column("app_settings_value", String(255), nullable=True, unique=None, default=None)
158 158
159 159 def __init__(self, k='', v=''):
160 160 self.app_settings_name = k
161 161 self.app_settings_value = v
162 162
163 163 @validates('_app_settings_value')
164 164 def validate_settings_value(self, key, val):
165 165 assert type(val) == unicode
166 166 return val
167 167
168 168 @hybrid_property
169 169 def app_settings_value(self):
170 170 v = self._app_settings_value
171 171 if self.app_settings_name in ["ldap_active",
172 172 "default_repo_enable_statistics",
173 173 "default_repo_enable_locking",
174 174 "default_repo_private",
175 175 "default_repo_enable_downloads"]:
176 176 v = str2bool(v)
177 177 return v
178 178
179 179 @app_settings_value.setter
180 180 def app_settings_value(self, val):
181 181 """
182 182 Setter that will always make sure we use unicode in app_settings_value
183 183
184 184 :param val:
185 185 """
186 186 self._app_settings_value = safe_unicode(val)
187 187
188 188 def __unicode__(self):
189 189 return u"<%s('%s:%s')>" % (
190 190 self.__class__.__name__,
191 191 self.app_settings_name, self.app_settings_value
192 192 )
193 193
194 194
195 195 class RhodeCodeUi(Base, BaseModel):
196 196 __tablename__ = 'rhodecode_ui'
197 197 __table_args__ = (
198 198 UniqueConstraint('ui_key'),
199 199 {'extend_existing': True, 'mysql_engine': 'InnoDB',
200 200 'mysql_charset': 'utf8'}
201 201 )
202 202
203 203 HOOK_REPO_SIZE = 'changegroup.repo_size'
204 204 HOOK_PUSH = 'changegroup.push_logger'
205 205 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
206 206 HOOK_PULL = 'outgoing.pull_logger'
207 207 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
208 208
209 209 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
210 210 ui_section = Column("ui_section", String(255), nullable=True, unique=None, default=None)
211 211 ui_key = Column("ui_key", String(255), nullable=True, unique=None, default=None)
212 212 ui_value = Column("ui_value", String(255), nullable=True, unique=None, default=None)
213 213 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
214 214
215 215
216 216
217 217 class User(Base, BaseModel):
218 218 __tablename__ = 'users'
219 219 __table_args__ = (
220 220 UniqueConstraint('username'), UniqueConstraint('email'),
221 221 Index('u_username_idx', 'username'),
222 222 Index('u_email_idx', 'email'),
223 223 {'extend_existing': True, 'mysql_engine': 'InnoDB',
224 224 'mysql_charset': 'utf8'}
225 225 )
226 226 DEFAULT_USER = 'default'
227 227 DEFAULT_PERMISSIONS = [
228 228 'hg.register.manual_activate', 'hg.create.repository',
229 229 'hg.fork.repository', 'repository.read', 'group.read'
230 230 ]
231 231 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
232 232 username = Column("username", String(255), nullable=True, unique=None, default=None)
233 233 password = Column("password", String(255), nullable=True, unique=None, default=None)
234 234 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
235 235 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
236 236 name = Column("firstname", String(255), nullable=True, unique=None, default=None)
237 237 lastname = Column("lastname", String(255), nullable=True, unique=None, default=None)
238 238 _email = Column("email", String(255), nullable=True, unique=None, default=None)
239 239 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
240 240 ldap_dn = Column("ldap_dn", String(255), nullable=True, unique=None, default=None)
241 241 api_key = Column("api_key", String(255), nullable=True, unique=None, default=None)
242 242 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
243 243
244 244 user_log = relationship('UserLog')
245 245 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
246 246
247 247 repositories = relationship('Repository')
248 248 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
249 249 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
250 250
251 251 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
252 252 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
253 253
254 254 group_member = relationship('UserGroupMember', cascade='all')
255 255
256 256 notifications = relationship('UserNotification', cascade='all')
257 257 # notifications assigned to this user
258 258 user_created_notifications = relationship('Notification', cascade='all')
259 259 # comments created by this user
260 260 user_comments = relationship('ChangesetComment', cascade='all')
261 261 user_emails = relationship('UserEmailMap', cascade='all')
262 262
263 263 @hybrid_property
264 264 def email(self):
265 265 return self._email
266 266
267 267 @email.setter
268 268 def email(self, val):
269 269 self._email = val.lower() if val else None
270 270
271 271 @property
272 272 def firstname(self):
273 273 # alias for future
274 274 return self.name
275 275
276 276 @property
277 277 def username_and_name(self):
278 278 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
279 279
280 280 @property
281 281 def full_name(self):
282 282 return '%s %s' % (self.firstname, self.lastname)
283 283
284 284 @property
285 285 def full_contact(self):
286 286 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
287 287
288 288 @property
289 289 def short_contact(self):
290 290 return '%s %s' % (self.firstname, self.lastname)
291 291
292 292 @property
293 293 def is_admin(self):
294 294 return self.admin
295 295
296 296 @classmethod
297 297 def get_by_username(cls, username, case_insensitive=False, cache=False):
298 298 if case_insensitive:
299 299 q = cls.query().filter(cls.username.ilike(username))
300 300 else:
301 301 q = cls.query().filter(cls.username == username)
302 302
303 303 if cache:
304 304 q = q.options(FromCache(
305 305 "sql_cache_short",
306 306 "get_user_%s" % _hash_key(username)
307 307 )
308 308 )
309 309 return q.scalar()
310 310
311 311 @classmethod
312 312 def get_by_auth_token(cls, auth_token, cache=False):
313 313 q = cls.query().filter(cls.api_key == auth_token)
314 314
315 315 if cache:
316 316 q = q.options(FromCache("sql_cache_short",
317 317 "get_auth_token_%s" % auth_token))
318 318 return q.scalar()
319 319
320 320 @classmethod
321 321 def get_by_email(cls, email, case_insensitive=False, cache=False):
322 322 if case_insensitive:
323 323 q = cls.query().filter(cls.email.ilike(email))
324 324 else:
325 325 q = cls.query().filter(cls.email == email)
326 326
327 327 if cache:
328 328 q = q.options(FromCache("sql_cache_short",
329 329 "get_email_key_%s" % email))
330 330
331 331 ret = q.scalar()
332 332 if ret is None:
333 333 q = UserEmailMap.query()
334 334 # try fetching in alternate email map
335 335 if case_insensitive:
336 336 q = q.filter(UserEmailMap.email.ilike(email))
337 337 else:
338 338 q = q.filter(UserEmailMap.email == email)
339 339 q = q.options(joinedload(UserEmailMap.user))
340 340 if cache:
341 341 q = q.options(FromCache("sql_cache_short",
342 342 "get_email_map_key_%s" % email))
343 343 ret = getattr(q.scalar(), 'user', None)
344 344
345 345 return ret
346 346
347 347
348 348 class UserEmailMap(Base, BaseModel):
349 349 __tablename__ = 'user_email_map'
350 350 __table_args__ = (
351 351 Index('uem_email_idx', 'email'),
352 352 UniqueConstraint('email'),
353 353 {'extend_existing': True, 'mysql_engine': 'InnoDB',
354 354 'mysql_charset': 'utf8'}
355 355 )
356 356 __mapper_args__ = {}
357 357
358 358 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
359 359 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
360 360 _email = Column("email", String(255), nullable=True, unique=False, default=None)
361 361 user = relationship('User', lazy='joined')
362 362
363 363 @validates('_email')
364 364 def validate_email(self, key, email):
365 365 # check if this email is not main one
366 366 main_email = Session().query(User).filter(User.email == email).scalar()
367 367 if main_email is not None:
368 368 raise AttributeError('email %s is present is user table' % email)
369 369 return email
370 370
371 371 @hybrid_property
372 372 def email(self):
373 373 return self._email
374 374
375 375 @email.setter
376 376 def email(self, val):
377 377 self._email = val.lower() if val else None
378 378
379 379
380 380 class UserLog(Base, BaseModel):
381 381 __tablename__ = 'user_logs'
382 382 __table_args__ = (
383 383 {'extend_existing': True, 'mysql_engine': 'InnoDB',
384 384 'mysql_charset': 'utf8'},
385 385 )
386 386 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
387 387 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
388 388 username = Column("username", String(255), nullable=True, unique=None, default=None)
389 389 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
390 390 repository_name = Column("repository_name", String(255), nullable=True, unique=None, default=None)
391 391 user_ip = Column("user_ip", String(255), nullable=True, unique=None, default=None)
392 392 action = Column("action", String(1200000), nullable=True, unique=None, default=None)
393 393 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
394 394
395 395
396 396 user = relationship('User')
397 397 repository = relationship('Repository', cascade='')
398 398
399 399
400 400 class UserGroup(Base, BaseModel):
401 401 __tablename__ = 'users_groups'
402 402 __table_args__ = (
403 403 {'extend_existing': True, 'mysql_engine': 'InnoDB',
404 404 'mysql_charset': 'utf8'},
405 405 )
406 406
407 407 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
408 408 users_group_name = Column("users_group_name", String(255), nullable=False, unique=True, default=None)
409 409 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
410 410 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
411 411
412 412 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
413 413 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
414 414 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
415 415
416 416 def __unicode__(self):
417 417 return u'<userGroup(%s)>' % (self.users_group_name)
418 418
419 419 @classmethod
420 420 def get_by_group_name(cls, group_name, cache=False,
421 421 case_insensitive=False):
422 422 if case_insensitive:
423 423 q = cls.query().filter(cls.users_group_name.ilike(group_name))
424 424 else:
425 425 q = cls.query().filter(cls.users_group_name == group_name)
426 426 if cache:
427 427 q = q.options(FromCache(
428 428 "sql_cache_short",
429 429 "get_user_%s" % _hash_key(group_name)
430 430 )
431 431 )
432 432 return q.scalar()
433 433
434 434 @classmethod
435 435 def get(cls, users_group_id, cache=False):
436 436 user_group = cls.query()
437 437 if cache:
438 438 user_group = user_group.options(FromCache("sql_cache_short",
439 439 "get_users_group_%s" % users_group_id))
440 440 return user_group.get(users_group_id)
441 441
442 442
443 443 class UserGroupMember(Base, BaseModel):
444 444 __tablename__ = 'users_groups_members'
445 445 __table_args__ = (
446 446 {'extend_existing': True, 'mysql_engine': 'InnoDB',
447 447 'mysql_charset': 'utf8'},
448 448 )
449 449
450 450 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
451 451 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
452 452 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
453 453
454 454 user = relationship('User', lazy='joined')
455 455 users_group = relationship('UserGroup')
456 456
457 457 def __init__(self, gr_id='', u_id=''):
458 458 self.users_group_id = gr_id
459 459 self.user_id = u_id
460 460
461 461
462 462 class Repository(Base, BaseModel):
463 463 __tablename__ = 'repositories'
464 464 __table_args__ = (
465 465 UniqueConstraint('repo_name'),
466 466 Index('r_repo_name_idx', 'repo_name'),
467 467 {'extend_existing': True, 'mysql_engine': 'InnoDB',
468 468 'mysql_charset': 'utf8'},
469 469 )
470 470
471 471 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
472 472 repo_name = Column("repo_name", String(255), nullable=False, unique=True, default=None)
473 473 clone_uri = Column("clone_uri", String(255), nullable=True, unique=False, default=None)
474 474 repo_type = Column("repo_type", String(255), nullable=False, unique=False, default=None)
475 475 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
476 476 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
477 477 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
478 478 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
479 479 description = Column("description", String(10000), nullable=True, unique=None, default=None)
480 480 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
481 481 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
482 482 landing_rev = Column("landing_revision", String(255), nullable=False, unique=False, default=None)
483 483 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
484 484 _locked = Column("locked", String(255), nullable=True, unique=False, default=None)
485 485
486 486 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
487 487 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
488 488
489 489 user = relationship('User')
490 490 fork = relationship('Repository', remote_side=repo_id)
491 491 group = relationship('RepoGroup')
492 492 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
493 493 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
494 494 stats = relationship('Statistics', cascade='all', uselist=False)
495 495
496 496 followers = relationship('UserFollowing',
497 497 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
498 498 cascade='all')
499 499
500 500 logs = relationship('UserLog')
501 501 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
502 502
503 503 pull_requests_org = relationship('PullRequest',
504 504 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
505 505 cascade="all, delete, delete-orphan")
506 506
507 507 pull_requests_other = relationship('PullRequest',
508 508 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
509 509 cascade="all, delete, delete-orphan")
510 510
511 511 def __unicode__(self):
512 512 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
513 513 self.repo_name)
514 514
515 515
516 516 @classmethod
517 517 def get_by_repo_name(cls, repo_name):
518 518 q = Session().query(cls).filter(cls.repo_name == repo_name)
519 519 q = q.options(joinedload(Repository.fork))\
520 520 .options(joinedload(Repository.user))\
521 521 .options(joinedload(Repository.group))
522 522 return q.scalar()
523 523
524 524
525 525 class RepoGroup(Base, BaseModel):
526 526 __tablename__ = 'groups'
527 527 __table_args__ = (
528 528 UniqueConstraint('group_name', 'group_parent_id'),
529 CheckConstraint('group_id != group_parent_id'),
530 529 {'extend_existing': True, 'mysql_engine': 'InnoDB',
531 530 'mysql_charset': 'utf8'},
532 531 )
533 532 __mapper_args__ = {'order_by': 'group_name'}
534 533
535 534 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
536 535 group_name = Column("group_name", String(255), nullable=False, unique=True, default=None)
537 536 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
538 537 group_description = Column("group_description", String(10000), nullable=True, unique=None, default=None)
539 538 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
540 539
541 540 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
542 541 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
543 542 parent_group = relationship('RepoGroup', remote_side=group_id)
544 543
545 544 def __init__(self, group_name='', parent_group=None):
546 545 self.group_name = group_name
547 546 self.parent_group = parent_group
548 547
549 548 def __unicode__(self):
550 549 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
551 550 self.group_name)
552 551
553 552 @classmethod
554 553 def url_sep(cls):
555 554 return URL_SEP
556 555
557 556 @classmethod
558 557 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
559 558 if case_insensitive:
560 559 gr = cls.query()\
561 560 .filter(cls.group_name.ilike(group_name))
562 561 else:
563 562 gr = cls.query()\
564 563 .filter(cls.group_name == group_name)
565 564 if cache:
566 565 gr = gr.options(FromCache(
567 566 "sql_cache_short",
568 567 "get_group_%s" % _hash_key(group_name)
569 568 )
570 569 )
571 570 return gr.scalar()
572 571
573 572
574 573 class Permission(Base, BaseModel):
575 574 __tablename__ = 'permissions'
576 575 __table_args__ = (
577 576 Index('p_perm_name_idx', 'permission_name'),
578 577 {'extend_existing': True, 'mysql_engine': 'InnoDB',
579 578 'mysql_charset': 'utf8'},
580 579 )
581 580 PERMS = [
582 581 ('repository.none', _('Repository no access')),
583 582 ('repository.read', _('Repository read access')),
584 583 ('repository.write', _('Repository write access')),
585 584 ('repository.admin', _('Repository admin access')),
586 585
587 586 ('group.none', _('Repositories Group no access')),
588 587 ('group.read', _('Repositories Group read access')),
589 588 ('group.write', _('Repositories Group write access')),
590 589 ('group.admin', _('Repositories Group admin access')),
591 590
592 591 ('hg.admin', _('RhodeCode Administrator')),
593 592 ('hg.create.none', _('Repository creation disabled')),
594 593 ('hg.create.repository', _('Repository creation enabled')),
595 594 ('hg.fork.none', _('Repository forking disabled')),
596 595 ('hg.fork.repository', _('Repository forking enabled')),
597 596 ('hg.register.none', _('Register disabled')),
598 597 ('hg.register.manual_activate', _('Register new user with RhodeCode '
599 598 'with manual activation')),
600 599
601 600 ('hg.register.auto_activate', _('Register new user with RhodeCode '
602 601 'with auto activation')),
603 602 ]
604 603
605 604 # defines which permissions are more important higher the more important
606 605 PERM_WEIGHTS = {
607 606 'repository.none': 0,
608 607 'repository.read': 1,
609 608 'repository.write': 3,
610 609 'repository.admin': 4,
611 610
612 611 'group.none': 0,
613 612 'group.read': 1,
614 613 'group.write': 3,
615 614 'group.admin': 4,
616 615
617 616 'hg.fork.none': 0,
618 617 'hg.fork.repository': 1,
619 618 'hg.create.none': 0,
620 619 'hg.create.repository':1
621 620 }
622 621
623 622 DEFAULT_USER_PERMISSIONS = [
624 623 'repository.read',
625 624 'group.read',
626 625 'hg.create.repository',
627 626 'hg.fork.repository',
628 627 'hg.register.manual_activate',
629 628 ]
630 629
631 630 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
632 631 permission_name = Column("permission_name", String(255), nullable=True, unique=None, default=None)
633 632 permission_longname = Column("permission_longname", String(255), nullable=True, unique=None, default=None)
634 633
635 634 def __unicode__(self):
636 635 return u"<%s('%s:%s')>" % (
637 636 self.__class__.__name__, self.permission_id, self.permission_name
638 637 )
639 638
640 639 @classmethod
641 640 def get_by_key(cls, key):
642 641 return cls.query().filter(cls.permission_name == key).scalar()
643 642
644 643
645 644 class UserRepoToPerm(Base, BaseModel):
646 645 __tablename__ = 'repo_to_perm'
647 646 __table_args__ = (
648 647 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
649 648 {'extend_existing': True, 'mysql_engine': 'InnoDB',
650 649 'mysql_charset': 'utf8'}
651 650 )
652 651 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
653 652 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
654 653 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
655 654 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
656 655
657 656 user = relationship('User')
658 657 repository = relationship('Repository')
659 658 permission = relationship('Permission')
660 659
661 660 def __unicode__(self):
662 661 return u'<user:%s => %s >' % (self.user, self.repository)
663 662
664 663
665 664 class UserToPerm(Base, BaseModel):
666 665 __tablename__ = 'user_to_perm'
667 666 __table_args__ = (
668 667 UniqueConstraint('user_id', 'permission_id'),
669 668 {'extend_existing': True, 'mysql_engine': 'InnoDB',
670 669 'mysql_charset': 'utf8'}
671 670 )
672 671 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
673 672 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
674 673 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
675 674
676 675 user = relationship('User')
677 676 permission = relationship('Permission', lazy='joined')
678 677
679 678
680 679 class UserGroupRepoToPerm(Base, BaseModel):
681 680 __tablename__ = 'users_group_repo_to_perm'
682 681 __table_args__ = (
683 682 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
684 683 {'extend_existing': True, 'mysql_engine': 'InnoDB',
685 684 'mysql_charset': 'utf8'}
686 685 )
687 686 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
688 687 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
689 688 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
690 689 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
691 690
692 691 users_group = relationship('UserGroup')
693 692 permission = relationship('Permission')
694 693 repository = relationship('Repository')
695 694
696 695 def __unicode__(self):
697 696 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
698 697
699 698
700 699 class UserGroupToPerm(Base, BaseModel):
701 700 __tablename__ = 'users_group_to_perm'
702 701 __table_args__ = (
703 702 UniqueConstraint('users_group_id', 'permission_id',),
704 703 {'extend_existing': True, 'mysql_engine': 'InnoDB',
705 704 'mysql_charset': 'utf8'}
706 705 )
707 706 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
708 707 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
709 708 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
710 709
711 710 users_group = relationship('UserGroup')
712 711 permission = relationship('Permission')
713 712
714 713
715 714 class UserRepoGroupToPerm(Base, BaseModel):
716 715 __tablename__ = 'user_repo_group_to_perm'
717 716 __table_args__ = (
718 717 UniqueConstraint('user_id', 'group_id', 'permission_id'),
719 718 {'extend_existing': True, 'mysql_engine': 'InnoDB',
720 719 'mysql_charset': 'utf8'}
721 720 )
722 721
723 722 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
724 723 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
725 724 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
726 725 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
727 726
728 727 user = relationship('User')
729 728 group = relationship('RepoGroup')
730 729 permission = relationship('Permission')
731 730
732 731
733 732 class UserGroupRepoGroupToPerm(Base, BaseModel):
734 733 __tablename__ = 'users_group_repo_group_to_perm'
735 734 __table_args__ = (
736 735 UniqueConstraint('users_group_id', 'group_id'),
737 736 {'extend_existing': True, 'mysql_engine': 'InnoDB',
738 737 'mysql_charset': 'utf8'}
739 738 )
740 739
741 740 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)
742 741 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
743 742 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
744 743 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
745 744
746 745 users_group = relationship('UserGroup')
747 746 permission = relationship('Permission')
748 747 group = relationship('RepoGroup')
749 748
750 749
751 750 class Statistics(Base, BaseModel):
752 751 __tablename__ = 'statistics'
753 752 __table_args__ = (
754 753 UniqueConstraint('repository_id'),
755 754 {'extend_existing': True, 'mysql_engine': 'InnoDB',
756 755 'mysql_charset': 'utf8'}
757 756 )
758 757 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
759 758 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
760 759 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
761 760 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
762 761 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
763 762 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
764 763
765 764 repository = relationship('Repository', single_parent=True)
766 765
767 766
768 767 class UserFollowing(Base, BaseModel):
769 768 __tablename__ = 'user_followings'
770 769 __table_args__ = (
771 770 UniqueConstraint('user_id', 'follows_repository_id'),
772 771 UniqueConstraint('user_id', 'follows_user_id'),
773 772 {'extend_existing': True, 'mysql_engine': 'InnoDB',
774 773 'mysql_charset': 'utf8'}
775 774 )
776 775
777 776 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
778 777 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
779 778 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
780 779 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
781 780 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
782 781
783 782 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
784 783
785 784 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
786 785 follows_repository = relationship('Repository', order_by='Repository.repo_name')
787 786
788 787
789 788 class CacheInvalidation(Base, BaseModel):
790 789 __tablename__ = 'cache_invalidation'
791 790 __table_args__ = (
792 791 UniqueConstraint('cache_key'),
793 792 Index('key_idx', 'cache_key'),
794 793 {'extend_existing': True, 'mysql_engine': 'InnoDB',
795 794 'mysql_charset': 'utf8'},
796 795 )
797 796 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
798 797 cache_key = Column("cache_key", String(255), nullable=True, unique=None, default=None)
799 798 cache_args = Column("cache_args", String(255), nullable=True, unique=None, default=None)
800 799 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
801 800
802 801 def __init__(self, cache_key, cache_args=''):
803 802 self.cache_key = cache_key
804 803 self.cache_args = cache_args
805 804 self.cache_active = False
806 805
807 806
808 807 class ChangesetComment(Base, BaseModel):
809 808 __tablename__ = 'changeset_comments'
810 809 __table_args__ = (
811 810 Index('cc_revision_idx', 'revision'),
812 811 {'extend_existing': True, 'mysql_engine': 'InnoDB',
813 812 'mysql_charset': 'utf8'},
814 813 )
815 814 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
816 815 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
817 816 revision = Column('revision', String(40), nullable=True)
818 817 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
819 818 line_no = Column('line_no', Unicode(10), nullable=True)
820 819 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
821 820 f_path = Column('f_path', Unicode(1000), nullable=True)
822 821 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
823 822 text = Column('text', UnicodeText().with_variant(UnicodeText(25000), 'mysql'), nullable=False)
824 823 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
825 824 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
826 825
827 826 author = relationship('User', lazy='joined')
828 827 repo = relationship('Repository')
829 828 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
830 829 pull_request = relationship('PullRequest', lazy='joined')
831 830
832 831 @classmethod
833 832 def get_users(cls, revision=None, pull_request_id=None):
834 833 """
835 834 Returns user associated with this ChangesetComment. ie those
836 835 who actually commented
837 836
838 837 :param cls:
839 838 :param revision:
840 839 """
841 840 q = Session().query(User)\
842 841 .join(ChangesetComment.author)
843 842 if revision:
844 843 q = q.filter(cls.revision == revision)
845 844 elif pull_request_id:
846 845 q = q.filter(cls.pull_request_id == pull_request_id)
847 846 return q.all()
848 847
849 848
850 849 class ChangesetStatus(Base, BaseModel):
851 850 __tablename__ = 'changeset_statuses'
852 851 __table_args__ = (
853 852 Index('cs_revision_idx', 'revision'),
854 853 Index('cs_version_idx', 'version'),
855 854 UniqueConstraint('repo_id', 'revision', 'version'),
856 855 {'extend_existing': True, 'mysql_engine': 'InnoDB',
857 856 'mysql_charset': 'utf8'}
858 857 )
859 858 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
860 859 STATUS_APPROVED = 'approved'
861 860 STATUS_REJECTED = 'rejected'
862 861 STATUS_UNDER_REVIEW = 'under_review'
863 862
864 863 STATUSES = [
865 864 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
866 865 (STATUS_APPROVED, _("Approved")),
867 866 (STATUS_REJECTED, _("Rejected")),
868 867 (STATUS_UNDER_REVIEW, _("Under Review")),
869 868 ]
870 869
871 870 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
872 871 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
873 872 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
874 873 revision = Column('revision', String(40), nullable=False)
875 874 status = Column('status', String(128), nullable=False, default=DEFAULT)
876 875 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
877 876 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
878 877 version = Column('version', Integer(), nullable=False, default=0)
879 878 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
880 879
881 880 author = relationship('User', lazy='joined')
882 881 repo = relationship('Repository')
883 882 comment = relationship('ChangesetComment', lazy='joined')
884 883 pull_request = relationship('PullRequest', lazy='joined')
885 884
886 885
887 886
888 887 class PullRequest(Base, BaseModel):
889 888 __tablename__ = 'pull_requests'
890 889 __table_args__ = (
891 890 {'extend_existing': True, 'mysql_engine': 'InnoDB',
892 891 'mysql_charset': 'utf8'},
893 892 )
894 893
895 894 STATUS_NEW = u'new'
896 895 STATUS_OPEN = u'open'
897 896 STATUS_CLOSED = u'closed'
898 897
899 898 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
900 899 title = Column('title', Unicode(256), nullable=True)
901 900 description = Column('description', UnicodeText().with_variant(UnicodeText(10240), 'mysql'), nullable=True)
902 901 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
903 902 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
904 903 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
905 904 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
906 905 _revisions = Column('revisions', UnicodeText().with_variant(UnicodeText(20500), 'mysql'))
907 906 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
908 907 org_ref = Column('org_ref', Unicode(256), nullable=False)
909 908 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
910 909 other_ref = Column('other_ref', Unicode(256), nullable=False)
911 910
912 911 author = relationship('User', lazy='joined')
913 912 reviewers = relationship('PullRequestReviewers',
914 913 cascade="all, delete, delete-orphan")
915 914 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
916 915 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
917 916 statuses = relationship('ChangesetStatus')
918 917 comments = relationship('ChangesetComment',
919 918 cascade="all, delete, delete-orphan")
920 919
921 920
922 921 class PullRequestReviewers(Base, BaseModel):
923 922 __tablename__ = 'pull_request_reviewers'
924 923 __table_args__ = (
925 924 {'extend_existing': True, 'mysql_engine': 'InnoDB',
926 925 'mysql_charset': 'utf8'},
927 926 )
928 927
929 928 def __init__(self, user=None, pull_request=None):
930 929 self.user = user
931 930 self.pull_request = pull_request
932 931
933 932 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
934 933 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
935 934 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
936 935
937 936 user = relationship('User')
938 937 pull_request = relationship('PullRequest')
939 938
940 939
941 940 class Notification(Base, BaseModel):
942 941 __tablename__ = 'notifications'
943 942 __table_args__ = (
944 943 Index('notification_type_idx', 'type'),
945 944 {'extend_existing': True, 'mysql_engine': 'InnoDB',
946 945 'mysql_charset': 'utf8'},
947 946 )
948 947
949 948 TYPE_CHANGESET_COMMENT = u'cs_comment'
950 949 TYPE_MESSAGE = u'message'
951 950 TYPE_MENTION = u'mention'
952 951 TYPE_REGISTRATION = u'registration'
953 952 TYPE_PULL_REQUEST = u'pull_request'
954 953 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
955 954
956 955 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
957 956 subject = Column('subject', Unicode(512), nullable=True)
958 957 body = Column('body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True)
959 958 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
960 959 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
961 960 type_ = Column('type', Unicode(256))
962 961
963 962 created_by_user = relationship('User')
964 963 notifications_to_users = relationship('UserNotification', lazy='joined',
965 964 cascade="all, delete, delete-orphan")
966 965
967 966
968 967 class UserNotification(Base, BaseModel):
969 968 __tablename__ = 'user_to_notification'
970 969 __table_args__ = (
971 970 UniqueConstraint('user_id', 'notification_id'),
972 971 {'extend_existing': True, 'mysql_engine': 'InnoDB',
973 972 'mysql_charset': 'utf8'}
974 973 )
975 974 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
976 975 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
977 976 read = Column('read', Boolean, default=False)
978 977 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
979 978
980 979 user = relationship('User', lazy="joined")
981 980 notification = relationship('Notification', lazy="joined",
982 981 order_by=lambda: Notification.created_on.desc(),)
983 982
984 983
985 984 class DbMigrateVersion(Base, BaseModel):
986 985 __tablename__ = 'db_migrate_version'
987 986 __table_args__ = (
988 987 {'extend_existing': True, 'mysql_engine': 'InnoDB',
989 988 'mysql_charset': 'utf8'},
990 989 )
991 990 repository_id = Column('repository_id', String(250), primary_key=True)
992 991 repository_path = Column('repository_path', Text)
993 992 version = Column('version', Integer)
@@ -1,1002 +1,1001 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import os
22 22 import time
23 23 import logging
24 24 import datetime
25 25 import traceback
26 26 import hashlib
27 27 import collections
28 28
29 29 from sqlalchemy import *
30 30 from sqlalchemy.ext.hybrid import hybrid_property
31 31 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
32 32 from sqlalchemy.exc import DatabaseError
33 33 from beaker.cache import cache_region, region_invalidate
34 34 from webob.exc import HTTPNotFound
35 35
36 36 from rhodecode.translation import _
37 37
38 38 from rhodecode.lib.vcs import get_backend
39 39 from rhodecode.lib.vcs.utils.helpers import get_scm
40 40 from rhodecode.lib.vcs.exceptions import VCSError
41 41 from zope.cachedescriptors.property import Lazy as LazyProperty
42 42 from rhodecode.lib.vcs.backends.base import EmptyCommit
43 43
44 44 from rhodecode.lib.utils2 import str2bool, safe_str, get_commit_safe, \
45 45 safe_unicode, remove_suffix, remove_prefix
46 46 from rhodecode.lib.ext_json import json
47 47 from rhodecode.lib.caching_query import FromCache
48 48
49 49 from rhodecode.model.meta import Base, Session
50 50
51 51 URL_SEP = '/'
52 52 log = logging.getLogger(__name__)
53 53
54 54 #==============================================================================
55 55 # BASE CLASSES
56 56 #==============================================================================
57 57
58 58 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
59 59
60 60
61 61 class BaseModel(object):
62 62 """
63 63 Base Model for all classes
64 64 """
65 65
66 66 @classmethod
67 67 def _get_keys(cls):
68 68 """return column names for this model """
69 69 return class_mapper(cls).c.keys()
70 70
71 71 def get_dict(self):
72 72 """
73 73 return dict with keys and values corresponding
74 74 to this model data """
75 75
76 76 d = {}
77 77 for k in self._get_keys():
78 78 d[k] = getattr(self, k)
79 79
80 80 # also use __json__() if present to get additional fields
81 81 _json_attr = getattr(self, '__json__', None)
82 82 if _json_attr:
83 83 # update with attributes from __json__
84 84 if callable(_json_attr):
85 85 _json_attr = _json_attr()
86 86 for k, val in _json_attr.iteritems():
87 87 d[k] = val
88 88 return d
89 89
90 90 def get_appstruct(self):
91 91 """return list with keys and values tupples corresponding
92 92 to this model data """
93 93
94 94 l = []
95 95 for k in self._get_keys():
96 96 l.append((k, getattr(self, k),))
97 97 return l
98 98
99 99 def populate_obj(self, populate_dict):
100 100 """populate model with data from given populate_dict"""
101 101
102 102 for k in self._get_keys():
103 103 if k in populate_dict:
104 104 setattr(self, k, populate_dict[k])
105 105
106 106 @classmethod
107 107 def query(cls):
108 108 return Session().query(cls)
109 109
110 110 @classmethod
111 111 def get(cls, id_):
112 112 if id_:
113 113 return cls.query().get(id_)
114 114
115 115 @classmethod
116 116 def get_or_404(cls, id_):
117 117 try:
118 118 id_ = int(id_)
119 119 except (TypeError, ValueError):
120 120 raise HTTPNotFound
121 121
122 122 res = cls.query().get(id_)
123 123 if not res:
124 124 raise HTTPNotFound
125 125 return res
126 126
127 127 @classmethod
128 128 def getAll(cls):
129 129 # deprecated and left for backward compatibility
130 130 return cls.get_all()
131 131
132 132 @classmethod
133 133 def get_all(cls):
134 134 return cls.query().all()
135 135
136 136 @classmethod
137 137 def delete(cls, id_):
138 138 obj = cls.query().get(id_)
139 139 Session().delete(obj)
140 140
141 141 def __repr__(self):
142 142 if hasattr(self, '__unicode__'):
143 143 # python repr needs to return str
144 144 return safe_str(self.__unicode__())
145 145 return '<DB:%s>' % (self.__class__.__name__)
146 146
147 147
148 148 class RhodeCodeSetting(Base, BaseModel):
149 149 __tablename__ = 'rhodecode_settings'
150 150 __table_args__ = (
151 151 UniqueConstraint('app_settings_name'),
152 152 {'extend_existing': True, 'mysql_engine': 'InnoDB',
153 153 'mysql_charset': 'utf8'}
154 154 )
155 155 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
156 156 app_settings_name = Column("app_settings_name", String(255), nullable=True, unique=None, default=None)
157 157 _app_settings_value = Column("app_settings_value", String(255), nullable=True, unique=None, default=None)
158 158
159 159 def __init__(self, k='', v=''):
160 160 self.app_settings_name = k
161 161 self.app_settings_value = v
162 162
163 163 @validates('_app_settings_value')
164 164 def validate_settings_value(self, key, val):
165 165 assert type(val) == unicode
166 166 return val
167 167
168 168 @hybrid_property
169 169 def app_settings_value(self):
170 170 v = self._app_settings_value
171 171 if self.app_settings_name in ["ldap_active",
172 172 "default_repo_enable_statistics",
173 173 "default_repo_enable_locking",
174 174 "default_repo_private",
175 175 "default_repo_enable_downloads"]:
176 176 v = str2bool(v)
177 177 return v
178 178
179 179 @app_settings_value.setter
180 180 def app_settings_value(self, val):
181 181 """
182 182 Setter that will always make sure we use unicode in app_settings_value
183 183
184 184 :param val:
185 185 """
186 186 self._app_settings_value = safe_unicode(val)
187 187
188 188 def __unicode__(self):
189 189 return u"<%s('%s:%s')>" % (
190 190 self.__class__.__name__,
191 191 self.app_settings_name, self.app_settings_value
192 192 )
193 193
194 194
195 195 class RhodeCodeUi(Base, BaseModel):
196 196 __tablename__ = 'rhodecode_ui'
197 197 __table_args__ = (
198 198 UniqueConstraint('ui_key'),
199 199 {'extend_existing': True, 'mysql_engine': 'InnoDB',
200 200 'mysql_charset': 'utf8'}
201 201 )
202 202
203 203 HOOK_REPO_SIZE = 'changegroup.repo_size'
204 204 HOOK_PUSH = 'changegroup.push_logger'
205 205 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
206 206 HOOK_PULL = 'outgoing.pull_logger'
207 207 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
208 208
209 209 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
210 210 ui_section = Column("ui_section", String(255), nullable=True, unique=None, default=None)
211 211 ui_key = Column("ui_key", String(255), nullable=True, unique=None, default=None)
212 212 ui_value = Column("ui_value", String(255), nullable=True, unique=None, default=None)
213 213 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
214 214
215 215
216 216
217 217 class User(Base, BaseModel):
218 218 __tablename__ = 'users'
219 219 __table_args__ = (
220 220 UniqueConstraint('username'), UniqueConstraint('email'),
221 221 Index('u_username_idx', 'username'),
222 222 Index('u_email_idx', 'email'),
223 223 {'extend_existing': True, 'mysql_engine': 'InnoDB',
224 224 'mysql_charset': 'utf8'}
225 225 )
226 226 DEFAULT_USER = 'default'
227 227 DEFAULT_PERMISSIONS = [
228 228 'hg.register.manual_activate', 'hg.create.repository',
229 229 'hg.fork.repository', 'repository.read', 'group.read'
230 230 ]
231 231 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
232 232 username = Column("username", String(255), nullable=True, unique=None, default=None)
233 233 password = Column("password", String(255), nullable=True, unique=None, default=None)
234 234 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
235 235 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
236 236 name = Column("firstname", String(255), nullable=True, unique=None, default=None)
237 237 lastname = Column("lastname", String(255), nullable=True, unique=None, default=None)
238 238 _email = Column("email", String(255), nullable=True, unique=None, default=None)
239 239 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
240 240 ldap_dn = Column("ldap_dn", String(255), nullable=True, unique=None, default=None)
241 241 api_key = Column("api_key", String(255), nullable=True, unique=None, default=None)
242 242 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
243 243
244 244 user_log = relationship('UserLog')
245 245 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
246 246
247 247 repositories = relationship('Repository')
248 248 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
249 249 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
250 250
251 251 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
252 252 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
253 253
254 254 group_member = relationship('UserGroupMember', cascade='all')
255 255
256 256 notifications = relationship('UserNotification', cascade='all')
257 257 # notifications assigned to this user
258 258 user_created_notifications = relationship('Notification', cascade='all')
259 259 # comments created by this user
260 260 user_comments = relationship('ChangesetComment', cascade='all')
261 261 user_emails = relationship('UserEmailMap', cascade='all')
262 262
263 263 @hybrid_property
264 264 def email(self):
265 265 return self._email
266 266
267 267 @email.setter
268 268 def email(self, val):
269 269 self._email = val.lower() if val else None
270 270
271 271 @property
272 272 def firstname(self):
273 273 # alias for future
274 274 return self.name
275 275
276 276 @property
277 277 def username_and_name(self):
278 278 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
279 279
280 280 @property
281 281 def full_name(self):
282 282 return '%s %s' % (self.firstname, self.lastname)
283 283
284 284 @property
285 285 def full_contact(self):
286 286 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
287 287
288 288 @property
289 289 def short_contact(self):
290 290 return '%s %s' % (self.firstname, self.lastname)
291 291
292 292 @property
293 293 def is_admin(self):
294 294 return self.admin
295 295
296 296 @classmethod
297 297 def get_by_username(cls, username, case_insensitive=False, cache=False):
298 298 if case_insensitive:
299 299 q = cls.query().filter(cls.username.ilike(username))
300 300 else:
301 301 q = cls.query().filter(cls.username == username)
302 302
303 303 if cache:
304 304 q = q.options(FromCache(
305 305 "sql_cache_short",
306 306 "get_user_%s" % _hash_key(username)
307 307 )
308 308 )
309 309 return q.scalar()
310 310
311 311 @classmethod
312 312 def get_by_auth_token(cls, auth_token, cache=False):
313 313 q = cls.query().filter(cls.api_key == auth_token)
314 314
315 315 if cache:
316 316 q = q.options(FromCache("sql_cache_short",
317 317 "get_auth_token_%s" % auth_token))
318 318 return q.scalar()
319 319
320 320 @classmethod
321 321 def get_by_email(cls, email, case_insensitive=False, cache=False):
322 322 if case_insensitive:
323 323 q = cls.query().filter(cls.email.ilike(email))
324 324 else:
325 325 q = cls.query().filter(cls.email == email)
326 326
327 327 if cache:
328 328 q = q.options(FromCache("sql_cache_short",
329 329 "get_email_key_%s" % email))
330 330
331 331 ret = q.scalar()
332 332 if ret is None:
333 333 q = UserEmailMap.query()
334 334 # try fetching in alternate email map
335 335 if case_insensitive:
336 336 q = q.filter(UserEmailMap.email.ilike(email))
337 337 else:
338 338 q = q.filter(UserEmailMap.email == email)
339 339 q = q.options(joinedload(UserEmailMap.user))
340 340 if cache:
341 341 q = q.options(FromCache("sql_cache_short",
342 342 "get_email_map_key_%s" % email))
343 343 ret = getattr(q.scalar(), 'user', None)
344 344
345 345 return ret
346 346
347 347
348 348 class UserEmailMap(Base, BaseModel):
349 349 __tablename__ = 'user_email_map'
350 350 __table_args__ = (
351 351 Index('uem_email_idx', 'email'),
352 352 UniqueConstraint('email'),
353 353 {'extend_existing': True, 'mysql_engine': 'InnoDB',
354 354 'mysql_charset': 'utf8'}
355 355 )
356 356 __mapper_args__ = {}
357 357
358 358 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
359 359 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
360 360 _email = Column("email", String(255), nullable=True, unique=False, default=None)
361 361 user = relationship('User', lazy='joined')
362 362
363 363 @validates('_email')
364 364 def validate_email(self, key, email):
365 365 # check if this email is not main one
366 366 main_email = Session().query(User).filter(User.email == email).scalar()
367 367 if main_email is not None:
368 368 raise AttributeError('email %s is present is user table' % email)
369 369 return email
370 370
371 371 @hybrid_property
372 372 def email(self):
373 373 return self._email
374 374
375 375 @email.setter
376 376 def email(self, val):
377 377 self._email = val.lower() if val else None
378 378
379 379
380 380 class UserIpMap(Base, BaseModel):
381 381 __tablename__ = 'user_ip_map'
382 382 __table_args__ = (
383 383 UniqueConstraint('user_id', 'ip_addr'),
384 384 {'extend_existing': True, 'mysql_engine': 'InnoDB',
385 385 'mysql_charset': 'utf8'}
386 386 )
387 387 __mapper_args__ = {}
388 388
389 389 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
390 390 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
391 391 ip_addr = Column("ip_addr", String(255), nullable=True, unique=False, default=None)
392 392 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
393 393 user = relationship('User', lazy='joined')
394 394
395 395
396 396 class UserLog(Base, BaseModel):
397 397 __tablename__ = 'user_logs'
398 398 __table_args__ = (
399 399 {'extend_existing': True, 'mysql_engine': 'InnoDB',
400 400 'mysql_charset': 'utf8'},
401 401 )
402 402 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
403 403 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
404 404 username = Column("username", String(255), nullable=True, unique=None, default=None)
405 405 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
406 406 repository_name = Column("repository_name", String(255), nullable=True, unique=None, default=None)
407 407 user_ip = Column("user_ip", String(255), nullable=True, unique=None, default=None)
408 408 action = Column("action", String(1200000), nullable=True, unique=None, default=None)
409 409 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
410 410
411 411
412 412 user = relationship('User')
413 413 repository = relationship('Repository', cascade='')
414 414
415 415
416 416 class UserGroup(Base, BaseModel):
417 417 __tablename__ = 'users_groups'
418 418 __table_args__ = (
419 419 {'extend_existing': True, 'mysql_engine': 'InnoDB',
420 420 'mysql_charset': 'utf8'},
421 421 )
422 422
423 423 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
424 424 users_group_name = Column("users_group_name", String(255), nullable=False, unique=True, default=None)
425 425 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
426 426 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
427 427
428 428 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
429 429 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
430 430 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
431 431
432 432 def __unicode__(self):
433 433 return u'<userGroup(%s)>' % (self.users_group_name)
434 434
435 435 @classmethod
436 436 def get_by_group_name(cls, group_name, cache=False,
437 437 case_insensitive=False):
438 438 if case_insensitive:
439 439 q = cls.query().filter(cls.users_group_name.ilike(group_name))
440 440 else:
441 441 q = cls.query().filter(cls.users_group_name == group_name)
442 442 if cache:
443 443 q = q.options(FromCache(
444 444 "sql_cache_short",
445 445 "get_user_%s" % _hash_key(group_name)
446 446 )
447 447 )
448 448 return q.scalar()
449 449
450 450 @classmethod
451 451 def get(cls, users_group_id, cache=False):
452 452 user_group = cls.query()
453 453 if cache:
454 454 user_group = user_group.options(FromCache("sql_cache_short",
455 455 "get_users_group_%s" % users_group_id))
456 456 return user_group.get(users_group_id)
457 457
458 458
459 459 class UserGroupMember(Base, BaseModel):
460 460 __tablename__ = 'users_groups_members'
461 461 __table_args__ = (
462 462 {'extend_existing': True, 'mysql_engine': 'InnoDB',
463 463 'mysql_charset': 'utf8'},
464 464 )
465 465
466 466 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
467 467 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
468 468 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
469 469
470 470 user = relationship('User', lazy='joined')
471 471 users_group = relationship('UserGroup')
472 472
473 473 def __init__(self, gr_id='', u_id=''):
474 474 self.users_group_id = gr_id
475 475 self.user_id = u_id
476 476
477 477
478 478 class Repository(Base, BaseModel):
479 479 __tablename__ = 'repositories'
480 480 __table_args__ = (
481 481 UniqueConstraint('repo_name'),
482 482 Index('r_repo_name_idx', 'repo_name'),
483 483 {'extend_existing': True, 'mysql_engine': 'InnoDB',
484 484 'mysql_charset': 'utf8'},
485 485 )
486 486
487 487 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
488 488 repo_name = Column("repo_name", String(255), nullable=False, unique=True, default=None)
489 489 clone_uri = Column("clone_uri", String(255), nullable=True, unique=False, default=None)
490 490 repo_type = Column("repo_type", String(255), nullable=False, unique=False, default=None)
491 491 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
492 492 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
493 493 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
494 494 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
495 495 description = Column("description", String(10000), nullable=True, unique=None, default=None)
496 496 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
497 497 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
498 498 landing_rev = Column("landing_revision", String(255), nullable=False, unique=False, default=None)
499 499 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
500 500 _locked = Column("locked", String(255), nullable=True, unique=False, default=None)
501 501 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
502 502
503 503 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
504 504 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
505 505
506 506 user = relationship('User')
507 507 fork = relationship('Repository', remote_side=repo_id)
508 508 group = relationship('RepoGroup')
509 509 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
510 510 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
511 511 stats = relationship('Statistics', cascade='all', uselist=False)
512 512
513 513 followers = relationship('UserFollowing',
514 514 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
515 515 cascade='all')
516 516
517 517 logs = relationship('UserLog')
518 518 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
519 519
520 520 pull_requests_org = relationship('PullRequest',
521 521 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
522 522 cascade="all, delete, delete-orphan")
523 523
524 524 pull_requests_other = relationship('PullRequest',
525 525 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
526 526 cascade="all, delete, delete-orphan")
527 527
528 528 def __unicode__(self):
529 529 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
530 530 safe_unicode(self.repo_name))
531 531
532 532
533 533 @classmethod
534 534 def get_by_repo_name(cls, repo_name):
535 535 q = Session().query(cls).filter(cls.repo_name == repo_name)
536 536 q = q.options(joinedload(Repository.fork))\
537 537 .options(joinedload(Repository.user))\
538 538 .options(joinedload(Repository.group))
539 539 return q.scalar()
540 540
541 541
542 542 class RepoGroup(Base, BaseModel):
543 543 __tablename__ = 'groups'
544 544 __table_args__ = (
545 545 UniqueConstraint('group_name', 'group_parent_id'),
546 CheckConstraint('group_id != group_parent_id'),
547 546 {'extend_existing': True, 'mysql_engine': 'InnoDB',
548 547 'mysql_charset': 'utf8'},
549 548 )
550 549 __mapper_args__ = {'order_by': 'group_name'}
551 550
552 551 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
553 552 group_name = Column("group_name", String(255), nullable=False, unique=True, default=None)
554 553 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
555 554 group_description = Column("group_description", String(10000), nullable=True, unique=None, default=None)
556 555 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
557 556
558 557 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
559 558 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
560 559 parent_group = relationship('RepoGroup', remote_side=group_id)
561 560
562 561 def __init__(self, group_name='', parent_group=None):
563 562 self.group_name = group_name
564 563 self.parent_group = parent_group
565 564
566 565 def __unicode__(self):
567 566 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
568 567 self.group_name)
569 568
570 569 @classmethod
571 570 def url_sep(cls):
572 571 return URL_SEP
573 572
574 573 @classmethod
575 574 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
576 575 if case_insensitive:
577 576 gr = cls.query()\
578 577 .filter(cls.group_name.ilike(group_name))
579 578 else:
580 579 gr = cls.query()\
581 580 .filter(cls.group_name == group_name)
582 581 if cache:
583 582 gr = gr.options(FromCache(
584 583 "sql_cache_short",
585 584 "get_group_%s" % _hash_key(group_name)
586 585 )
587 586 )
588 587 return gr.scalar()
589 588
590 589
591 590 class Permission(Base, BaseModel):
592 591 __tablename__ = 'permissions'
593 592 __table_args__ = (
594 593 Index('p_perm_name_idx', 'permission_name'),
595 594 {'extend_existing': True, 'mysql_engine': 'InnoDB',
596 595 'mysql_charset': 'utf8'},
597 596 )
598 597 PERMS = [
599 598 ('repository.none', _('Repository no access')),
600 599 ('repository.read', _('Repository read access')),
601 600 ('repository.write', _('Repository write access')),
602 601 ('repository.admin', _('Repository admin access')),
603 602
604 603 ('group.none', _('Repositories Group no access')),
605 604 ('group.read', _('Repositories Group read access')),
606 605 ('group.write', _('Repositories Group write access')),
607 606 ('group.admin', _('Repositories Group admin access')),
608 607
609 608 ('hg.admin', _('RhodeCode Administrator')),
610 609 ('hg.create.none', _('Repository creation disabled')),
611 610 ('hg.create.repository', _('Repository creation enabled')),
612 611 ('hg.fork.none', _('Repository forking disabled')),
613 612 ('hg.fork.repository', _('Repository forking enabled')),
614 613 ('hg.register.none', _('Register disabled')),
615 614 ('hg.register.manual_activate', _('Register new user with RhodeCode '
616 615 'with manual activation')),
617 616
618 617 ('hg.register.auto_activate', _('Register new user with RhodeCode '
619 618 'with auto activation')),
620 619 ]
621 620
622 621 # defines which permissions are more important higher the more important
623 622 PERM_WEIGHTS = {
624 623 'repository.none': 0,
625 624 'repository.read': 1,
626 625 'repository.write': 3,
627 626 'repository.admin': 4,
628 627
629 628 'group.none': 0,
630 629 'group.read': 1,
631 630 'group.write': 3,
632 631 'group.admin': 4,
633 632
634 633 'hg.fork.none': 0,
635 634 'hg.fork.repository': 1,
636 635 'hg.create.none': 0,
637 636 'hg.create.repository':1
638 637 }
639 638
640 639 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
641 640 permission_name = Column("permission_name", String(255), nullable=True, unique=None, default=None)
642 641 permission_longname = Column("permission_longname", String(255), nullable=True, unique=None, default=None)
643 642
644 643 def __unicode__(self):
645 644 return u"<%s('%s:%s')>" % (
646 645 self.__class__.__name__, self.permission_id, self.permission_name
647 646 )
648 647
649 648 @classmethod
650 649 def get_by_key(cls, key):
651 650 return cls.query().filter(cls.permission_name == key).scalar()
652 651
653 652
654 653 class UserRepoToPerm(Base, BaseModel):
655 654 __tablename__ = 'repo_to_perm'
656 655 __table_args__ = (
657 656 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
658 657 {'extend_existing': True, 'mysql_engine': 'InnoDB',
659 658 'mysql_charset': 'utf8'}
660 659 )
661 660 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
662 661 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
663 662 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
664 663 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
665 664
666 665 user = relationship('User')
667 666 repository = relationship('Repository')
668 667 permission = relationship('Permission')
669 668
670 669 def __unicode__(self):
671 670 return u'<user:%s => %s >' % (self.user, self.repository)
672 671
673 672
674 673 class UserToPerm(Base, BaseModel):
675 674 __tablename__ = 'user_to_perm'
676 675 __table_args__ = (
677 676 UniqueConstraint('user_id', 'permission_id'),
678 677 {'extend_existing': True, 'mysql_engine': 'InnoDB',
679 678 'mysql_charset': 'utf8'}
680 679 )
681 680 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
682 681 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
683 682 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
684 683
685 684 user = relationship('User')
686 685 permission = relationship('Permission', lazy='joined')
687 686
688 687
689 688 class UserGroupRepoToPerm(Base, BaseModel):
690 689 __tablename__ = 'users_group_repo_to_perm'
691 690 __table_args__ = (
692 691 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
693 692 {'extend_existing': True, 'mysql_engine': 'InnoDB',
694 693 'mysql_charset': 'utf8'}
695 694 )
696 695 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
697 696 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
698 697 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
699 698 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
700 699
701 700 users_group = relationship('UserGroup')
702 701 permission = relationship('Permission')
703 702 repository = relationship('Repository')
704 703
705 704 def __unicode__(self):
706 705 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
707 706
708 707
709 708 class UserGroupToPerm(Base, BaseModel):
710 709 __tablename__ = 'users_group_to_perm'
711 710 __table_args__ = (
712 711 UniqueConstraint('users_group_id', 'permission_id',),
713 712 {'extend_existing': True, 'mysql_engine': 'InnoDB',
714 713 'mysql_charset': 'utf8'}
715 714 )
716 715 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
717 716 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
718 717 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
719 718
720 719 users_group = relationship('UserGroup')
721 720 permission = relationship('Permission')
722 721
723 722
724 723 class UserRepoGroupToPerm(Base, BaseModel):
725 724 __tablename__ = 'user_repo_group_to_perm'
726 725 __table_args__ = (
727 726 UniqueConstraint('user_id', 'group_id', 'permission_id'),
728 727 {'extend_existing': True, 'mysql_engine': 'InnoDB',
729 728 'mysql_charset': 'utf8'}
730 729 )
731 730
732 731 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
733 732 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
734 733 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
735 734 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
736 735
737 736 user = relationship('User')
738 737 group = relationship('RepoGroup')
739 738 permission = relationship('Permission')
740 739
741 740
742 741 class UserGroupRepoGroupToPerm(Base, BaseModel):
743 742 __tablename__ = 'users_group_repo_group_to_perm'
744 743 __table_args__ = (
745 744 UniqueConstraint('users_group_id', 'group_id'),
746 745 {'extend_existing': True, 'mysql_engine': 'InnoDB',
747 746 'mysql_charset': 'utf8'}
748 747 )
749 748
750 749 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)
751 750 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
752 751 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
753 752 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
754 753
755 754 users_group = relationship('UserGroup')
756 755 permission = relationship('Permission')
757 756 group = relationship('RepoGroup')
758 757
759 758
760 759 class Statistics(Base, BaseModel):
761 760 __tablename__ = 'statistics'
762 761 __table_args__ = (
763 762 UniqueConstraint('repository_id'),
764 763 {'extend_existing': True, 'mysql_engine': 'InnoDB',
765 764 'mysql_charset': 'utf8'}
766 765 )
767 766 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
768 767 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
769 768 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
770 769 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
771 770 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
772 771 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
773 772
774 773 repository = relationship('Repository', single_parent=True)
775 774
776 775
777 776 class UserFollowing(Base, BaseModel):
778 777 __tablename__ = 'user_followings'
779 778 __table_args__ = (
780 779 UniqueConstraint('user_id', 'follows_repository_id'),
781 780 UniqueConstraint('user_id', 'follows_user_id'),
782 781 {'extend_existing': True, 'mysql_engine': 'InnoDB',
783 782 'mysql_charset': 'utf8'}
784 783 )
785 784
786 785 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
787 786 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
788 787 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
789 788 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
790 789 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
791 790
792 791 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
793 792
794 793 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
795 794 follows_repository = relationship('Repository', order_by='Repository.repo_name')
796 795
797 796
798 797 class CacheInvalidation(Base, BaseModel):
799 798 __tablename__ = 'cache_invalidation'
800 799 __table_args__ = (
801 800 UniqueConstraint('cache_key'),
802 801 Index('key_idx', 'cache_key'),
803 802 {'extend_existing': True, 'mysql_engine': 'InnoDB',
804 803 'mysql_charset': 'utf8'},
805 804 )
806 805 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
807 806 cache_key = Column("cache_key", String(255), nullable=True, unique=None, default=None)
808 807 cache_args = Column("cache_args", String(255), nullable=True, unique=None, default=None)
809 808 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
810 809
811 810 def __init__(self, cache_key, cache_args=''):
812 811 self.cache_key = cache_key
813 812 self.cache_args = cache_args
814 813 self.cache_active = False
815 814
816 815
817 816 class ChangesetComment(Base, BaseModel):
818 817 __tablename__ = 'changeset_comments'
819 818 __table_args__ = (
820 819 Index('cc_revision_idx', 'revision'),
821 820 {'extend_existing': True, 'mysql_engine': 'InnoDB',
822 821 'mysql_charset': 'utf8'},
823 822 )
824 823 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
825 824 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
826 825 revision = Column('revision', String(40), nullable=True)
827 826 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
828 827 line_no = Column('line_no', Unicode(10), nullable=True)
829 828 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
830 829 f_path = Column('f_path', Unicode(1000), nullable=True)
831 830 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
832 831 text = Column('text', UnicodeText().with_variant(UnicodeText(25000), 'mysql'), nullable=False)
833 832 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
834 833 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
835 834
836 835 author = relationship('User', lazy='joined')
837 836 repo = relationship('Repository')
838 837 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
839 838 pull_request = relationship('PullRequest', lazy='joined')
840 839
841 840 @classmethod
842 841 def get_users(cls, revision=None, pull_request_id=None):
843 842 """
844 843 Returns user associated with this ChangesetComment. ie those
845 844 who actually commented
846 845
847 846 :param cls:
848 847 :param revision:
849 848 """
850 849 q = Session().query(User)\
851 850 .join(ChangesetComment.author)
852 851 if revision:
853 852 q = q.filter(cls.revision == revision)
854 853 elif pull_request_id:
855 854 q = q.filter(cls.pull_request_id == pull_request_id)
856 855 return q.all()
857 856
858 857
859 858 class ChangesetStatus(Base, BaseModel):
860 859 __tablename__ = 'changeset_statuses'
861 860 __table_args__ = (
862 861 Index('cs_revision_idx', 'revision'),
863 862 Index('cs_version_idx', 'version'),
864 863 UniqueConstraint('repo_id', 'revision', 'version'),
865 864 {'extend_existing': True, 'mysql_engine': 'InnoDB',
866 865 'mysql_charset': 'utf8'}
867 866 )
868 867 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
869 868 STATUS_APPROVED = 'approved'
870 869 STATUS_REJECTED = 'rejected'
871 870 STATUS_UNDER_REVIEW = 'under_review'
872 871
873 872 STATUSES = [
874 873 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
875 874 (STATUS_APPROVED, _("Approved")),
876 875 (STATUS_REJECTED, _("Rejected")),
877 876 (STATUS_UNDER_REVIEW, _("Under Review")),
878 877 ]
879 878
880 879 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
881 880 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
882 881 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
883 882 revision = Column('revision', String(40), nullable=False)
884 883 status = Column('status', String(128), nullable=False, default=DEFAULT)
885 884 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
886 885 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
887 886 version = Column('version', Integer(), nullable=False, default=0)
888 887 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
889 888
890 889 author = relationship('User', lazy='joined')
891 890 repo = relationship('Repository')
892 891 comment = relationship('ChangesetComment', lazy='joined')
893 892 pull_request = relationship('PullRequest', lazy='joined')
894 893
895 894
896 895
897 896 class PullRequest(Base, BaseModel):
898 897 __tablename__ = 'pull_requests'
899 898 __table_args__ = (
900 899 {'extend_existing': True, 'mysql_engine': 'InnoDB',
901 900 'mysql_charset': 'utf8'},
902 901 )
903 902
904 903 STATUS_NEW = u'new'
905 904 STATUS_OPEN = u'open'
906 905 STATUS_CLOSED = u'closed'
907 906
908 907 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
909 908 title = Column('title', Unicode(256), nullable=True)
910 909 description = Column('description', UnicodeText().with_variant(UnicodeText(10240), 'mysql'), nullable=True)
911 910 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
912 911 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
913 912 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
914 913 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
915 914 _revisions = Column('revisions', UnicodeText().with_variant(UnicodeText(20500), 'mysql'))
916 915 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
917 916 org_ref = Column('org_ref', Unicode(256), nullable=False)
918 917 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
919 918 other_ref = Column('other_ref', Unicode(256), nullable=False)
920 919
921 920 author = relationship('User', lazy='joined')
922 921 reviewers = relationship('PullRequestReviewers',
923 922 cascade="all, delete, delete-orphan")
924 923 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
925 924 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
926 925 statuses = relationship('ChangesetStatus')
927 926 comments = relationship('ChangesetComment',
928 927 cascade="all, delete, delete-orphan")
929 928
930 929
931 930 class PullRequestReviewers(Base, BaseModel):
932 931 __tablename__ = 'pull_request_reviewers'
933 932 __table_args__ = (
934 933 {'extend_existing': True, 'mysql_engine': 'InnoDB',
935 934 'mysql_charset': 'utf8'},
936 935 )
937 936
938 937 def __init__(self, user=None, pull_request=None):
939 938 self.user = user
940 939 self.pull_request = pull_request
941 940
942 941 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
943 942 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
944 943 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
945 944
946 945 user = relationship('User')
947 946 pull_request = relationship('PullRequest')
948 947
949 948
950 949 class Notification(Base, BaseModel):
951 950 __tablename__ = 'notifications'
952 951 __table_args__ = (
953 952 Index('notification_type_idx', 'type'),
954 953 {'extend_existing': True, 'mysql_engine': 'InnoDB',
955 954 'mysql_charset': 'utf8'},
956 955 )
957 956
958 957 TYPE_CHANGESET_COMMENT = u'cs_comment'
959 958 TYPE_MESSAGE = u'message'
960 959 TYPE_MENTION = u'mention'
961 960 TYPE_REGISTRATION = u'registration'
962 961 TYPE_PULL_REQUEST = u'pull_request'
963 962 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
964 963
965 964 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
966 965 subject = Column('subject', Unicode(512), nullable=True)
967 966 body = Column('body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True)
968 967 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
969 968 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
970 969 type_ = Column('type', Unicode(256))
971 970
972 971 created_by_user = relationship('User')
973 972 notifications_to_users = relationship('UserNotification', lazy='joined',
974 973 cascade="all, delete, delete-orphan")
975 974
976 975
977 976 class UserNotification(Base, BaseModel):
978 977 __tablename__ = 'user_to_notification'
979 978 __table_args__ = (
980 979 UniqueConstraint('user_id', 'notification_id'),
981 980 {'extend_existing': True, 'mysql_engine': 'InnoDB',
982 981 'mysql_charset': 'utf8'}
983 982 )
984 983 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
985 984 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
986 985 read = Column('read', Boolean, default=False)
987 986 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
988 987
989 988 user = relationship('User', lazy="joined")
990 989 notification = relationship('Notification', lazy="joined",
991 990 order_by=lambda: Notification.created_on.desc(),)
992 991
993 992
994 993 class DbMigrateVersion(Base, BaseModel):
995 994 __tablename__ = 'db_migrate_version'
996 995 __table_args__ = (
997 996 {'extend_existing': True, 'mysql_engine': 'InnoDB',
998 997 'mysql_charset': 'utf8'},
999 998 )
1000 999 repository_id = Column('repository_id', String(250), primary_key=True)
1001 1000 repository_path = Column('repository_path', Text)
1002 1001 version = Column('version', Integer)
@@ -1,1085 +1,1084 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import os
22 22 import time
23 23 import logging
24 24 import datetime
25 25 import traceback
26 26 import hashlib
27 27 import collections
28 28
29 29 from sqlalchemy import *
30 30 from sqlalchemy.ext.hybrid import hybrid_property
31 31 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
32 32 from sqlalchemy.exc import DatabaseError
33 33 from beaker.cache import cache_region, region_invalidate
34 34 from webob.exc import HTTPNotFound
35 35
36 36 from rhodecode.translation import _
37 37
38 38 from rhodecode.lib.vcs import get_backend
39 39 from rhodecode.lib.vcs.utils.helpers import get_scm
40 40 from rhodecode.lib.vcs.exceptions import VCSError
41 41 from zope.cachedescriptors.property import Lazy as LazyProperty
42 42 from rhodecode.lib.vcs.backends.base import EmptyCommit
43 43
44 44 from rhodecode.lib.utils2 import str2bool, safe_str, get_commit_safe, \
45 45 safe_unicode, remove_suffix, remove_prefix, time_to_datetime
46 46 from rhodecode.lib.ext_json import json
47 47 from rhodecode.lib.caching_query import FromCache
48 48
49 49 from rhodecode.model.meta import Base, Session
50 50
51 51 URL_SEP = '/'
52 52 log = logging.getLogger(__name__)
53 53
54 54 #==============================================================================
55 55 # BASE CLASSES
56 56 #==============================================================================
57 57
58 58 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
59 59
60 60
61 61 class BaseModel(object):
62 62 """
63 63 Base Model for all classes
64 64 """
65 65
66 66 @classmethod
67 67 def _get_keys(cls):
68 68 """return column names for this model """
69 69 return class_mapper(cls).c.keys()
70 70
71 71 def get_dict(self):
72 72 """
73 73 return dict with keys and values corresponding
74 74 to this model data """
75 75
76 76 d = {}
77 77 for k in self._get_keys():
78 78 d[k] = getattr(self, k)
79 79
80 80 # also use __json__() if present to get additional fields
81 81 _json_attr = getattr(self, '__json__', None)
82 82 if _json_attr:
83 83 # update with attributes from __json__
84 84 if callable(_json_attr):
85 85 _json_attr = _json_attr()
86 86 for k, val in _json_attr.iteritems():
87 87 d[k] = val
88 88 return d
89 89
90 90 def get_appstruct(self):
91 91 """return list with keys and values tupples corresponding
92 92 to this model data """
93 93
94 94 l = []
95 95 for k in self._get_keys():
96 96 l.append((k, getattr(self, k),))
97 97 return l
98 98
99 99 def populate_obj(self, populate_dict):
100 100 """populate model with data from given populate_dict"""
101 101
102 102 for k in self._get_keys():
103 103 if k in populate_dict:
104 104 setattr(self, k, populate_dict[k])
105 105
106 106 @classmethod
107 107 def query(cls):
108 108 return Session().query(cls)
109 109
110 110 @classmethod
111 111 def get(cls, id_):
112 112 if id_:
113 113 return cls.query().get(id_)
114 114
115 115 @classmethod
116 116 def get_or_404(cls, id_):
117 117 try:
118 118 id_ = int(id_)
119 119 except (TypeError, ValueError):
120 120 raise HTTPNotFound
121 121
122 122 res = cls.query().get(id_)
123 123 if not res:
124 124 raise HTTPNotFound
125 125 return res
126 126
127 127 @classmethod
128 128 def getAll(cls):
129 129 # deprecated and left for backward compatibility
130 130 return cls.get_all()
131 131
132 132 @classmethod
133 133 def get_all(cls):
134 134 return cls.query().all()
135 135
136 136 @classmethod
137 137 def delete(cls, id_):
138 138 obj = cls.query().get(id_)
139 139 Session().delete(obj)
140 140
141 141 def __repr__(self):
142 142 if hasattr(self, '__unicode__'):
143 143 # python repr needs to return str
144 144 return safe_str(self.__unicode__())
145 145 return '<DB:%s>' % (self.__class__.__name__)
146 146
147 147
148 148 class RhodeCodeSetting(Base, BaseModel):
149 149 __tablename__ = 'rhodecode_settings'
150 150 __table_args__ = (
151 151 UniqueConstraint('app_settings_name'),
152 152 {'extend_existing': True, 'mysql_engine': 'InnoDB',
153 153 'mysql_charset': 'utf8'}
154 154 )
155 155 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
156 156 app_settings_name = Column("app_settings_name", String(255), nullable=True, unique=None, default=None)
157 157 _app_settings_value = Column("app_settings_value", String(255), nullable=True, unique=None, default=None)
158 158
159 159 def __init__(self, k='', v=''):
160 160 self.app_settings_name = k
161 161 self.app_settings_value = v
162 162
163 163 @validates('_app_settings_value')
164 164 def validate_settings_value(self, key, val):
165 165 assert type(val) == unicode
166 166 return val
167 167
168 168 @hybrid_property
169 169 def app_settings_value(self):
170 170 v = self._app_settings_value
171 171 if self.app_settings_name in ["ldap_active",
172 172 "default_repo_enable_statistics",
173 173 "default_repo_enable_locking",
174 174 "default_repo_private",
175 175 "default_repo_enable_downloads"]:
176 176 v = str2bool(v)
177 177 return v
178 178
179 179 @app_settings_value.setter
180 180 def app_settings_value(self, val):
181 181 """
182 182 Setter that will always make sure we use unicode in app_settings_value
183 183
184 184 :param val:
185 185 """
186 186 self._app_settings_value = safe_unicode(val)
187 187
188 188 def __unicode__(self):
189 189 return u"<%s('%s:%s')>" % (
190 190 self.__class__.__name__,
191 191 self.app_settings_name, self.app_settings_value
192 192 )
193 193
194 194
195 195 class RhodeCodeUi(Base, BaseModel):
196 196 __tablename__ = 'rhodecode_ui'
197 197 __table_args__ = (
198 198 UniqueConstraint('ui_key'),
199 199 {'extend_existing': True, 'mysql_engine': 'InnoDB',
200 200 'mysql_charset': 'utf8'}
201 201 )
202 202
203 203 HOOK_REPO_SIZE = 'changegroup.repo_size'
204 204 HOOK_PUSH = 'changegroup.push_logger'
205 205 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
206 206 HOOK_PULL = 'outgoing.pull_logger'
207 207 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
208 208
209 209 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
210 210 ui_section = Column("ui_section", String(255), nullable=True, unique=None, default=None)
211 211 ui_key = Column("ui_key", String(255), nullable=True, unique=None, default=None)
212 212 ui_value = Column("ui_value", String(255), nullable=True, unique=None, default=None)
213 213 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
214 214
215 215
216 216
217 217 class User(Base, BaseModel):
218 218 __tablename__ = 'users'
219 219 __table_args__ = (
220 220 UniqueConstraint('username'), UniqueConstraint('email'),
221 221 Index('u_username_idx', 'username'),
222 222 Index('u_email_idx', 'email'),
223 223 {'extend_existing': True, 'mysql_engine': 'InnoDB',
224 224 'mysql_charset': 'utf8'}
225 225 )
226 226 DEFAULT_USER = 'default'
227 227 DEFAULT_PERMISSIONS = [
228 228 'hg.register.manual_activate', 'hg.create.repository',
229 229 'hg.fork.repository', 'repository.read', 'group.read'
230 230 ]
231 231 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
232 232 username = Column("username", String(255), nullable=True, unique=None, default=None)
233 233 password = Column("password", String(255), nullable=True, unique=None, default=None)
234 234 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
235 235 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
236 236 name = Column("firstname", String(255), nullable=True, unique=None, default=None)
237 237 lastname = Column("lastname", String(255), nullable=True, unique=None, default=None)
238 238 _email = Column("email", String(255), nullable=True, unique=None, default=None)
239 239 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
240 240 ldap_dn = Column("ldap_dn", String(255), nullable=True, unique=None, default=None)
241 241 api_key = Column("api_key", String(255), nullable=True, unique=None, default=None)
242 242 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
243 243
244 244 user_log = relationship('UserLog')
245 245 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
246 246
247 247 repositories = relationship('Repository')
248 248 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
249 249 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
250 250
251 251 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
252 252 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
253 253
254 254 group_member = relationship('UserGroupMember', cascade='all')
255 255
256 256 notifications = relationship('UserNotification', cascade='all')
257 257 # notifications assigned to this user
258 258 user_created_notifications = relationship('Notification', cascade='all')
259 259 # comments created by this user
260 260 user_comments = relationship('ChangesetComment', cascade='all')
261 261 user_emails = relationship('UserEmailMap', cascade='all')
262 262
263 263 @hybrid_property
264 264 def email(self):
265 265 return self._email
266 266
267 267 @email.setter
268 268 def email(self, val):
269 269 self._email = val.lower() if val else None
270 270
271 271 @property
272 272 def firstname(self):
273 273 # alias for future
274 274 return self.name
275 275
276 276 @property
277 277 def username_and_name(self):
278 278 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
279 279
280 280 @property
281 281 def full_name(self):
282 282 return '%s %s' % (self.firstname, self.lastname)
283 283
284 284 @property
285 285 def full_contact(self):
286 286 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
287 287
288 288 @property
289 289 def short_contact(self):
290 290 return '%s %s' % (self.firstname, self.lastname)
291 291
292 292 @property
293 293 def is_admin(self):
294 294 return self.admin
295 295
296 296 @classmethod
297 297 def get_by_username(cls, username, case_insensitive=False, cache=False):
298 298 if case_insensitive:
299 299 q = cls.query().filter(cls.username.ilike(username))
300 300 else:
301 301 q = cls.query().filter(cls.username == username)
302 302
303 303 if cache:
304 304 q = q.options(FromCache(
305 305 "sql_cache_short",
306 306 "get_user_%s" % _hash_key(username)
307 307 )
308 308 )
309 309 return q.scalar()
310 310
311 311 @classmethod
312 312 def get_by_auth_token(cls, auth_token, cache=False):
313 313 q = cls.query().filter(cls.api_key == auth_token)
314 314
315 315 if cache:
316 316 q = q.options(FromCache("sql_cache_short",
317 317 "get_auth_token_%s" % auth_token))
318 318 return q.scalar()
319 319
320 320 @classmethod
321 321 def get_by_email(cls, email, case_insensitive=False, cache=False):
322 322 if case_insensitive:
323 323 q = cls.query().filter(cls.email.ilike(email))
324 324 else:
325 325 q = cls.query().filter(cls.email == email)
326 326
327 327 if cache:
328 328 q = q.options(FromCache("sql_cache_short",
329 329 "get_email_key_%s" % email))
330 330
331 331 ret = q.scalar()
332 332 if ret is None:
333 333 q = UserEmailMap.query()
334 334 # try fetching in alternate email map
335 335 if case_insensitive:
336 336 q = q.filter(UserEmailMap.email.ilike(email))
337 337 else:
338 338 q = q.filter(UserEmailMap.email == email)
339 339 q = q.options(joinedload(UserEmailMap.user))
340 340 if cache:
341 341 q = q.options(FromCache("sql_cache_short",
342 342 "get_email_map_key_%s" % email))
343 343 ret = getattr(q.scalar(), 'user', None)
344 344
345 345 return ret
346 346
347 347
348 348 class UserEmailMap(Base, BaseModel):
349 349 __tablename__ = 'user_email_map'
350 350 __table_args__ = (
351 351 Index('uem_email_idx', 'email'),
352 352 UniqueConstraint('email'),
353 353 {'extend_existing': True, 'mysql_engine': 'InnoDB',
354 354 'mysql_charset': 'utf8'}
355 355 )
356 356 __mapper_args__ = {}
357 357
358 358 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
359 359 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
360 360 _email = Column("email", String(255), nullable=True, unique=False, default=None)
361 361 user = relationship('User', lazy='joined')
362 362
363 363 @validates('_email')
364 364 def validate_email(self, key, email):
365 365 # check if this email is not main one
366 366 main_email = Session().query(User).filter(User.email == email).scalar()
367 367 if main_email is not None:
368 368 raise AttributeError('email %s is present is user table' % email)
369 369 return email
370 370
371 371 @hybrid_property
372 372 def email(self):
373 373 return self._email
374 374
375 375 @email.setter
376 376 def email(self, val):
377 377 self._email = val.lower() if val else None
378 378
379 379
380 380 class UserIpMap(Base, BaseModel):
381 381 __tablename__ = 'user_ip_map'
382 382 __table_args__ = (
383 383 UniqueConstraint('user_id', 'ip_addr'),
384 384 {'extend_existing': True, 'mysql_engine': 'InnoDB',
385 385 'mysql_charset': 'utf8'}
386 386 )
387 387 __mapper_args__ = {}
388 388
389 389 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
390 390 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
391 391 ip_addr = Column("ip_addr", String(255), nullable=True, unique=False, default=None)
392 392 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
393 393 user = relationship('User', lazy='joined')
394 394
395 395
396 396 class UserLog(Base, BaseModel):
397 397 __tablename__ = 'user_logs'
398 398 __table_args__ = (
399 399 {'extend_existing': True, 'mysql_engine': 'InnoDB',
400 400 'mysql_charset': 'utf8'},
401 401 )
402 402 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
403 403 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
404 404 username = Column("username", String(255), nullable=True, unique=None, default=None)
405 405 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
406 406 repository_name = Column("repository_name", String(255), nullable=True, unique=None, default=None)
407 407 user_ip = Column("user_ip", String(255), nullable=True, unique=None, default=None)
408 408 action = Column("action", String(1200000), nullable=True, unique=None, default=None)
409 409 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
410 410
411 411
412 412 user = relationship('User')
413 413 repository = relationship('Repository', cascade='')
414 414
415 415
416 416 class UserGroup(Base, BaseModel):
417 417 __tablename__ = 'users_groups'
418 418 __table_args__ = (
419 419 {'extend_existing': True, 'mysql_engine': 'InnoDB',
420 420 'mysql_charset': 'utf8'},
421 421 )
422 422
423 423 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
424 424 users_group_name = Column("users_group_name", String(255), nullable=False, unique=True, default=None)
425 425 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
426 426 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
427 427
428 428 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
429 429 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
430 430 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
431 431
432 432 def __unicode__(self):
433 433 return u'<userGroup(%s)>' % (self.users_group_name)
434 434
435 435 @classmethod
436 436 def get_by_group_name(cls, group_name, cache=False,
437 437 case_insensitive=False):
438 438 if case_insensitive:
439 439 q = cls.query().filter(cls.users_group_name.ilike(group_name))
440 440 else:
441 441 q = cls.query().filter(cls.users_group_name == group_name)
442 442 if cache:
443 443 q = q.options(FromCache(
444 444 "sql_cache_short",
445 445 "get_user_%s" % _hash_key(group_name)
446 446 )
447 447 )
448 448 return q.scalar()
449 449
450 450 @classmethod
451 451 def get(cls, users_group_id, cache=False):
452 452 user_group = cls.query()
453 453 if cache:
454 454 user_group = user_group.options(FromCache("sql_cache_short",
455 455 "get_users_group_%s" % users_group_id))
456 456 return user_group.get(users_group_id)
457 457
458 458
459 459 class UserGroupMember(Base, BaseModel):
460 460 __tablename__ = 'users_groups_members'
461 461 __table_args__ = (
462 462 {'extend_existing': True, 'mysql_engine': 'InnoDB',
463 463 'mysql_charset': 'utf8'},
464 464 )
465 465
466 466 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
467 467 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
468 468 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
469 469
470 470 user = relationship('User', lazy='joined')
471 471 users_group = relationship('UserGroup')
472 472
473 473 def __init__(self, gr_id='', u_id=''):
474 474 self.users_group_id = gr_id
475 475 self.user_id = u_id
476 476
477 477
478 478 class RepositoryField(Base, BaseModel):
479 479 __tablename__ = 'repositories_fields'
480 480 __table_args__ = (
481 481 UniqueConstraint('repository_id', 'field_key'), # no-multi field
482 482 {'extend_existing': True, 'mysql_engine': 'InnoDB',
483 483 'mysql_charset': 'utf8'},
484 484 )
485 485 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
486 486
487 487 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
488 488 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
489 489 field_key = Column("field_key", String(250))
490 490 field_label = Column("field_label", String(1024), nullable=False)
491 491 field_value = Column("field_value", String(10000), nullable=False)
492 492 field_desc = Column("field_desc", String(1024), nullable=False)
493 493 field_type = Column("field_type", String(256), nullable=False, unique=None)
494 494 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
495 495
496 496 repository = relationship('Repository')
497 497
498 498 @classmethod
499 499 def get_by_key_name(cls, key, repo):
500 500 row = cls.query()\
501 501 .filter(cls.repository == repo)\
502 502 .filter(cls.field_key == key).scalar()
503 503 return row
504 504
505 505
506 506 class Repository(Base, BaseModel):
507 507 __tablename__ = 'repositories'
508 508 __table_args__ = (
509 509 UniqueConstraint('repo_name'),
510 510 Index('r_repo_name_idx', 'repo_name'),
511 511 {'extend_existing': True, 'mysql_engine': 'InnoDB',
512 512 'mysql_charset': 'utf8'},
513 513 )
514 514
515 515 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
516 516 repo_name = Column("repo_name", String(255), nullable=False, unique=True, default=None)
517 517 clone_uri = Column("clone_uri", String(255), nullable=True, unique=False, default=None)
518 518 repo_type = Column("repo_type", String(255), nullable=False, unique=False, default=None)
519 519 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
520 520 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
521 521 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
522 522 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
523 523 description = Column("description", String(10000), nullable=True, unique=None, default=None)
524 524 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
525 525 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
526 526 landing_rev = Column("landing_revision", String(255), nullable=False, unique=False, default=None)
527 527 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
528 528 _locked = Column("locked", String(255), nullable=True, unique=False, default=None)
529 529 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
530 530
531 531 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
532 532 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
533 533
534 534 user = relationship('User')
535 535 fork = relationship('Repository', remote_side=repo_id)
536 536 group = relationship('RepoGroup')
537 537 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
538 538 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
539 539 stats = relationship('Statistics', cascade='all', uselist=False)
540 540
541 541 followers = relationship('UserFollowing',
542 542 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
543 543 cascade='all')
544 544 extra_fields = relationship('RepositoryField',
545 545 cascade="all, delete, delete-orphan")
546 546
547 547 logs = relationship('UserLog')
548 548 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
549 549
550 550 pull_requests_org = relationship('PullRequest',
551 551 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
552 552 cascade="all, delete, delete-orphan")
553 553
554 554 pull_requests_other = relationship('PullRequest',
555 555 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
556 556 cascade="all, delete, delete-orphan")
557 557
558 558 def __unicode__(self):
559 559 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
560 560 safe_unicode(self.repo_name))
561 561
562 562 #NOTE for this migration we are required tio have it
563 563 @hybrid_property
564 564 def changeset_cache(self):
565 565 from rhodecode.lib.vcs.backends.base import EmptyCommit
566 566 dummy = EmptyCommit().__json__()
567 567 if not self._changeset_cache:
568 568 return dummy
569 569 try:
570 570 return json.loads(self._changeset_cache)
571 571 except TypeError:
572 572 return dummy
573 573
574 574 @changeset_cache.setter
575 575 def changeset_cache(self, val):
576 576 try:
577 577 self._changeset_cache = json.dumps(val)
578 578 except Exception:
579 579 log.error(traceback.format_exc())
580 580
581 581 @classmethod
582 582 def get_by_repo_name(cls, repo_name):
583 583 q = Session().query(cls).filter(cls.repo_name == repo_name)
584 584 q = q.options(joinedload(Repository.fork))\
585 585 .options(joinedload(Repository.user))\
586 586 .options(joinedload(Repository.group))
587 587 return q.scalar()
588 588
589 589 #NOTE this is required for this migration to work
590 590 def update_commit_cache(self, cs_cache=None):
591 591 """
592 592 Update cache of last changeset for repository, keys should be::
593 593
594 594 short_id
595 595 raw_id
596 596 revision
597 597 message
598 598 date
599 599 author
600 600
601 601 :param cs_cache:
602 602 """
603 603 from rhodecode.lib.vcs.backends.base import BaseChangeset
604 604 if cs_cache is None:
605 605 cs_cache = EmptyCommit()
606 606 # Note: Using always the empty commit here in case we are
607 607 # upgrading towards version 3.0 and above. Reason is that in this
608 608 # case the vcsclient connection is not available and things
609 609 # would explode here.
610 610
611 611 if isinstance(cs_cache, BaseChangeset):
612 612 cs_cache = cs_cache.__json__()
613 613
614 614 if (cs_cache != self.changeset_cache or not self.changeset_cache):
615 615 _default = datetime.datetime.fromtimestamp(0)
616 616 last_change = cs_cache.get('date') or _default
617 617 log.debug('updated repo %s with new cs cache %s', self.repo_name, cs_cache)
618 618 self.updated_on = last_change
619 619 self.changeset_cache = cs_cache
620 620 Session().add(self)
621 621 Session().commit()
622 622 else:
623 623 log.debug('Skipping repo:%s already with latest changes', self.repo_name)
624 624
625 625 class RepoGroup(Base, BaseModel):
626 626 __tablename__ = 'groups'
627 627 __table_args__ = (
628 628 UniqueConstraint('group_name', 'group_parent_id'),
629 CheckConstraint('group_id != group_parent_id'),
630 629 {'extend_existing': True, 'mysql_engine': 'InnoDB',
631 630 'mysql_charset': 'utf8'},
632 631 )
633 632 __mapper_args__ = {'order_by': 'group_name'}
634 633
635 634 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
636 635 group_name = Column("group_name", String(255), nullable=False, unique=True, default=None)
637 636 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
638 637 group_description = Column("group_description", String(10000), nullable=True, unique=None, default=None)
639 638 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
640 639
641 640 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
642 641 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
643 642 parent_group = relationship('RepoGroup', remote_side=group_id)
644 643
645 644 def __init__(self, group_name='', parent_group=None):
646 645 self.group_name = group_name
647 646 self.parent_group = parent_group
648 647
649 648 def __unicode__(self):
650 649 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
651 650 self.group_name)
652 651
653 652 @classmethod
654 653 def url_sep(cls):
655 654 return URL_SEP
656 655
657 656 @classmethod
658 657 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
659 658 if case_insensitive:
660 659 gr = cls.query()\
661 660 .filter(cls.group_name.ilike(group_name))
662 661 else:
663 662 gr = cls.query()\
664 663 .filter(cls.group_name == group_name)
665 664 if cache:
666 665 gr = gr.options(FromCache(
667 666 "sql_cache_short",
668 667 "get_group_%s" % _hash_key(group_name)
669 668 )
670 669 )
671 670 return gr.scalar()
672 671
673 672
674 673 class Permission(Base, BaseModel):
675 674 __tablename__ = 'permissions'
676 675 __table_args__ = (
677 676 Index('p_perm_name_idx', 'permission_name'),
678 677 {'extend_existing': True, 'mysql_engine': 'InnoDB',
679 678 'mysql_charset': 'utf8'},
680 679 )
681 680 PERMS = [
682 681 ('repository.none', _('Repository no access')),
683 682 ('repository.read', _('Repository read access')),
684 683 ('repository.write', _('Repository write access')),
685 684 ('repository.admin', _('Repository admin access')),
686 685
687 686 ('group.none', _('Repository group no access')),
688 687 ('group.read', _('Repository group read access')),
689 688 ('group.write', _('Repository group write access')),
690 689 ('group.admin', _('Repository group admin access')),
691 690
692 691 ('hg.admin', _('RhodeCode Administrator')),
693 692 ('hg.create.none', _('Repository creation disabled')),
694 693 ('hg.create.repository', _('Repository creation enabled')),
695 694 ('hg.fork.none', _('Repository forking disabled')),
696 695 ('hg.fork.repository', _('Repository forking enabled')),
697 696 ('hg.register.none', _('Register disabled')),
698 697 ('hg.register.manual_activate', _('Register new user with RhodeCode '
699 698 'with manual activation')),
700 699
701 700 ('hg.register.auto_activate', _('Register new user with RhodeCode '
702 701 'with auto activation')),
703 702 ]
704 703
705 704 # defines which permissions are more important higher the more important
706 705 PERM_WEIGHTS = {
707 706 'repository.none': 0,
708 707 'repository.read': 1,
709 708 'repository.write': 3,
710 709 'repository.admin': 4,
711 710
712 711 'group.none': 0,
713 712 'group.read': 1,
714 713 'group.write': 3,
715 714 'group.admin': 4,
716 715
717 716 'hg.fork.none': 0,
718 717 'hg.fork.repository': 1,
719 718 'hg.create.none': 0,
720 719 'hg.create.repository':1
721 720 }
722 721
723 722 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
724 723 permission_name = Column("permission_name", String(255), nullable=True, unique=None, default=None)
725 724 permission_longname = Column("permission_longname", String(255), nullable=True, unique=None, default=None)
726 725
727 726 def __unicode__(self):
728 727 return u"<%s('%s:%s')>" % (
729 728 self.__class__.__name__, self.permission_id, self.permission_name
730 729 )
731 730
732 731 @classmethod
733 732 def get_by_key(cls, key):
734 733 return cls.query().filter(cls.permission_name == key).scalar()
735 734
736 735
737 736 class UserRepoToPerm(Base, BaseModel):
738 737 __tablename__ = 'repo_to_perm'
739 738 __table_args__ = (
740 739 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
741 740 {'extend_existing': True, 'mysql_engine': 'InnoDB',
742 741 'mysql_charset': 'utf8'}
743 742 )
744 743 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
745 744 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
746 745 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
747 746 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
748 747
749 748 user = relationship('User')
750 749 repository = relationship('Repository')
751 750 permission = relationship('Permission')
752 751
753 752 def __unicode__(self):
754 753 return u'<user:%s => %s >' % (self.user, self.repository)
755 754
756 755
757 756 class UserToPerm(Base, BaseModel):
758 757 __tablename__ = 'user_to_perm'
759 758 __table_args__ = (
760 759 UniqueConstraint('user_id', 'permission_id'),
761 760 {'extend_existing': True, 'mysql_engine': 'InnoDB',
762 761 'mysql_charset': 'utf8'}
763 762 )
764 763 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
765 764 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
766 765 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
767 766
768 767 user = relationship('User')
769 768 permission = relationship('Permission', lazy='joined')
770 769
771 770
772 771 class UserGroupRepoToPerm(Base, BaseModel):
773 772 __tablename__ = 'users_group_repo_to_perm'
774 773 __table_args__ = (
775 774 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
776 775 {'extend_existing': True, 'mysql_engine': 'InnoDB',
777 776 'mysql_charset': 'utf8'}
778 777 )
779 778 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
780 779 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
781 780 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
782 781 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
783 782
784 783 users_group = relationship('UserGroup')
785 784 permission = relationship('Permission')
786 785 repository = relationship('Repository')
787 786
788 787 def __unicode__(self):
789 788 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
790 789
791 790
792 791 class UserGroupToPerm(Base, BaseModel):
793 792 __tablename__ = 'users_group_to_perm'
794 793 __table_args__ = (
795 794 UniqueConstraint('users_group_id', 'permission_id',),
796 795 {'extend_existing': True, 'mysql_engine': 'InnoDB',
797 796 'mysql_charset': 'utf8'}
798 797 )
799 798 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
800 799 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
801 800 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
802 801
803 802 users_group = relationship('UserGroup')
804 803 permission = relationship('Permission')
805 804
806 805
807 806 class UserRepoGroupToPerm(Base, BaseModel):
808 807 __tablename__ = 'user_repo_group_to_perm'
809 808 __table_args__ = (
810 809 UniqueConstraint('user_id', 'group_id', 'permission_id'),
811 810 {'extend_existing': True, 'mysql_engine': 'InnoDB',
812 811 'mysql_charset': 'utf8'}
813 812 )
814 813
815 814 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
816 815 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
817 816 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
818 817 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
819 818
820 819 user = relationship('User')
821 820 group = relationship('RepoGroup')
822 821 permission = relationship('Permission')
823 822
824 823
825 824 class UserGroupRepoGroupToPerm(Base, BaseModel):
826 825 __tablename__ = 'users_group_repo_group_to_perm'
827 826 __table_args__ = (
828 827 UniqueConstraint('users_group_id', 'group_id'),
829 828 {'extend_existing': True, 'mysql_engine': 'InnoDB',
830 829 'mysql_charset': 'utf8'}
831 830 )
832 831
833 832 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)
834 833 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
835 834 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
836 835 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
837 836
838 837 users_group = relationship('UserGroup')
839 838 permission = relationship('Permission')
840 839 group = relationship('RepoGroup')
841 840
842 841
843 842 class Statistics(Base, BaseModel):
844 843 __tablename__ = 'statistics'
845 844 __table_args__ = (
846 845 UniqueConstraint('repository_id'),
847 846 {'extend_existing': True, 'mysql_engine': 'InnoDB',
848 847 'mysql_charset': 'utf8'}
849 848 )
850 849 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
851 850 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
852 851 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
853 852 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
854 853 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
855 854 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
856 855
857 856 repository = relationship('Repository', single_parent=True)
858 857
859 858
860 859 class UserFollowing(Base, BaseModel):
861 860 __tablename__ = 'user_followings'
862 861 __table_args__ = (
863 862 UniqueConstraint('user_id', 'follows_repository_id'),
864 863 UniqueConstraint('user_id', 'follows_user_id'),
865 864 {'extend_existing': True, 'mysql_engine': 'InnoDB',
866 865 'mysql_charset': 'utf8'}
867 866 )
868 867
869 868 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
870 869 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
871 870 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
872 871 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
873 872 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
874 873
875 874 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
876 875
877 876 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
878 877 follows_repository = relationship('Repository', order_by='Repository.repo_name')
879 878
880 879
881 880 class CacheInvalidation(Base, BaseModel):
882 881 __tablename__ = 'cache_invalidation'
883 882 __table_args__ = (
884 883 UniqueConstraint('cache_key'),
885 884 Index('key_idx', 'cache_key'),
886 885 {'extend_existing': True, 'mysql_engine': 'InnoDB',
887 886 'mysql_charset': 'utf8'},
888 887 )
889 888 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
890 889 cache_key = Column("cache_key", String(255), nullable=True, unique=None, default=None)
891 890 cache_args = Column("cache_args", String(255), nullable=True, unique=None, default=None)
892 891 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
893 892
894 893 def __init__(self, cache_key, cache_args=''):
895 894 self.cache_key = cache_key
896 895 self.cache_args = cache_args
897 896 self.cache_active = False
898 897
899 898
900 899 class ChangesetComment(Base, BaseModel):
901 900 __tablename__ = 'changeset_comments'
902 901 __table_args__ = (
903 902 Index('cc_revision_idx', 'revision'),
904 903 {'extend_existing': True, 'mysql_engine': 'InnoDB',
905 904 'mysql_charset': 'utf8'},
906 905 )
907 906 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
908 907 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
909 908 revision = Column('revision', String(40), nullable=True)
910 909 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
911 910 line_no = Column('line_no', Unicode(10), nullable=True)
912 911 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
913 912 f_path = Column('f_path', Unicode(1000), nullable=True)
914 913 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
915 914 text = Column('text', UnicodeText().with_variant(UnicodeText(25000), 'mysql'), nullable=False)
916 915 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
917 916 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
918 917
919 918 author = relationship('User', lazy='joined')
920 919 repo = relationship('Repository')
921 920 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
922 921 pull_request = relationship('PullRequest', lazy='joined')
923 922
924 923 @classmethod
925 924 def get_users(cls, revision=None, pull_request_id=None):
926 925 """
927 926 Returns user associated with this ChangesetComment. ie those
928 927 who actually commented
929 928
930 929 :param cls:
931 930 :param revision:
932 931 """
933 932 q = Session().query(User)\
934 933 .join(ChangesetComment.author)
935 934 if revision:
936 935 q = q.filter(cls.revision == revision)
937 936 elif pull_request_id:
938 937 q = q.filter(cls.pull_request_id == pull_request_id)
939 938 return q.all()
940 939
941 940
942 941 class ChangesetStatus(Base, BaseModel):
943 942 __tablename__ = 'changeset_statuses'
944 943 __table_args__ = (
945 944 Index('cs_revision_idx', 'revision'),
946 945 Index('cs_version_idx', 'version'),
947 946 UniqueConstraint('repo_id', 'revision', 'version'),
948 947 {'extend_existing': True, 'mysql_engine': 'InnoDB',
949 948 'mysql_charset': 'utf8'}
950 949 )
951 950 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
952 951 STATUS_APPROVED = 'approved'
953 952 STATUS_REJECTED = 'rejected'
954 953 STATUS_UNDER_REVIEW = 'under_review'
955 954
956 955 STATUSES = [
957 956 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
958 957 (STATUS_APPROVED, _("Approved")),
959 958 (STATUS_REJECTED, _("Rejected")),
960 959 (STATUS_UNDER_REVIEW, _("Under Review")),
961 960 ]
962 961
963 962 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
964 963 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
965 964 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
966 965 revision = Column('revision', String(40), nullable=False)
967 966 status = Column('status', String(128), nullable=False, default=DEFAULT)
968 967 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
969 968 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
970 969 version = Column('version', Integer(), nullable=False, default=0)
971 970 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
972 971
973 972 author = relationship('User', lazy='joined')
974 973 repo = relationship('Repository')
975 974 comment = relationship('ChangesetComment', lazy='joined')
976 975 pull_request = relationship('PullRequest', lazy='joined')
977 976
978 977
979 978
980 979 class PullRequest(Base, BaseModel):
981 980 __tablename__ = 'pull_requests'
982 981 __table_args__ = (
983 982 {'extend_existing': True, 'mysql_engine': 'InnoDB',
984 983 'mysql_charset': 'utf8'},
985 984 )
986 985
987 986 STATUS_NEW = u'new'
988 987 STATUS_OPEN = u'open'
989 988 STATUS_CLOSED = u'closed'
990 989
991 990 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
992 991 title = Column('title', Unicode(256), nullable=True)
993 992 description = Column('description', UnicodeText().with_variant(UnicodeText(10240), 'mysql'), nullable=True)
994 993 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
995 994 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
996 995 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
997 996 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
998 997 _revisions = Column('revisions', UnicodeText().with_variant(UnicodeText(20500), 'mysql'))
999 998 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1000 999 org_ref = Column('org_ref', Unicode(256), nullable=False)
1001 1000 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1002 1001 other_ref = Column('other_ref', Unicode(256), nullable=False)
1003 1002
1004 1003 author = relationship('User', lazy='joined')
1005 1004 reviewers = relationship('PullRequestReviewers',
1006 1005 cascade="all, delete, delete-orphan")
1007 1006 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1008 1007 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1009 1008 statuses = relationship('ChangesetStatus')
1010 1009 comments = relationship('ChangesetComment',
1011 1010 cascade="all, delete, delete-orphan")
1012 1011
1013 1012
1014 1013 class PullRequestReviewers(Base, BaseModel):
1015 1014 __tablename__ = 'pull_request_reviewers'
1016 1015 __table_args__ = (
1017 1016 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1018 1017 'mysql_charset': 'utf8'},
1019 1018 )
1020 1019
1021 1020 def __init__(self, user=None, pull_request=None):
1022 1021 self.user = user
1023 1022 self.pull_request = pull_request
1024 1023
1025 1024 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1026 1025 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1027 1026 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1028 1027
1029 1028 user = relationship('User')
1030 1029 pull_request = relationship('PullRequest')
1031 1030
1032 1031
1033 1032 class Notification(Base, BaseModel):
1034 1033 __tablename__ = 'notifications'
1035 1034 __table_args__ = (
1036 1035 Index('notification_type_idx', 'type'),
1037 1036 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1038 1037 'mysql_charset': 'utf8'},
1039 1038 )
1040 1039
1041 1040 TYPE_CHANGESET_COMMENT = u'cs_comment'
1042 1041 TYPE_MESSAGE = u'message'
1043 1042 TYPE_MENTION = u'mention'
1044 1043 TYPE_REGISTRATION = u'registration'
1045 1044 TYPE_PULL_REQUEST = u'pull_request'
1046 1045 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1047 1046
1048 1047 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1049 1048 subject = Column('subject', Unicode(512), nullable=True)
1050 1049 body = Column('body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True)
1051 1050 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1052 1051 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1053 1052 type_ = Column('type', Unicode(256))
1054 1053
1055 1054 created_by_user = relationship('User')
1056 1055 notifications_to_users = relationship('UserNotification', lazy='joined',
1057 1056 cascade="all, delete, delete-orphan")
1058 1057
1059 1058
1060 1059 class UserNotification(Base, BaseModel):
1061 1060 __tablename__ = 'user_to_notification'
1062 1061 __table_args__ = (
1063 1062 UniqueConstraint('user_id', 'notification_id'),
1064 1063 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1065 1064 'mysql_charset': 'utf8'}
1066 1065 )
1067 1066 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1068 1067 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1069 1068 read = Column('read', Boolean, default=False)
1070 1069 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1071 1070
1072 1071 user = relationship('User', lazy="joined")
1073 1072 notification = relationship('Notification', lazy="joined",
1074 1073 order_by=lambda: Notification.created_on.desc(),)
1075 1074
1076 1075
1077 1076 class DbMigrateVersion(Base, BaseModel):
1078 1077 __tablename__ = 'db_migrate_version'
1079 1078 __table_args__ = (
1080 1079 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1081 1080 'mysql_charset': 'utf8'},
1082 1081 )
1083 1082 repository_id = Column('repository_id', String(250), primary_key=True)
1084 1083 repository_path = Column('repository_path', Text)
1085 1084 version = Column('version', Integer)
@@ -1,1146 +1,1145 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import os
22 22 import time
23 23 import logging
24 24 import datetime
25 25 import traceback
26 26 import hashlib
27 27 import collections
28 28
29 29 from sqlalchemy import *
30 30 from sqlalchemy.ext.hybrid import hybrid_property
31 31 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
32 32 from sqlalchemy.exc import DatabaseError
33 33 from beaker.cache import cache_region, region_invalidate
34 34 from webob.exc import HTTPNotFound
35 35
36 36 from rhodecode.translation import _
37 37
38 38 from rhodecode.lib.vcs import get_backend
39 39 from rhodecode.lib.vcs.utils.helpers import get_scm
40 40 from rhodecode.lib.vcs.exceptions import VCSError
41 41 from zope.cachedescriptors.property import Lazy as LazyProperty
42 42 from rhodecode.lib.vcs.backends.base import EmptyCommit
43 43
44 44 from rhodecode.lib.utils2 import str2bool, safe_str, get_commit_safe, \
45 45 safe_unicode, remove_suffix, remove_prefix, time_to_datetime
46 46 from rhodecode.lib.ext_json import json
47 47 from rhodecode.lib.caching_query import FromCache
48 48
49 49 from rhodecode.model.meta import Base, Session
50 50
51 51 URL_SEP = '/'
52 52 log = logging.getLogger(__name__)
53 53
54 54 #==============================================================================
55 55 # BASE CLASSES
56 56 #==============================================================================
57 57
58 58 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
59 59
60 60
61 61 class BaseModel(object):
62 62 """
63 63 Base Model for all classes
64 64 """
65 65
66 66 @classmethod
67 67 def _get_keys(cls):
68 68 """return column names for this model """
69 69 return class_mapper(cls).c.keys()
70 70
71 71 def get_dict(self):
72 72 """
73 73 return dict with keys and values corresponding
74 74 to this model data """
75 75
76 76 d = {}
77 77 for k in self._get_keys():
78 78 d[k] = getattr(self, k)
79 79
80 80 # also use __json__() if present to get additional fields
81 81 _json_attr = getattr(self, '__json__', None)
82 82 if _json_attr:
83 83 # update with attributes from __json__
84 84 if callable(_json_attr):
85 85 _json_attr = _json_attr()
86 86 for k, val in _json_attr.iteritems():
87 87 d[k] = val
88 88 return d
89 89
90 90 def get_appstruct(self):
91 91 """return list with keys and values tupples corresponding
92 92 to this model data """
93 93
94 94 l = []
95 95 for k in self._get_keys():
96 96 l.append((k, getattr(self, k),))
97 97 return l
98 98
99 99 def populate_obj(self, populate_dict):
100 100 """populate model with data from given populate_dict"""
101 101
102 102 for k in self._get_keys():
103 103 if k in populate_dict:
104 104 setattr(self, k, populate_dict[k])
105 105
106 106 @classmethod
107 107 def query(cls):
108 108 return Session().query(cls)
109 109
110 110 @classmethod
111 111 def get(cls, id_):
112 112 if id_:
113 113 return cls.query().get(id_)
114 114
115 115 @classmethod
116 116 def get_or_404(cls, id_):
117 117 try:
118 118 id_ = int(id_)
119 119 except (TypeError, ValueError):
120 120 raise HTTPNotFound
121 121
122 122 res = cls.query().get(id_)
123 123 if not res:
124 124 raise HTTPNotFound
125 125 return res
126 126
127 127 @classmethod
128 128 def getAll(cls):
129 129 # deprecated and left for backward compatibility
130 130 return cls.get_all()
131 131
132 132 @classmethod
133 133 def get_all(cls):
134 134 return cls.query().all()
135 135
136 136 @classmethod
137 137 def delete(cls, id_):
138 138 obj = cls.query().get(id_)
139 139 Session().delete(obj)
140 140
141 141 def __repr__(self):
142 142 if hasattr(self, '__unicode__'):
143 143 # python repr needs to return str
144 144 return safe_str(self.__unicode__())
145 145 return '<DB:%s>' % (self.__class__.__name__)
146 146
147 147
148 148 class RhodeCodeSetting(Base, BaseModel):
149 149 __tablename__ = 'rhodecode_settings'
150 150 __table_args__ = (
151 151 UniqueConstraint('app_settings_name'),
152 152 {'extend_existing': True, 'mysql_engine': 'InnoDB',
153 153 'mysql_charset': 'utf8'}
154 154 )
155 155 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
156 156 app_settings_name = Column("app_settings_name", String(255), nullable=True, unique=None, default=None)
157 157 _app_settings_value = Column("app_settings_value", String(255), nullable=True, unique=None, default=None)
158 158
159 159 def __init__(self, k='', v=''):
160 160 self.app_settings_name = k
161 161 self.app_settings_value = v
162 162
163 163 @validates('_app_settings_value')
164 164 def validate_settings_value(self, key, val):
165 165 assert type(val) == unicode
166 166 return val
167 167
168 168 @hybrid_property
169 169 def app_settings_value(self):
170 170 v = self._app_settings_value
171 171 if self.app_settings_name in ["ldap_active",
172 172 "default_repo_enable_statistics",
173 173 "default_repo_enable_locking",
174 174 "default_repo_private",
175 175 "default_repo_enable_downloads"]:
176 176 v = str2bool(v)
177 177 return v
178 178
179 179 @app_settings_value.setter
180 180 def app_settings_value(self, val):
181 181 """
182 182 Setter that will always make sure we use unicode in app_settings_value
183 183
184 184 :param val:
185 185 """
186 186 self._app_settings_value = safe_unicode(val)
187 187
188 188 def __unicode__(self):
189 189 return u"<%s('%s:%s')>" % (
190 190 self.__class__.__name__,
191 191 self.app_settings_name, self.app_settings_value
192 192 )
193 193
194 194
195 195 class RhodeCodeUi(Base, BaseModel):
196 196 __tablename__ = 'rhodecode_ui'
197 197 __table_args__ = (
198 198 UniqueConstraint('ui_key'),
199 199 {'extend_existing': True, 'mysql_engine': 'InnoDB',
200 200 'mysql_charset': 'utf8'}
201 201 )
202 202
203 203 HOOK_REPO_SIZE = 'changegroup.repo_size'
204 204 HOOK_PUSH = 'changegroup.push_logger'
205 205 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
206 206 HOOK_PULL = 'outgoing.pull_logger'
207 207 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
208 208
209 209 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
210 210 ui_section = Column("ui_section", String(255), nullable=True, unique=None, default=None)
211 211 ui_key = Column("ui_key", String(255), nullable=True, unique=None, default=None)
212 212 ui_value = Column("ui_value", String(255), nullable=True, unique=None, default=None)
213 213 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
214 214
215 215
216 216
217 217 class User(Base, BaseModel):
218 218 __tablename__ = 'users'
219 219 __table_args__ = (
220 220 UniqueConstraint('username'), UniqueConstraint('email'),
221 221 Index('u_username_idx', 'username'),
222 222 Index('u_email_idx', 'email'),
223 223 {'extend_existing': True, 'mysql_engine': 'InnoDB',
224 224 'mysql_charset': 'utf8'}
225 225 )
226 226 DEFAULT_USER = 'default'
227 227
228 228 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
229 229 username = Column("username", String(255), nullable=True, unique=None, default=None)
230 230 password = Column("password", String(255), nullable=True, unique=None, default=None)
231 231 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
232 232 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
233 233 name = Column("firstname", String(255), nullable=True, unique=None, default=None)
234 234 lastname = Column("lastname", String(255), nullable=True, unique=None, default=None)
235 235 _email = Column("email", String(255), nullable=True, unique=None, default=None)
236 236 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
237 237 ldap_dn = Column("ldap_dn", String(255), nullable=True, unique=None, default=None)
238 238 api_key = Column("api_key", String(255), nullable=True, unique=None, default=None)
239 239 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
240 240
241 241 user_log = relationship('UserLog')
242 242 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
243 243
244 244 repositories = relationship('Repository')
245 245 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
246 246 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
247 247
248 248 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
249 249 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
250 250
251 251 group_member = relationship('UserGroupMember', cascade='all')
252 252
253 253 notifications = relationship('UserNotification', cascade='all')
254 254 # notifications assigned to this user
255 255 user_created_notifications = relationship('Notification', cascade='all')
256 256 # comments created by this user
257 257 user_comments = relationship('ChangesetComment', cascade='all')
258 258 user_emails = relationship('UserEmailMap', cascade='all')
259 259
260 260 @hybrid_property
261 261 def email(self):
262 262 return self._email
263 263
264 264 @email.setter
265 265 def email(self, val):
266 266 self._email = val.lower() if val else None
267 267
268 268 @property
269 269 def firstname(self):
270 270 # alias for future
271 271 return self.name
272 272
273 273 @property
274 274 def username_and_name(self):
275 275 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
276 276
277 277 @property
278 278 def full_name(self):
279 279 return '%s %s' % (self.firstname, self.lastname)
280 280
281 281 @property
282 282 def full_contact(self):
283 283 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
284 284
285 285 @property
286 286 def short_contact(self):
287 287 return '%s %s' % (self.firstname, self.lastname)
288 288
289 289 @property
290 290 def is_admin(self):
291 291 return self.admin
292 292
293 293 @classmethod
294 294 def get_by_username(cls, username, case_insensitive=False, cache=False):
295 295 if case_insensitive:
296 296 q = cls.query().filter(cls.username.ilike(username))
297 297 else:
298 298 q = cls.query().filter(cls.username == username)
299 299
300 300 if cache:
301 301 q = q.options(FromCache(
302 302 "sql_cache_short",
303 303 "get_user_%s" % _hash_key(username)
304 304 )
305 305 )
306 306 return q.scalar()
307 307
308 308 @classmethod
309 309 def get_by_auth_token(cls, auth_token, cache=False):
310 310 q = cls.query().filter(cls.api_key == auth_token)
311 311
312 312 if cache:
313 313 q = q.options(FromCache("sql_cache_short",
314 314 "get_auth_token_%s" % auth_token))
315 315 return q.scalar()
316 316
317 317 @classmethod
318 318 def get_by_email(cls, email, case_insensitive=False, cache=False):
319 319 if case_insensitive:
320 320 q = cls.query().filter(cls.email.ilike(email))
321 321 else:
322 322 q = cls.query().filter(cls.email == email)
323 323
324 324 if cache:
325 325 q = q.options(FromCache("sql_cache_short",
326 326 "get_email_key_%s" % email))
327 327
328 328 ret = q.scalar()
329 329 if ret is None:
330 330 q = UserEmailMap.query()
331 331 # try fetching in alternate email map
332 332 if case_insensitive:
333 333 q = q.filter(UserEmailMap.email.ilike(email))
334 334 else:
335 335 q = q.filter(UserEmailMap.email == email)
336 336 q = q.options(joinedload(UserEmailMap.user))
337 337 if cache:
338 338 q = q.options(FromCache("sql_cache_short",
339 339 "get_email_map_key_%s" % email))
340 340 ret = getattr(q.scalar(), 'user', None)
341 341
342 342 return ret
343 343
344 344 @classmethod
345 345 def get_first_admin(cls):
346 346 user = User.query().filter(User.admin == True).first()
347 347 if user is None:
348 348 raise Exception('Missing administrative account!')
349 349 return user
350 350
351 351 @classmethod
352 352 def get_default_user(cls, cache=False):
353 353 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
354 354 if user is None:
355 355 raise Exception('Missing default account!')
356 356 return user
357 357
358 358
359 359
360 360
361 361 class UserEmailMap(Base, BaseModel):
362 362 __tablename__ = 'user_email_map'
363 363 __table_args__ = (
364 364 Index('uem_email_idx', 'email'),
365 365 UniqueConstraint('email'),
366 366 {'extend_existing': True, 'mysql_engine': 'InnoDB',
367 367 'mysql_charset': 'utf8'}
368 368 )
369 369 __mapper_args__ = {}
370 370
371 371 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
372 372 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
373 373 _email = Column("email", String(255), nullable=True, unique=False, default=None)
374 374 user = relationship('User', lazy='joined')
375 375
376 376 @validates('_email')
377 377 def validate_email(self, key, email):
378 378 # check if this email is not main one
379 379 main_email = Session().query(User).filter(User.email == email).scalar()
380 380 if main_email is not None:
381 381 raise AttributeError('email %s is present is user table' % email)
382 382 return email
383 383
384 384 @hybrid_property
385 385 def email(self):
386 386 return self._email
387 387
388 388 @email.setter
389 389 def email(self, val):
390 390 self._email = val.lower() if val else None
391 391
392 392
393 393 class UserIpMap(Base, BaseModel):
394 394 __tablename__ = 'user_ip_map'
395 395 __table_args__ = (
396 396 UniqueConstraint('user_id', 'ip_addr'),
397 397 {'extend_existing': True, 'mysql_engine': 'InnoDB',
398 398 'mysql_charset': 'utf8'}
399 399 )
400 400 __mapper_args__ = {}
401 401
402 402 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
403 403 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
404 404 ip_addr = Column("ip_addr", String(255), nullable=True, unique=False, default=None)
405 405 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
406 406 user = relationship('User', lazy='joined')
407 407
408 408
409 409 class UserLog(Base, BaseModel):
410 410 __tablename__ = 'user_logs'
411 411 __table_args__ = (
412 412 {'extend_existing': True, 'mysql_engine': 'InnoDB',
413 413 'mysql_charset': 'utf8'},
414 414 )
415 415 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
416 416 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
417 417 username = Column("username", String(255), nullable=True, unique=None, default=None)
418 418 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
419 419 repository_name = Column("repository_name", String(255), nullable=True, unique=None, default=None)
420 420 user_ip = Column("user_ip", String(255), nullable=True, unique=None, default=None)
421 421 action = Column("action", String(1200000), nullable=True, unique=None, default=None)
422 422 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
423 423
424 424 def __unicode__(self):
425 425 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
426 426 self.repository_name,
427 427 self.action)
428 428
429 429 user = relationship('User')
430 430 repository = relationship('Repository', cascade='')
431 431
432 432
433 433 class UserGroup(Base, BaseModel):
434 434 __tablename__ = 'users_groups'
435 435 __table_args__ = (
436 436 {'extend_existing': True, 'mysql_engine': 'InnoDB',
437 437 'mysql_charset': 'utf8'},
438 438 )
439 439
440 440 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
441 441 users_group_name = Column("users_group_name", String(255), nullable=False, unique=True, default=None)
442 442 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
443 443 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
444 444 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
445 445
446 446 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
447 447 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
448 448 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
449 449 users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
450 450 user_user_group_to_perm = relationship('UserUserGroupToPerm', cascade='all')
451 451 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm ', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
452 452
453 453 user = relationship('User')
454 454
455 455 def __unicode__(self):
456 456 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
457 457 self.users_group_id,
458 458 self.users_group_name)
459 459
460 460 @classmethod
461 461 def get_by_group_name(cls, group_name, cache=False,
462 462 case_insensitive=False):
463 463 if case_insensitive:
464 464 q = cls.query().filter(cls.users_group_name.ilike(group_name))
465 465 else:
466 466 q = cls.query().filter(cls.users_group_name == group_name)
467 467 if cache:
468 468 q = q.options(FromCache(
469 469 "sql_cache_short",
470 470 "get_user_%s" % _hash_key(group_name)
471 471 )
472 472 )
473 473 return q.scalar()
474 474
475 475 @classmethod
476 476 def get(cls, users_group_id, cache=False):
477 477 user_group = cls.query()
478 478 if cache:
479 479 user_group = user_group.options(FromCache("sql_cache_short",
480 480 "get_users_group_%s" % users_group_id))
481 481 return user_group.get(users_group_id)
482 482
483 483
484 484 class UserGroupMember(Base, BaseModel):
485 485 __tablename__ = 'users_groups_members'
486 486 __table_args__ = (
487 487 {'extend_existing': True, 'mysql_engine': 'InnoDB',
488 488 'mysql_charset': 'utf8'},
489 489 )
490 490
491 491 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
492 492 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
493 493 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
494 494
495 495 user = relationship('User', lazy='joined')
496 496 users_group = relationship('UserGroup')
497 497
498 498 def __init__(self, gr_id='', u_id=''):
499 499 self.users_group_id = gr_id
500 500 self.user_id = u_id
501 501
502 502
503 503 class RepositoryField(Base, BaseModel):
504 504 __tablename__ = 'repositories_fields'
505 505 __table_args__ = (
506 506 UniqueConstraint('repository_id', 'field_key'), # no-multi field
507 507 {'extend_existing': True, 'mysql_engine': 'InnoDB',
508 508 'mysql_charset': 'utf8'},
509 509 )
510 510 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
511 511
512 512 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
513 513 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
514 514 field_key = Column("field_key", String(250))
515 515 field_label = Column("field_label", String(1024), nullable=False)
516 516 field_value = Column("field_value", String(10000), nullable=False)
517 517 field_desc = Column("field_desc", String(1024), nullable=False)
518 518 field_type = Column("field_type", String(256), nullable=False, unique=None)
519 519 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
520 520
521 521 repository = relationship('Repository')
522 522
523 523 @classmethod
524 524 def get_by_key_name(cls, key, repo):
525 525 row = cls.query()\
526 526 .filter(cls.repository == repo)\
527 527 .filter(cls.field_key == key).scalar()
528 528 return row
529 529
530 530
531 531 class Repository(Base, BaseModel):
532 532 __tablename__ = 'repositories'
533 533 __table_args__ = (
534 534 UniqueConstraint('repo_name'),
535 535 Index('r_repo_name_idx', 'repo_name'),
536 536 {'extend_existing': True, 'mysql_engine': 'InnoDB',
537 537 'mysql_charset': 'utf8'},
538 538 )
539 539
540 540 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
541 541 repo_name = Column("repo_name", String(255), nullable=False, unique=True, default=None)
542 542 clone_uri = Column("clone_uri", String(255), nullable=True, unique=False, default=None)
543 543 repo_type = Column("repo_type", String(255), nullable=False, unique=False, default=None)
544 544 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
545 545 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
546 546 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
547 547 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
548 548 description = Column("description", String(10000), nullable=True, unique=None, default=None)
549 549 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
550 550 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
551 551 landing_rev = Column("landing_revision", String(255), nullable=False, unique=False, default=None)
552 552 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
553 553 _locked = Column("locked", String(255), nullable=True, unique=False, default=None)
554 554 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
555 555
556 556 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
557 557 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
558 558
559 559 user = relationship('User')
560 560 fork = relationship('Repository', remote_side=repo_id)
561 561 group = relationship('RepoGroup')
562 562 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
563 563 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
564 564 stats = relationship('Statistics', cascade='all', uselist=False)
565 565
566 566 followers = relationship('UserFollowing',
567 567 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
568 568 cascade='all')
569 569 extra_fields = relationship('RepositoryField',
570 570 cascade="all, delete, delete-orphan")
571 571
572 572 logs = relationship('UserLog')
573 573 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
574 574
575 575 pull_requests_org = relationship('PullRequest',
576 576 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
577 577 cascade="all, delete, delete-orphan")
578 578
579 579 pull_requests_other = relationship('PullRequest',
580 580 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
581 581 cascade="all, delete, delete-orphan")
582 582
583 583 def __unicode__(self):
584 584 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
585 585 safe_unicode(self.repo_name))
586 586
587 587 @classmethod
588 588 def get_by_repo_name(cls, repo_name):
589 589 q = Session().query(cls).filter(cls.repo_name == repo_name)
590 590 q = q.options(joinedload(Repository.fork))\
591 591 .options(joinedload(Repository.user))\
592 592 .options(joinedload(Repository.group))
593 593 return q.scalar()
594 594
595 595
596 596 class RepoGroup(Base, BaseModel):
597 597 __tablename__ = 'groups'
598 598 __table_args__ = (
599 599 UniqueConstraint('group_name', 'group_parent_id'),
600 CheckConstraint('group_id != group_parent_id'),
601 600 {'extend_existing': True, 'mysql_engine': 'InnoDB',
602 601 'mysql_charset': 'utf8'},
603 602 )
604 603 __mapper_args__ = {'order_by': 'group_name'}
605 604
606 605 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
607 606 group_name = Column("group_name", String(255), nullable=False, unique=True, default=None)
608 607 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
609 608 group_description = Column("group_description", String(10000), nullable=True, unique=None, default=None)
610 609 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
611 610 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
612 611
613 612 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
614 613 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
615 614 parent_group = relationship('RepoGroup', remote_side=group_id)
616 615 user = relationship('User')
617 616
618 617 def __init__(self, group_name='', parent_group=None):
619 618 self.group_name = group_name
620 619 self.parent_group = parent_group
621 620
622 621 def __unicode__(self):
623 622 return u"<%s('id:%s:%s')>" % (self.__class__.__name__, self.group_id,
624 623 self.group_name)
625 624
626 625 @classmethod
627 626 def url_sep(cls):
628 627 return URL_SEP
629 628
630 629 @classmethod
631 630 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
632 631 if case_insensitive:
633 632 gr = cls.query()\
634 633 .filter(cls.group_name.ilike(group_name))
635 634 else:
636 635 gr = cls.query()\
637 636 .filter(cls.group_name == group_name)
638 637 if cache:
639 638 gr = gr.options(FromCache(
640 639 "sql_cache_short",
641 640 "get_group_%s" % _hash_key(group_name)
642 641 )
643 642 )
644 643 return gr.scalar()
645 644
646 645
647 646 class Permission(Base, BaseModel):
648 647 __tablename__ = 'permissions'
649 648 __table_args__ = (
650 649 Index('p_perm_name_idx', 'permission_name'),
651 650 {'extend_existing': True, 'mysql_engine': 'InnoDB',
652 651 'mysql_charset': 'utf8'},
653 652 )
654 653 PERMS = [
655 654 ('hg.admin', _('RhodeCode Administrator')),
656 655
657 656 ('repository.none', _('Repository no access')),
658 657 ('repository.read', _('Repository read access')),
659 658 ('repository.write', _('Repository write access')),
660 659 ('repository.admin', _('Repository admin access')),
661 660
662 661 ('group.none', _('Repository group no access')),
663 662 ('group.read', _('Repository group read access')),
664 663 ('group.write', _('Repository group write access')),
665 664 ('group.admin', _('Repository group admin access')),
666 665
667 666 ('usergroup.none', _('User group no access')),
668 667 ('usergroup.read', _('User group read access')),
669 668 ('usergroup.write', _('User group write access')),
670 669 ('usergroup.admin', _('User group admin access')),
671 670
672 671 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
673 672 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
674 673
675 674 ('hg.usergroup.create.false', _('User Group creation disabled')),
676 675 ('hg.usergroup.create.true', _('User Group creation enabled')),
677 676
678 677 ('hg.create.none', _('Repository creation disabled')),
679 678 ('hg.create.repository', _('Repository creation enabled')),
680 679
681 680 ('hg.fork.none', _('Repository forking disabled')),
682 681 ('hg.fork.repository', _('Repository forking enabled')),
683 682
684 683 ('hg.register.none', _('Registration disabled')),
685 684 ('hg.register.manual_activate', _('User Registration with manual account activation')),
686 685 ('hg.register.auto_activate', _('User Registration with automatic account activation')),
687 686
688 687 ('hg.extern_activate.manual', _('Manual activation of external account')),
689 688 ('hg.extern_activate.auto', _('Automatic activation of external account')),
690 689
691 690 ]
692 691
693 692 #definition of system default permissions for DEFAULT user
694 693 DEFAULT_USER_PERMISSIONS = [
695 694 'repository.read',
696 695 'group.read',
697 696 'usergroup.read',
698 697 'hg.create.repository',
699 698 'hg.fork.repository',
700 699 'hg.register.manual_activate',
701 700 'hg.extern_activate.auto',
702 701 ]
703 702
704 703 # defines which permissions are more important higher the more important
705 704 # Weight defines which permissions are more important.
706 705 # The higher number the more important.
707 706 PERM_WEIGHTS = {
708 707 'repository.none': 0,
709 708 'repository.read': 1,
710 709 'repository.write': 3,
711 710 'repository.admin': 4,
712 711
713 712 'group.none': 0,
714 713 'group.read': 1,
715 714 'group.write': 3,
716 715 'group.admin': 4,
717 716
718 717 'usergroup.none': 0,
719 718 'usergroup.read': 1,
720 719 'usergroup.write': 3,
721 720 'usergroup.admin': 4,
722 721 'hg.repogroup.create.false': 0,
723 722 'hg.repogroup.create.true': 1,
724 723
725 724 'hg.usergroup.create.false': 0,
726 725 'hg.usergroup.create.true': 1,
727 726
728 727 'hg.fork.none': 0,
729 728 'hg.fork.repository': 1,
730 729 'hg.create.none': 0,
731 730 'hg.create.repository': 1
732 731 }
733 732
734 733 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
735 734 permission_name = Column("permission_name", String(255), nullable=True, unique=None, default=None)
736 735 permission_longname = Column("permission_longname", String(255), nullable=True, unique=None, default=None)
737 736
738 737 def __unicode__(self):
739 738 return u"<%s('%s:%s')>" % (
740 739 self.__class__.__name__, self.permission_id, self.permission_name
741 740 )
742 741
743 742 @classmethod
744 743 def get_by_key(cls, key):
745 744 return cls.query().filter(cls.permission_name == key).scalar()
746 745
747 746
748 747 class UserRepoToPerm(Base, BaseModel):
749 748 __tablename__ = 'repo_to_perm'
750 749 __table_args__ = (
751 750 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
752 751 {'extend_existing': True, 'mysql_engine': 'InnoDB',
753 752 'mysql_charset': 'utf8'}
754 753 )
755 754 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
756 755 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
757 756 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
758 757 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
759 758
760 759 user = relationship('User')
761 760 repository = relationship('Repository')
762 761 permission = relationship('Permission')
763 762
764 763 def __unicode__(self):
765 764 return u'<%s => %s >' % (self.user, self.repository)
766 765
767 766
768 767 class UserUserGroupToPerm(Base, BaseModel):
769 768 __tablename__ = 'user_user_group_to_perm'
770 769 __table_args__ = (
771 770 UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
772 771 {'extend_existing': True, 'mysql_engine': 'InnoDB',
773 772 'mysql_charset': 'utf8'}
774 773 )
775 774 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
776 775 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
777 776 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
778 777 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
779 778
780 779 user = relationship('User')
781 780 user_group = relationship('UserGroup')
782 781 permission = relationship('Permission')
783 782
784 783 def __unicode__(self):
785 784 return u'<%s => %s >' % (self.user, self.user_group)
786 785
787 786
788 787 class UserToPerm(Base, BaseModel):
789 788 __tablename__ = 'user_to_perm'
790 789 __table_args__ = (
791 790 UniqueConstraint('user_id', 'permission_id'),
792 791 {'extend_existing': True, 'mysql_engine': 'InnoDB',
793 792 'mysql_charset': 'utf8'}
794 793 )
795 794 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
796 795 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
797 796 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
798 797
799 798 user = relationship('User')
800 799 permission = relationship('Permission', lazy='joined')
801 800
802 801 def __unicode__(self):
803 802 return u'<%s => %s >' % (self.user, self.permission)
804 803
805 804
806 805 class UserGroupRepoToPerm(Base, BaseModel):
807 806 __tablename__ = 'users_group_repo_to_perm'
808 807 __table_args__ = (
809 808 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
810 809 {'extend_existing': True, 'mysql_engine': 'InnoDB',
811 810 'mysql_charset': 'utf8'}
812 811 )
813 812 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
814 813 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
815 814 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
816 815 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
817 816
818 817 users_group = relationship('UserGroup')
819 818 permission = relationship('Permission')
820 819 repository = relationship('Repository')
821 820
822 821 def __unicode__(self):
823 822 return u'<UserGroupRepoToPerm:%s => %s >' % (self.users_group, self.repository)
824 823
825 824
826 825 class UserGroupUserGroupToPerm(Base, BaseModel):
827 826 __tablename__ = 'user_group_user_group_to_perm'
828 827 __table_args__ = (
829 828 UniqueConstraint('target_user_group_id', 'user_group_id', 'permission_id'),
830 829 CheckConstraint('target_user_group_id != user_group_id'),
831 830 {'extend_existing': True, 'mysql_engine': 'InnoDB',
832 831 'mysql_charset': 'utf8'}
833 832 )
834 833 user_group_user_group_to_perm_id = Column("user_group_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
835 834 target_user_group_id = Column("target_user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
836 835 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
837 836 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
838 837
839 838 target_user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
840 839 user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
841 840 permission = relationship('Permission')
842 841
843 842 def __unicode__(self):
844 843 return u'<UserGroupUserGroup:%s => %s >' % (self.target_user_group, self.user_group)
845 844
846 845
847 846 class UserGroupToPerm(Base, BaseModel):
848 847 __tablename__ = 'users_group_to_perm'
849 848 __table_args__ = (
850 849 UniqueConstraint('users_group_id', 'permission_id',),
851 850 {'extend_existing': True, 'mysql_engine': 'InnoDB',
852 851 'mysql_charset': 'utf8'}
853 852 )
854 853 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
855 854 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
856 855 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
857 856
858 857 users_group = relationship('UserGroup')
859 858 permission = relationship('Permission')
860 859
861 860
862 861 class UserRepoGroupToPerm(Base, BaseModel):
863 862 __tablename__ = 'user_repo_group_to_perm'
864 863 __table_args__ = (
865 864 UniqueConstraint('user_id', 'group_id', 'permission_id'),
866 865 {'extend_existing': True, 'mysql_engine': 'InnoDB',
867 866 'mysql_charset': 'utf8'}
868 867 )
869 868
870 869 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
871 870 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
872 871 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
873 872 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
874 873
875 874 user = relationship('User')
876 875 group = relationship('RepoGroup')
877 876 permission = relationship('Permission')
878 877
879 878
880 879 class UserGroupRepoGroupToPerm(Base, BaseModel):
881 880 __tablename__ = 'users_group_repo_group_to_perm'
882 881 __table_args__ = (
883 882 UniqueConstraint('users_group_id', 'group_id'),
884 883 {'extend_existing': True, 'mysql_engine': 'InnoDB',
885 884 'mysql_charset': 'utf8'}
886 885 )
887 886
888 887 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)
889 888 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
890 889 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
891 890 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
892 891
893 892 users_group = relationship('UserGroup')
894 893 permission = relationship('Permission')
895 894 group = relationship('RepoGroup')
896 895
897 896
898 897 class Statistics(Base, BaseModel):
899 898 __tablename__ = 'statistics'
900 899 __table_args__ = (
901 900 UniqueConstraint('repository_id'),
902 901 {'extend_existing': True, 'mysql_engine': 'InnoDB',
903 902 'mysql_charset': 'utf8'}
904 903 )
905 904 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
906 905 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
907 906 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
908 907 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
909 908 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
910 909 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
911 910
912 911 repository = relationship('Repository', single_parent=True)
913 912
914 913
915 914 class UserFollowing(Base, BaseModel):
916 915 __tablename__ = 'user_followings'
917 916 __table_args__ = (
918 917 UniqueConstraint('user_id', 'follows_repository_id'),
919 918 UniqueConstraint('user_id', 'follows_user_id'),
920 919 {'extend_existing': True, 'mysql_engine': 'InnoDB',
921 920 'mysql_charset': 'utf8'}
922 921 )
923 922
924 923 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
925 924 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
926 925 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
927 926 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
928 927 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
929 928
930 929 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
931 930
932 931 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
933 932 follows_repository = relationship('Repository', order_by='Repository.repo_name')
934 933
935 934
936 935 class CacheInvalidation(Base, BaseModel):
937 936 __tablename__ = 'cache_invalidation'
938 937 __table_args__ = (
939 938 UniqueConstraint('cache_key'),
940 939 Index('key_idx', 'cache_key'),
941 940 {'extend_existing': True, 'mysql_engine': 'InnoDB',
942 941 'mysql_charset': 'utf8'},
943 942 )
944 943 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
945 944 cache_key = Column("cache_key", String(255), nullable=True, unique=None, default=None)
946 945 cache_args = Column("cache_args", String(255), nullable=True, unique=None, default=None)
947 946 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
948 947
949 948 def __init__(self, cache_key, cache_args=''):
950 949 self.cache_key = cache_key
951 950 self.cache_args = cache_args
952 951 self.cache_active = False
953 952
954 953
955 954 class ChangesetComment(Base, BaseModel):
956 955 __tablename__ = 'changeset_comments'
957 956 __table_args__ = (
958 957 Index('cc_revision_idx', 'revision'),
959 958 {'extend_existing': True, 'mysql_engine': 'InnoDB',
960 959 'mysql_charset': 'utf8'},
961 960 )
962 961 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
963 962 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
964 963 revision = Column('revision', String(40), nullable=True)
965 964 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
966 965 line_no = Column('line_no', Unicode(10), nullable=True)
967 966 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
968 967 f_path = Column('f_path', Unicode(1000), nullable=True)
969 968 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
970 969 text = Column('text', UnicodeText().with_variant(UnicodeText(25000), 'mysql'), nullable=False)
971 970 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
972 971 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
973 972
974 973 author = relationship('User', lazy='joined')
975 974 repo = relationship('Repository')
976 975 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
977 976 pull_request = relationship('PullRequest', lazy='joined')
978 977
979 978
980 979 class ChangesetStatus(Base, BaseModel):
981 980 __tablename__ = 'changeset_statuses'
982 981 __table_args__ = (
983 982 Index('cs_revision_idx', 'revision'),
984 983 Index('cs_version_idx', 'version'),
985 984 UniqueConstraint('repo_id', 'revision', 'version'),
986 985 {'extend_existing': True, 'mysql_engine': 'InnoDB',
987 986 'mysql_charset': 'utf8'}
988 987 )
989 988 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
990 989 STATUS_APPROVED = 'approved'
991 990 STATUS_REJECTED = 'rejected'
992 991 STATUS_UNDER_REVIEW = 'under_review'
993 992
994 993 STATUSES = [
995 994 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
996 995 (STATUS_APPROVED, _("Approved")),
997 996 (STATUS_REJECTED, _("Rejected")),
998 997 (STATUS_UNDER_REVIEW, _("Under Review")),
999 998 ]
1000 999
1001 1000 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1002 1001 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1003 1002 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1004 1003 revision = Column('revision', String(40), nullable=False)
1005 1004 status = Column('status', String(128), nullable=False, default=DEFAULT)
1006 1005 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1007 1006 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1008 1007 version = Column('version', Integer(), nullable=False, default=0)
1009 1008 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1010 1009
1011 1010 author = relationship('User', lazy='joined')
1012 1011 repo = relationship('Repository')
1013 1012 comment = relationship('ChangesetComment', lazy='joined')
1014 1013 pull_request = relationship('PullRequest', lazy='joined')
1015 1014
1016 1015
1017 1016
1018 1017 class PullRequest(Base, BaseModel):
1019 1018 __tablename__ = 'pull_requests'
1020 1019 __table_args__ = (
1021 1020 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1022 1021 'mysql_charset': 'utf8'},
1023 1022 )
1024 1023
1025 1024 STATUS_NEW = u'new'
1026 1025 STATUS_OPEN = u'open'
1027 1026 STATUS_CLOSED = u'closed'
1028 1027
1029 1028 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1030 1029 title = Column('title', Unicode(256), nullable=True)
1031 1030 description = Column('description', UnicodeText().with_variant(UnicodeText(10240), 'mysql'), nullable=True)
1032 1031 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1033 1032 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1034 1033 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1035 1034 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1036 1035 _revisions = Column('revisions', UnicodeText().with_variant(UnicodeText(20500), 'mysql'))
1037 1036 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1038 1037 org_ref = Column('org_ref', Unicode(256), nullable=False)
1039 1038 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1040 1039 other_ref = Column('other_ref', Unicode(256), nullable=False)
1041 1040
1042 1041 author = relationship('User', lazy='joined')
1043 1042 reviewers = relationship('PullRequestReviewers',
1044 1043 cascade="all, delete, delete-orphan")
1045 1044 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1046 1045 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1047 1046 statuses = relationship('ChangesetStatus')
1048 1047 comments = relationship('ChangesetComment',
1049 1048 cascade="all, delete, delete-orphan")
1050 1049
1051 1050
1052 1051 class PullRequestReviewers(Base, BaseModel):
1053 1052 __tablename__ = 'pull_request_reviewers'
1054 1053 __table_args__ = (
1055 1054 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1056 1055 'mysql_charset': 'utf8'},
1057 1056 )
1058 1057
1059 1058 def __init__(self, user=None, pull_request=None):
1060 1059 self.user = user
1061 1060 self.pull_request = pull_request
1062 1061
1063 1062 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1064 1063 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1065 1064 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1066 1065
1067 1066 user = relationship('User')
1068 1067 pull_request = relationship('PullRequest')
1069 1068
1070 1069
1071 1070 class Notification(Base, BaseModel):
1072 1071 __tablename__ = 'notifications'
1073 1072 __table_args__ = (
1074 1073 Index('notification_type_idx', 'type'),
1075 1074 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1076 1075 'mysql_charset': 'utf8'},
1077 1076 )
1078 1077
1079 1078 TYPE_CHANGESET_COMMENT = u'cs_comment'
1080 1079 TYPE_MESSAGE = u'message'
1081 1080 TYPE_MENTION = u'mention'
1082 1081 TYPE_REGISTRATION = u'registration'
1083 1082 TYPE_PULL_REQUEST = u'pull_request'
1084 1083 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1085 1084
1086 1085 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1087 1086 subject = Column('subject', Unicode(512), nullable=True)
1088 1087 body = Column('body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True)
1089 1088 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1090 1089 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1091 1090 type_ = Column('type', Unicode(256))
1092 1091
1093 1092 created_by_user = relationship('User')
1094 1093 notifications_to_users = relationship('UserNotification', lazy='joined',
1095 1094 cascade="all, delete, delete-orphan")
1096 1095
1097 1096
1098 1097 class UserNotification(Base, BaseModel):
1099 1098 __tablename__ = 'user_to_notification'
1100 1099 __table_args__ = (
1101 1100 UniqueConstraint('user_id', 'notification_id'),
1102 1101 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1103 1102 'mysql_charset': 'utf8'}
1104 1103 )
1105 1104 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1106 1105 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1107 1106 read = Column('read', Boolean, default=False)
1108 1107 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1109 1108
1110 1109 user = relationship('User', lazy="joined")
1111 1110 notification = relationship('Notification', lazy="joined",
1112 1111 order_by=lambda: Notification.created_on.desc(),)
1113 1112
1114 1113
1115 1114 class Gist(Base, BaseModel):
1116 1115 __tablename__ = 'gists'
1117 1116 __table_args__ = (
1118 1117 Index('g_gist_access_id_idx', 'gist_access_id'),
1119 1118 Index('g_created_on_idx', 'created_on'),
1120 1119 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1121 1120 'mysql_charset': 'utf8'}
1122 1121 )
1123 1122 GIST_PUBLIC = u'public'
1124 1123 GIST_PRIVATE = u'private'
1125 1124
1126 1125 gist_id = Column('gist_id', Integer(), primary_key=True)
1127 1126 gist_access_id = Column('gist_access_id', Unicode(250))
1128 1127 gist_description = Column('gist_description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
1129 1128 gist_owner = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=True)
1130 1129 gist_expires = Column('gist_expires', Float(), nullable=False)
1131 1130 gist_type = Column('gist_type', Unicode(128), nullable=False)
1132 1131 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1133 1132 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1134 1133
1135 1134 owner = relationship('User')
1136 1135
1137 1136
1138 1137 class DbMigrateVersion(Base, BaseModel):
1139 1138 __tablename__ = 'db_migrate_version'
1140 1139 __table_args__ = (
1141 1140 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1142 1141 'mysql_charset': 'utf8'},
1143 1142 )
1144 1143 repository_id = Column('repository_id', String(250), primary_key=True)
1145 1144 repository_path = Column('repository_path', Text)
1146 1145 version = Column('version', Integer)
@@ -1,1148 +1,1147 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import os
22 22 import time
23 23 import logging
24 24 import datetime
25 25 import traceback
26 26 import hashlib
27 27 import collections
28 28
29 29 from sqlalchemy import *
30 30 from sqlalchemy.ext.hybrid import hybrid_property
31 31 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
32 32 from sqlalchemy.exc import DatabaseError
33 33 from beaker.cache import cache_region, region_invalidate
34 34 from webob.exc import HTTPNotFound
35 35
36 36 from rhodecode.translation import _
37 37
38 38 from rhodecode.lib.vcs import get_backend
39 39 from rhodecode.lib.vcs.utils.helpers import get_scm
40 40 from rhodecode.lib.vcs.exceptions import VCSError
41 41 from zope.cachedescriptors.property import Lazy as LazyProperty
42 42 from rhodecode.lib.vcs.backends.base import EmptyCommit
43 43
44 44 from rhodecode.lib.utils2 import str2bool, safe_str, get_commit_safe, \
45 45 safe_unicode, remove_suffix, remove_prefix, time_to_datetime
46 46 from rhodecode.lib.ext_json import json
47 47 from rhodecode.lib.caching_query import FromCache
48 48
49 49 from rhodecode.model.meta import Base, Session
50 50
51 51 URL_SEP = '/'
52 52 log = logging.getLogger(__name__)
53 53
54 54 #==============================================================================
55 55 # BASE CLASSES
56 56 #==============================================================================
57 57
58 58 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
59 59
60 60
61 61 class BaseModel(object):
62 62 """
63 63 Base Model for all classes
64 64 """
65 65
66 66 @classmethod
67 67 def _get_keys(cls):
68 68 """return column names for this model """
69 69 return class_mapper(cls).c.keys()
70 70
71 71 def get_dict(self):
72 72 """
73 73 return dict with keys and values corresponding
74 74 to this model data """
75 75
76 76 d = {}
77 77 for k in self._get_keys():
78 78 d[k] = getattr(self, k)
79 79
80 80 # also use __json__() if present to get additional fields
81 81 _json_attr = getattr(self, '__json__', None)
82 82 if _json_attr:
83 83 # update with attributes from __json__
84 84 if callable(_json_attr):
85 85 _json_attr = _json_attr()
86 86 for k, val in _json_attr.iteritems():
87 87 d[k] = val
88 88 return d
89 89
90 90 def get_appstruct(self):
91 91 """return list with keys and values tupples corresponding
92 92 to this model data """
93 93
94 94 l = []
95 95 for k in self._get_keys():
96 96 l.append((k, getattr(self, k),))
97 97 return l
98 98
99 99 def populate_obj(self, populate_dict):
100 100 """populate model with data from given populate_dict"""
101 101
102 102 for k in self._get_keys():
103 103 if k in populate_dict:
104 104 setattr(self, k, populate_dict[k])
105 105
106 106 @classmethod
107 107 def query(cls):
108 108 return Session().query(cls)
109 109
110 110 @classmethod
111 111 def get(cls, id_):
112 112 if id_:
113 113 return cls.query().get(id_)
114 114
115 115 @classmethod
116 116 def get_or_404(cls, id_):
117 117 try:
118 118 id_ = int(id_)
119 119 except (TypeError, ValueError):
120 120 raise HTTPNotFound
121 121
122 122 res = cls.query().get(id_)
123 123 if not res:
124 124 raise HTTPNotFound
125 125 return res
126 126
127 127 @classmethod
128 128 def getAll(cls):
129 129 # deprecated and left for backward compatibility
130 130 return cls.get_all()
131 131
132 132 @classmethod
133 133 def get_all(cls):
134 134 return cls.query().all()
135 135
136 136 @classmethod
137 137 def delete(cls, id_):
138 138 obj = cls.query().get(id_)
139 139 Session().delete(obj)
140 140
141 141 def __repr__(self):
142 142 if hasattr(self, '__unicode__'):
143 143 # python repr needs to return str
144 144 return safe_str(self.__unicode__())
145 145 return '<DB:%s>' % (self.__class__.__name__)
146 146
147 147
148 148 class RhodeCodeSetting(Base, BaseModel):
149 149 __tablename__ = 'rhodecode_settings'
150 150 __table_args__ = (
151 151 UniqueConstraint('app_settings_name'),
152 152 {'extend_existing': True, 'mysql_engine': 'InnoDB',
153 153 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
154 154 )
155 155 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
156 156 app_settings_name = Column("app_settings_name", String(255), nullable=True, unique=None, default=None)
157 157 _app_settings_value = Column("app_settings_value", String(255), nullable=True, unique=None, default=None)
158 158 _app_settings_type = Column("app_settings_type", String(255), nullable=True, unique=None, default=None)
159 159
160 160 def __init__(self, key='', val='', type='unicode'):
161 161 self.app_settings_name = key
162 162 self.app_settings_value = val
163 163 self.app_settings_type = type
164 164
165 165 @validates('_app_settings_value')
166 166 def validate_settings_value(self, key, val):
167 167 assert type(val) == unicode
168 168 return val
169 169
170 170 @hybrid_property
171 171 def app_settings_value(self):
172 172 v = self._app_settings_value
173 173 if self.app_settings_name in ["ldap_active",
174 174 "default_repo_enable_statistics",
175 175 "default_repo_enable_locking",
176 176 "default_repo_private",
177 177 "default_repo_enable_downloads"]:
178 178 v = str2bool(v)
179 179 return v
180 180
181 181 @app_settings_value.setter
182 182 def app_settings_value(self, val):
183 183 """
184 184 Setter that will always make sure we use unicode in app_settings_value
185 185
186 186 :param val:
187 187 """
188 188 self._app_settings_value = safe_unicode(val)
189 189
190 190 def __unicode__(self):
191 191 return u"<%s('%s:%s')>" % (
192 192 self.__class__.__name__,
193 193 self.app_settings_name, self.app_settings_value
194 194 )
195 195
196 196
197 197 class RhodeCodeUi(Base, BaseModel):
198 198 __tablename__ = 'rhodecode_ui'
199 199 __table_args__ = (
200 200 UniqueConstraint('ui_key'),
201 201 {'extend_existing': True, 'mysql_engine': 'InnoDB',
202 202 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
203 203 )
204 204
205 205 HOOK_REPO_SIZE = 'changegroup.repo_size'
206 206 HOOK_PUSH = 'changegroup.push_logger'
207 207 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
208 208 HOOK_PULL = 'outgoing.pull_logger'
209 209 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
210 210
211 211 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
212 212 ui_section = Column("ui_section", String(255), nullable=True, unique=None, default=None)
213 213 ui_key = Column("ui_key", String(255), nullable=True, unique=None, default=None)
214 214 ui_value = Column("ui_value", String(255), nullable=True, unique=None, default=None)
215 215 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
216 216
217 217
218 218
219 219 class User(Base, BaseModel):
220 220 __tablename__ = 'users'
221 221 __table_args__ = (
222 222 UniqueConstraint('username'), UniqueConstraint('email'),
223 223 Index('u_username_idx', 'username'),
224 224 Index('u_email_idx', 'email'),
225 225 {'extend_existing': True, 'mysql_engine': 'InnoDB',
226 226 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
227 227 )
228 228 DEFAULT_USER = 'default'
229 229
230 230 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
231 231 username = Column("username", String(255), nullable=True, unique=None, default=None)
232 232 password = Column("password", String(255), nullable=True, unique=None, default=None)
233 233 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
234 234 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
235 235 name = Column("firstname", String(255), nullable=True, unique=None, default=None)
236 236 lastname = Column("lastname", String(255), nullable=True, unique=None, default=None)
237 237 _email = Column("email", String(255), nullable=True, unique=None, default=None)
238 238 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
239 239 ldap_dn = Column("ldap_dn", String(255), nullable=True, unique=None, default=None)
240 240 api_key = Column("api_key", String(255), nullable=True, unique=None, default=None)
241 241 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
242 242
243 243 user_log = relationship('UserLog')
244 244 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
245 245
246 246 repositories = relationship('Repository')
247 247 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
248 248 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
249 249
250 250 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
251 251 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
252 252
253 253 group_member = relationship('UserGroupMember', cascade='all')
254 254
255 255 notifications = relationship('UserNotification', cascade='all')
256 256 # notifications assigned to this user
257 257 user_created_notifications = relationship('Notification', cascade='all')
258 258 # comments created by this user
259 259 user_comments = relationship('ChangesetComment', cascade='all')
260 260 user_emails = relationship('UserEmailMap', cascade='all')
261 261
262 262 @hybrid_property
263 263 def email(self):
264 264 return self._email
265 265
266 266 @email.setter
267 267 def email(self, val):
268 268 self._email = val.lower() if val else None
269 269
270 270 @property
271 271 def firstname(self):
272 272 # alias for future
273 273 return self.name
274 274
275 275 @property
276 276 def username_and_name(self):
277 277 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
278 278
279 279 @property
280 280 def full_name(self):
281 281 return '%s %s' % (self.firstname, self.lastname)
282 282
283 283 @property
284 284 def full_contact(self):
285 285 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
286 286
287 287 @property
288 288 def short_contact(self):
289 289 return '%s %s' % (self.firstname, self.lastname)
290 290
291 291 @property
292 292 def is_admin(self):
293 293 return self.admin
294 294
295 295 @classmethod
296 296 def get_by_username(cls, username, case_insensitive=False, cache=False):
297 297 if case_insensitive:
298 298 q = cls.query().filter(cls.username.ilike(username))
299 299 else:
300 300 q = cls.query().filter(cls.username == username)
301 301
302 302 if cache:
303 303 q = q.options(FromCache(
304 304 "sql_cache_short",
305 305 "get_user_%s" % _hash_key(username)
306 306 )
307 307 )
308 308 return q.scalar()
309 309
310 310 @classmethod
311 311 def get_by_auth_token(cls, auth_token, cache=False):
312 312 q = cls.query().filter(cls.api_key == auth_token)
313 313
314 314 if cache:
315 315 q = q.options(FromCache("sql_cache_short",
316 316 "get_auth_token_%s" % auth_token))
317 317 return q.scalar()
318 318
319 319 @classmethod
320 320 def get_by_email(cls, email, case_insensitive=False, cache=False):
321 321 if case_insensitive:
322 322 q = cls.query().filter(cls.email.ilike(email))
323 323 else:
324 324 q = cls.query().filter(cls.email == email)
325 325
326 326 if cache:
327 327 q = q.options(FromCache("sql_cache_short",
328 328 "get_email_key_%s" % email))
329 329
330 330 ret = q.scalar()
331 331 if ret is None:
332 332 q = UserEmailMap.query()
333 333 # try fetching in alternate email map
334 334 if case_insensitive:
335 335 q = q.filter(UserEmailMap.email.ilike(email))
336 336 else:
337 337 q = q.filter(UserEmailMap.email == email)
338 338 q = q.options(joinedload(UserEmailMap.user))
339 339 if cache:
340 340 q = q.options(FromCache("sql_cache_short",
341 341 "get_email_map_key_%s" % email))
342 342 ret = getattr(q.scalar(), 'user', None)
343 343
344 344 return ret
345 345
346 346 @classmethod
347 347 def get_first_admin(cls):
348 348 user = User.query().filter(User.admin == True).first()
349 349 if user is None:
350 350 raise Exception('Missing administrative account!')
351 351 return user
352 352
353 353 @classmethod
354 354 def get_default_user(cls, cache=False):
355 355 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
356 356 if user is None:
357 357 raise Exception('Missing default account!')
358 358 return user
359 359
360 360
361 361
362 362
363 363 class UserEmailMap(Base, BaseModel):
364 364 __tablename__ = 'user_email_map'
365 365 __table_args__ = (
366 366 Index('uem_email_idx', 'email'),
367 367 UniqueConstraint('email'),
368 368 {'extend_existing': True, 'mysql_engine': 'InnoDB',
369 369 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
370 370 )
371 371 __mapper_args__ = {}
372 372
373 373 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
374 374 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
375 375 _email = Column("email", String(255), nullable=True, unique=False, default=None)
376 376 user = relationship('User', lazy='joined')
377 377
378 378 @validates('_email')
379 379 def validate_email(self, key, email):
380 380 # check if this email is not main one
381 381 main_email = Session().query(User).filter(User.email == email).scalar()
382 382 if main_email is not None:
383 383 raise AttributeError('email %s is present is user table' % email)
384 384 return email
385 385
386 386 @hybrid_property
387 387 def email(self):
388 388 return self._email
389 389
390 390 @email.setter
391 391 def email(self, val):
392 392 self._email = val.lower() if val else None
393 393
394 394
395 395 class UserIpMap(Base, BaseModel):
396 396 __tablename__ = 'user_ip_map'
397 397 __table_args__ = (
398 398 UniqueConstraint('user_id', 'ip_addr'),
399 399 {'extend_existing': True, 'mysql_engine': 'InnoDB',
400 400 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
401 401 )
402 402 __mapper_args__ = {}
403 403
404 404 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
405 405 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
406 406 ip_addr = Column("ip_addr", String(255), nullable=True, unique=False, default=None)
407 407 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
408 408 user = relationship('User', lazy='joined')
409 409
410 410
411 411 class UserLog(Base, BaseModel):
412 412 __tablename__ = 'user_logs'
413 413 __table_args__ = (
414 414 {'extend_existing': True, 'mysql_engine': 'InnoDB',
415 415 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
416 416 )
417 417 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
418 418 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
419 419 username = Column("username", String(255), nullable=True, unique=None, default=None)
420 420 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
421 421 repository_name = Column("repository_name", String(255), nullable=True, unique=None, default=None)
422 422 user_ip = Column("user_ip", String(255), nullable=True, unique=None, default=None)
423 423 action = Column("action", String(1200000), nullable=True, unique=None, default=None)
424 424 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
425 425
426 426 def __unicode__(self):
427 427 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
428 428 self.repository_name,
429 429 self.action)
430 430
431 431 user = relationship('User')
432 432 repository = relationship('Repository', cascade='')
433 433
434 434
435 435 class UserGroup(Base, BaseModel):
436 436 __tablename__ = 'users_groups'
437 437 __table_args__ = (
438 438 {'extend_existing': True, 'mysql_engine': 'InnoDB',
439 439 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
440 440 )
441 441
442 442 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
443 443 users_group_name = Column("users_group_name", String(255), nullable=False, unique=True, default=None)
444 444 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
445 445 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
446 446 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
447 447
448 448 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
449 449 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
450 450 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
451 451 users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
452 452 user_user_group_to_perm = relationship('UserUserGroupToPerm', cascade='all')
453 453 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm ', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
454 454
455 455 user = relationship('User')
456 456
457 457 def __unicode__(self):
458 458 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
459 459 self.users_group_id,
460 460 self.users_group_name)
461 461
462 462 @classmethod
463 463 def get_by_group_name(cls, group_name, cache=False,
464 464 case_insensitive=False):
465 465 if case_insensitive:
466 466 q = cls.query().filter(cls.users_group_name.ilike(group_name))
467 467 else:
468 468 q = cls.query().filter(cls.users_group_name == group_name)
469 469 if cache:
470 470 q = q.options(FromCache(
471 471 "sql_cache_short",
472 472 "get_user_%s" % _hash_key(group_name)
473 473 )
474 474 )
475 475 return q.scalar()
476 476
477 477 @classmethod
478 478 def get(cls, user_group_id, cache=False):
479 479 user_group = cls.query()
480 480 if cache:
481 481 user_group = user_group.options(FromCache("sql_cache_short",
482 482 "get_users_group_%s" % user_group_id))
483 483 return user_group.get(user_group_id)
484 484
485 485
486 486 class UserGroupMember(Base, BaseModel):
487 487 __tablename__ = 'users_groups_members'
488 488 __table_args__ = (
489 489 {'extend_existing': True, 'mysql_engine': 'InnoDB',
490 490 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
491 491 )
492 492
493 493 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
494 494 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
495 495 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
496 496
497 497 user = relationship('User', lazy='joined')
498 498 users_group = relationship('UserGroup')
499 499
500 500 def __init__(self, gr_id='', u_id=''):
501 501 self.users_group_id = gr_id
502 502 self.user_id = u_id
503 503
504 504
505 505 class RepositoryField(Base, BaseModel):
506 506 __tablename__ = 'repositories_fields'
507 507 __table_args__ = (
508 508 UniqueConstraint('repository_id', 'field_key'), # no-multi field
509 509 {'extend_existing': True, 'mysql_engine': 'InnoDB',
510 510 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
511 511 )
512 512 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
513 513
514 514 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
515 515 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
516 516 field_key = Column("field_key", String(250))
517 517 field_label = Column("field_label", String(1024), nullable=False)
518 518 field_value = Column("field_value", String(10000), nullable=False)
519 519 field_desc = Column("field_desc", String(1024), nullable=False)
520 520 field_type = Column("field_type", String(256), nullable=False, unique=None)
521 521 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
522 522
523 523 repository = relationship('Repository')
524 524
525 525 @classmethod
526 526 def get_by_key_name(cls, key, repo):
527 527 row = cls.query()\
528 528 .filter(cls.repository == repo)\
529 529 .filter(cls.field_key == key).scalar()
530 530 return row
531 531
532 532
533 533 class Repository(Base, BaseModel):
534 534 __tablename__ = 'repositories'
535 535 __table_args__ = (
536 536 UniqueConstraint('repo_name'),
537 537 Index('r_repo_name_idx', 'repo_name'),
538 538 {'extend_existing': True, 'mysql_engine': 'InnoDB',
539 539 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
540 540 )
541 541
542 542 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
543 543 repo_name = Column("repo_name", String(255), nullable=False, unique=True, default=None)
544 544 clone_uri = Column("clone_uri", String(255), nullable=True, unique=False, default=None)
545 545 repo_type = Column("repo_type", String(255), nullable=False, unique=False, default=None)
546 546 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
547 547 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
548 548 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
549 549 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
550 550 description = Column("description", String(10000), nullable=True, unique=None, default=None)
551 551 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
552 552 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
553 553 landing_rev = Column("landing_revision", String(255), nullable=False, unique=False, default=None)
554 554 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
555 555 _locked = Column("locked", String(255), nullable=True, unique=False, default=None)
556 556 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
557 557
558 558 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
559 559 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
560 560
561 561 user = relationship('User')
562 562 fork = relationship('Repository', remote_side=repo_id)
563 563 group = relationship('RepoGroup')
564 564 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
565 565 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
566 566 stats = relationship('Statistics', cascade='all', uselist=False)
567 567
568 568 followers = relationship('UserFollowing',
569 569 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
570 570 cascade='all')
571 571 extra_fields = relationship('RepositoryField',
572 572 cascade="all, delete, delete-orphan")
573 573
574 574 logs = relationship('UserLog')
575 575 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
576 576
577 577 pull_requests_org = relationship('PullRequest',
578 578 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
579 579 cascade="all, delete, delete-orphan")
580 580
581 581 pull_requests_other = relationship('PullRequest',
582 582 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
583 583 cascade="all, delete, delete-orphan")
584 584
585 585 def __unicode__(self):
586 586 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
587 587 safe_unicode(self.repo_name))
588 588
589 589 @classmethod
590 590 def get_by_repo_name(cls, repo_name):
591 591 q = Session().query(cls).filter(cls.repo_name == repo_name)
592 592 q = q.options(joinedload(Repository.fork))\
593 593 .options(joinedload(Repository.user))\
594 594 .options(joinedload(Repository.group))
595 595 return q.scalar()
596 596
597 597
598 598 class RepoGroup(Base, BaseModel):
599 599 __tablename__ = 'groups'
600 600 __table_args__ = (
601 601 UniqueConstraint('group_name', 'group_parent_id'),
602 CheckConstraint('group_id != group_parent_id'),
603 602 {'extend_existing': True, 'mysql_engine': 'InnoDB',
604 603 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
605 604 )
606 605 __mapper_args__ = {'order_by': 'group_name'}
607 606
608 607 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
609 608 group_name = Column("group_name", String(255), nullable=False, unique=True, default=None)
610 609 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
611 610 group_description = Column("group_description", String(10000), nullable=True, unique=None, default=None)
612 611 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
613 612 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
614 613
615 614 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
616 615 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
617 616 parent_group = relationship('RepoGroup', remote_side=group_id)
618 617 user = relationship('User')
619 618
620 619 def __init__(self, group_name='', parent_group=None):
621 620 self.group_name = group_name
622 621 self.parent_group = parent_group
623 622
624 623 def __unicode__(self):
625 624 return u"<%s('id:%s:%s')>" % (self.__class__.__name__, self.group_id,
626 625 self.group_name)
627 626
628 627 @classmethod
629 628 def url_sep(cls):
630 629 return URL_SEP
631 630
632 631 @classmethod
633 632 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
634 633 if case_insensitive:
635 634 gr = cls.query()\
636 635 .filter(cls.group_name.ilike(group_name))
637 636 else:
638 637 gr = cls.query()\
639 638 .filter(cls.group_name == group_name)
640 639 if cache:
641 640 gr = gr.options(FromCache(
642 641 "sql_cache_short",
643 642 "get_group_%s" % _hash_key(group_name)
644 643 )
645 644 )
646 645 return gr.scalar()
647 646
648 647
649 648 class Permission(Base, BaseModel):
650 649 __tablename__ = 'permissions'
651 650 __table_args__ = (
652 651 Index('p_perm_name_idx', 'permission_name'),
653 652 {'extend_existing': True, 'mysql_engine': 'InnoDB',
654 653 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
655 654 )
656 655 PERMS = [
657 656 ('hg.admin', _('RhodeCode Administrator')),
658 657
659 658 ('repository.none', _('Repository no access')),
660 659 ('repository.read', _('Repository read access')),
661 660 ('repository.write', _('Repository write access')),
662 661 ('repository.admin', _('Repository admin access')),
663 662
664 663 ('group.none', _('Repository group no access')),
665 664 ('group.read', _('Repository group read access')),
666 665 ('group.write', _('Repository group write access')),
667 666 ('group.admin', _('Repository group admin access')),
668 667
669 668 ('usergroup.none', _('User group no access')),
670 669 ('usergroup.read', _('User group read access')),
671 670 ('usergroup.write', _('User group write access')),
672 671 ('usergroup.admin', _('User group admin access')),
673 672
674 673 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
675 674 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
676 675
677 676 ('hg.usergroup.create.false', _('User Group creation disabled')),
678 677 ('hg.usergroup.create.true', _('User Group creation enabled')),
679 678
680 679 ('hg.create.none', _('Repository creation disabled')),
681 680 ('hg.create.repository', _('Repository creation enabled')),
682 681
683 682 ('hg.fork.none', _('Repository forking disabled')),
684 683 ('hg.fork.repository', _('Repository forking enabled')),
685 684
686 685 ('hg.register.none', _('Registration disabled')),
687 686 ('hg.register.manual_activate', _('User Registration with manual account activation')),
688 687 ('hg.register.auto_activate', _('User Registration with automatic account activation')),
689 688
690 689 ('hg.extern_activate.manual', _('Manual activation of external account')),
691 690 ('hg.extern_activate.auto', _('Automatic activation of external account')),
692 691
693 692 ]
694 693
695 694 #definition of system default permissions for DEFAULT user
696 695 DEFAULT_USER_PERMISSIONS = [
697 696 'repository.read',
698 697 'group.read',
699 698 'usergroup.read',
700 699 'hg.create.repository',
701 700 'hg.fork.repository',
702 701 'hg.register.manual_activate',
703 702 'hg.extern_activate.auto',
704 703 ]
705 704
706 705 # defines which permissions are more important higher the more important
707 706 # Weight defines which permissions are more important.
708 707 # The higher number the more important.
709 708 PERM_WEIGHTS = {
710 709 'repository.none': 0,
711 710 'repository.read': 1,
712 711 'repository.write': 3,
713 712 'repository.admin': 4,
714 713
715 714 'group.none': 0,
716 715 'group.read': 1,
717 716 'group.write': 3,
718 717 'group.admin': 4,
719 718
720 719 'usergroup.none': 0,
721 720 'usergroup.read': 1,
722 721 'usergroup.write': 3,
723 722 'usergroup.admin': 4,
724 723 'hg.repogroup.create.false': 0,
725 724 'hg.repogroup.create.true': 1,
726 725
727 726 'hg.usergroup.create.false': 0,
728 727 'hg.usergroup.create.true': 1,
729 728
730 729 'hg.fork.none': 0,
731 730 'hg.fork.repository': 1,
732 731 'hg.create.none': 0,
733 732 'hg.create.repository': 1
734 733 }
735 734
736 735 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
737 736 permission_name = Column("permission_name", String(255), nullable=True, unique=None, default=None)
738 737 permission_longname = Column("permission_longname", String(255), nullable=True, unique=None, default=None)
739 738
740 739 def __unicode__(self):
741 740 return u"<%s('%s:%s')>" % (
742 741 self.__class__.__name__, self.permission_id, self.permission_name
743 742 )
744 743
745 744 @classmethod
746 745 def get_by_key(cls, key):
747 746 return cls.query().filter(cls.permission_name == key).scalar()
748 747
749 748
750 749 class UserRepoToPerm(Base, BaseModel):
751 750 __tablename__ = 'repo_to_perm'
752 751 __table_args__ = (
753 752 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
754 753 {'extend_existing': True, 'mysql_engine': 'InnoDB',
755 754 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
756 755 )
757 756 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
758 757 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
759 758 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
760 759 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
761 760
762 761 user = relationship('User')
763 762 repository = relationship('Repository')
764 763 permission = relationship('Permission')
765 764
766 765 def __unicode__(self):
767 766 return u'<%s => %s >' % (self.user, self.repository)
768 767
769 768
770 769 class UserUserGroupToPerm(Base, BaseModel):
771 770 __tablename__ = 'user_user_group_to_perm'
772 771 __table_args__ = (
773 772 UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
774 773 {'extend_existing': True, 'mysql_engine': 'InnoDB',
775 774 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
776 775 )
777 776 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
778 777 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
779 778 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
780 779 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
781 780
782 781 user = relationship('User')
783 782 user_group = relationship('UserGroup')
784 783 permission = relationship('Permission')
785 784
786 785 def __unicode__(self):
787 786 return u'<%s => %s >' % (self.user, self.user_group)
788 787
789 788
790 789 class UserToPerm(Base, BaseModel):
791 790 __tablename__ = 'user_to_perm'
792 791 __table_args__ = (
793 792 UniqueConstraint('user_id', 'permission_id'),
794 793 {'extend_existing': True, 'mysql_engine': 'InnoDB',
795 794 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
796 795 )
797 796 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
798 797 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
799 798 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
800 799
801 800 user = relationship('User')
802 801 permission = relationship('Permission', lazy='joined')
803 802
804 803 def __unicode__(self):
805 804 return u'<%s => %s >' % (self.user, self.permission)
806 805
807 806
808 807 class UserGroupRepoToPerm(Base, BaseModel):
809 808 __tablename__ = 'users_group_repo_to_perm'
810 809 __table_args__ = (
811 810 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
812 811 {'extend_existing': True, 'mysql_engine': 'InnoDB',
813 812 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
814 813 )
815 814 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
816 815 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
817 816 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
818 817 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
819 818
820 819 users_group = relationship('UserGroup')
821 820 permission = relationship('Permission')
822 821 repository = relationship('Repository')
823 822
824 823 def __unicode__(self):
825 824 return u'<UserGroupRepoToPerm:%s => %s >' % (self.users_group, self.repository)
826 825
827 826
828 827 class UserGroupUserGroupToPerm(Base, BaseModel):
829 828 __tablename__ = 'user_group_user_group_to_perm'
830 829 __table_args__ = (
831 830 UniqueConstraint('target_user_group_id', 'user_group_id', 'permission_id'),
832 831 CheckConstraint('target_user_group_id != user_group_id'),
833 832 {'extend_existing': True, 'mysql_engine': 'InnoDB',
834 833 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
835 834 )
836 835 user_group_user_group_to_perm_id = Column("user_group_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
837 836 target_user_group_id = Column("target_user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
838 837 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
839 838 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
840 839
841 840 target_user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
842 841 user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
843 842 permission = relationship('Permission')
844 843
845 844 def __unicode__(self):
846 845 return u'<UserGroupUserGroup:%s => %s >' % (self.target_user_group, self.user_group)
847 846
848 847
849 848 class UserGroupToPerm(Base, BaseModel):
850 849 __tablename__ = 'users_group_to_perm'
851 850 __table_args__ = (
852 851 UniqueConstraint('users_group_id', 'permission_id',),
853 852 {'extend_existing': True, 'mysql_engine': 'InnoDB',
854 853 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
855 854 )
856 855 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
857 856 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
858 857 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
859 858
860 859 users_group = relationship('UserGroup')
861 860 permission = relationship('Permission')
862 861
863 862
864 863 class UserRepoGroupToPerm(Base, BaseModel):
865 864 __tablename__ = 'user_repo_group_to_perm'
866 865 __table_args__ = (
867 866 UniqueConstraint('user_id', 'group_id', 'permission_id'),
868 867 {'extend_existing': True, 'mysql_engine': 'InnoDB',
869 868 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
870 869 )
871 870
872 871 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
873 872 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
874 873 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
875 874 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
876 875
877 876 user = relationship('User')
878 877 group = relationship('RepoGroup')
879 878 permission = relationship('Permission')
880 879
881 880
882 881 class UserGroupRepoGroupToPerm(Base, BaseModel):
883 882 __tablename__ = 'users_group_repo_group_to_perm'
884 883 __table_args__ = (
885 884 UniqueConstraint('users_group_id', 'group_id'),
886 885 {'extend_existing': True, 'mysql_engine': 'InnoDB',
887 886 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
888 887 )
889 888
890 889 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)
891 890 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
892 891 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
893 892 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
894 893
895 894 users_group = relationship('UserGroup')
896 895 permission = relationship('Permission')
897 896 group = relationship('RepoGroup')
898 897
899 898
900 899 class Statistics(Base, BaseModel):
901 900 __tablename__ = 'statistics'
902 901 __table_args__ = (
903 902 UniqueConstraint('repository_id'),
904 903 {'extend_existing': True, 'mysql_engine': 'InnoDB',
905 904 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
906 905 )
907 906 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
908 907 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
909 908 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
910 909 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
911 910 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
912 911 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
913 912
914 913 repository = relationship('Repository', single_parent=True)
915 914
916 915
917 916 class UserFollowing(Base, BaseModel):
918 917 __tablename__ = 'user_followings'
919 918 __table_args__ = (
920 919 UniqueConstraint('user_id', 'follows_repository_id'),
921 920 UniqueConstraint('user_id', 'follows_user_id'),
922 921 {'extend_existing': True, 'mysql_engine': 'InnoDB',
923 922 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
924 923 )
925 924
926 925 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
927 926 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
928 927 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
929 928 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
930 929 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
931 930
932 931 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
933 932
934 933 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
935 934 follows_repository = relationship('Repository', order_by='Repository.repo_name')
936 935
937 936
938 937 class CacheInvalidation(Base, BaseModel):
939 938 __tablename__ = 'cache_invalidation'
940 939 __table_args__ = (
941 940 UniqueConstraint('cache_key'),
942 941 Index('key_idx', 'cache_key'),
943 942 {'extend_existing': True, 'mysql_engine': 'InnoDB',
944 943 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
945 944 )
946 945 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
947 946 cache_key = Column("cache_key", String(255), nullable=True, unique=None, default=None)
948 947 cache_args = Column("cache_args", String(255), nullable=True, unique=None, default=None)
949 948 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
950 949
951 950 def __init__(self, cache_key, cache_args=''):
952 951 self.cache_key = cache_key
953 952 self.cache_args = cache_args
954 953 self.cache_active = False
955 954
956 955
957 956 class ChangesetComment(Base, BaseModel):
958 957 __tablename__ = 'changeset_comments'
959 958 __table_args__ = (
960 959 Index('cc_revision_idx', 'revision'),
961 960 {'extend_existing': True, 'mysql_engine': 'InnoDB',
962 961 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
963 962 )
964 963 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
965 964 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
966 965 revision = Column('revision', String(40), nullable=True)
967 966 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
968 967 line_no = Column('line_no', Unicode(10), nullable=True)
969 968 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
970 969 f_path = Column('f_path', Unicode(1000), nullable=True)
971 970 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
972 971 text = Column('text', UnicodeText().with_variant(UnicodeText(25000), 'mysql'), nullable=False)
973 972 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
974 973 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
975 974
976 975 author = relationship('User', lazy='joined')
977 976 repo = relationship('Repository')
978 977 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
979 978 pull_request = relationship('PullRequest', lazy='joined')
980 979
981 980
982 981 class ChangesetStatus(Base, BaseModel):
983 982 __tablename__ = 'changeset_statuses'
984 983 __table_args__ = (
985 984 Index('cs_revision_idx', 'revision'),
986 985 Index('cs_version_idx', 'version'),
987 986 UniqueConstraint('repo_id', 'revision', 'version'),
988 987 {'extend_existing': True, 'mysql_engine': 'InnoDB',
989 988 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
990 989 )
991 990 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
992 991 STATUS_APPROVED = 'approved'
993 992 STATUS_REJECTED = 'rejected'
994 993 STATUS_UNDER_REVIEW = 'under_review'
995 994
996 995 STATUSES = [
997 996 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
998 997 (STATUS_APPROVED, _("Approved")),
999 998 (STATUS_REJECTED, _("Rejected")),
1000 999 (STATUS_UNDER_REVIEW, _("Under Review")),
1001 1000 ]
1002 1001
1003 1002 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1004 1003 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1005 1004 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1006 1005 revision = Column('revision', String(40), nullable=False)
1007 1006 status = Column('status', String(128), nullable=False, default=DEFAULT)
1008 1007 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1009 1008 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1010 1009 version = Column('version', Integer(), nullable=False, default=0)
1011 1010 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1012 1011
1013 1012 author = relationship('User', lazy='joined')
1014 1013 repo = relationship('Repository')
1015 1014 comment = relationship('ChangesetComment', lazy='joined')
1016 1015 pull_request = relationship('PullRequest', lazy='joined')
1017 1016
1018 1017
1019 1018
1020 1019 class PullRequest(Base, BaseModel):
1021 1020 __tablename__ = 'pull_requests'
1022 1021 __table_args__ = (
1023 1022 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1024 1023 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1025 1024 )
1026 1025
1027 1026 STATUS_NEW = u'new'
1028 1027 STATUS_OPEN = u'open'
1029 1028 STATUS_CLOSED = u'closed'
1030 1029
1031 1030 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1032 1031 title = Column('title', Unicode(256), nullable=True)
1033 1032 description = Column('description', UnicodeText().with_variant(UnicodeText(10240), 'mysql'), nullable=True)
1034 1033 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1035 1034 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1036 1035 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1037 1036 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1038 1037 _revisions = Column('revisions', UnicodeText().with_variant(UnicodeText(20500), 'mysql'))
1039 1038 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1040 1039 org_ref = Column('org_ref', Unicode(256), nullable=False)
1041 1040 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1042 1041 other_ref = Column('other_ref', Unicode(256), nullable=False)
1043 1042
1044 1043 author = relationship('User', lazy='joined')
1045 1044 reviewers = relationship('PullRequestReviewers',
1046 1045 cascade="all, delete, delete-orphan")
1047 1046 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1048 1047 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1049 1048 statuses = relationship('ChangesetStatus')
1050 1049 comments = relationship('ChangesetComment',
1051 1050 cascade="all, delete, delete-orphan")
1052 1051
1053 1052
1054 1053 class PullRequestReviewers(Base, BaseModel):
1055 1054 __tablename__ = 'pull_request_reviewers'
1056 1055 __table_args__ = (
1057 1056 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1058 1057 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1059 1058 )
1060 1059
1061 1060 def __init__(self, user=None, pull_request=None):
1062 1061 self.user = user
1063 1062 self.pull_request = pull_request
1064 1063
1065 1064 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1066 1065 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1067 1066 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1068 1067
1069 1068 user = relationship('User')
1070 1069 pull_request = relationship('PullRequest')
1071 1070
1072 1071
1073 1072 class Notification(Base, BaseModel):
1074 1073 __tablename__ = 'notifications'
1075 1074 __table_args__ = (
1076 1075 Index('notification_type_idx', 'type'),
1077 1076 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1078 1077 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1079 1078 )
1080 1079
1081 1080 TYPE_CHANGESET_COMMENT = u'cs_comment'
1082 1081 TYPE_MESSAGE = u'message'
1083 1082 TYPE_MENTION = u'mention'
1084 1083 TYPE_REGISTRATION = u'registration'
1085 1084 TYPE_PULL_REQUEST = u'pull_request'
1086 1085 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1087 1086
1088 1087 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1089 1088 subject = Column('subject', Unicode(512), nullable=True)
1090 1089 body = Column('body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True)
1091 1090 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1092 1091 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1093 1092 type_ = Column('type', Unicode(256))
1094 1093
1095 1094 created_by_user = relationship('User')
1096 1095 notifications_to_users = relationship('UserNotification', lazy='joined',
1097 1096 cascade="all, delete, delete-orphan")
1098 1097
1099 1098
1100 1099 class UserNotification(Base, BaseModel):
1101 1100 __tablename__ = 'user_to_notification'
1102 1101 __table_args__ = (
1103 1102 UniqueConstraint('user_id', 'notification_id'),
1104 1103 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1105 1104 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1106 1105 )
1107 1106 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1108 1107 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1109 1108 read = Column('read', Boolean, default=False)
1110 1109 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1111 1110
1112 1111 user = relationship('User', lazy="joined")
1113 1112 notification = relationship('Notification', lazy="joined",
1114 1113 order_by=lambda: Notification.created_on.desc(),)
1115 1114
1116 1115
1117 1116 class Gist(Base, BaseModel):
1118 1117 __tablename__ = 'gists'
1119 1118 __table_args__ = (
1120 1119 Index('g_gist_access_id_idx', 'gist_access_id'),
1121 1120 Index('g_created_on_idx', 'created_on'),
1122 1121 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1123 1122 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1124 1123 )
1125 1124 GIST_PUBLIC = u'public'
1126 1125 GIST_PRIVATE = u'private'
1127 1126
1128 1127 gist_id = Column('gist_id', Integer(), primary_key=True)
1129 1128 gist_access_id = Column('gist_access_id', Unicode(250))
1130 1129 gist_description = Column('gist_description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
1131 1130 gist_owner = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=True)
1132 1131 gist_expires = Column('gist_expires', Float(53), nullable=False)
1133 1132 gist_type = Column('gist_type', Unicode(128), nullable=False)
1134 1133 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1135 1134 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1136 1135
1137 1136 owner = relationship('User')
1138 1137
1139 1138
1140 1139 class DbMigrateVersion(Base, BaseModel):
1141 1140 __tablename__ = 'db_migrate_version'
1142 1141 __table_args__ = (
1143 1142 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1144 1143 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1145 1144 )
1146 1145 repository_id = Column('repository_id', String(250), primary_key=True)
1147 1146 repository_path = Column('repository_path', Text)
1148 1147 version = Column('version', Integer)
@@ -1,1171 +1,1170 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import os
22 22 import time
23 23 import logging
24 24 import datetime
25 25 import traceback
26 26 import hashlib
27 27 import collections
28 28 import functools
29 29
30 30 from sqlalchemy import *
31 31 from sqlalchemy.ext.hybrid import hybrid_property
32 32 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
33 33 from sqlalchemy.exc import DatabaseError
34 34 from beaker.cache import cache_region, region_invalidate
35 35 from webob.exc import HTTPNotFound
36 36
37 37 from rhodecode.translation import _
38 38
39 39 from rhodecode.lib.vcs import get_backend
40 40 from rhodecode.lib.vcs.utils.helpers import get_scm
41 41 from rhodecode.lib.vcs.exceptions import VCSError
42 42 from zope.cachedescriptors.property import Lazy as LazyProperty
43 43 from rhodecode.lib.vcs.backends.base import EmptyCommit
44 44
45 45 from rhodecode.lib.utils2 import str2bool, safe_str, get_commit_safe, \
46 46 safe_unicode, remove_prefix, time_to_datetime, aslist, Optional, safe_int
47 47 from rhodecode.lib.ext_json import json
48 48 from rhodecode.lib.caching_query import FromCache
49 49
50 50 from rhodecode.model.meta import Base, Session
51 51
52 52 URL_SEP = '/'
53 53 log = logging.getLogger(__name__)
54 54
55 55 #==============================================================================
56 56 # BASE CLASSES
57 57 #==============================================================================
58 58
59 59 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
60 60
61 61
62 62 class BaseModel(object):
63 63 """
64 64 Base Model for all classes
65 65 """
66 66
67 67 @classmethod
68 68 def _get_keys(cls):
69 69 """return column names for this model """
70 70 return class_mapper(cls).c.keys()
71 71
72 72 def get_dict(self):
73 73 """
74 74 return dict with keys and values corresponding
75 75 to this model data """
76 76
77 77 d = {}
78 78 for k in self._get_keys():
79 79 d[k] = getattr(self, k)
80 80
81 81 # also use __json__() if present to get additional fields
82 82 _json_attr = getattr(self, '__json__', None)
83 83 if _json_attr:
84 84 # update with attributes from __json__
85 85 if callable(_json_attr):
86 86 _json_attr = _json_attr()
87 87 for k, val in _json_attr.iteritems():
88 88 d[k] = val
89 89 return d
90 90
91 91 def get_appstruct(self):
92 92 """return list with keys and values tupples corresponding
93 93 to this model data """
94 94
95 95 l = []
96 96 for k in self._get_keys():
97 97 l.append((k, getattr(self, k),))
98 98 return l
99 99
100 100 def populate_obj(self, populate_dict):
101 101 """populate model with data from given populate_dict"""
102 102
103 103 for k in self._get_keys():
104 104 if k in populate_dict:
105 105 setattr(self, k, populate_dict[k])
106 106
107 107 @classmethod
108 108 def query(cls):
109 109 return Session().query(cls)
110 110
111 111 @classmethod
112 112 def get(cls, id_):
113 113 if id_:
114 114 return cls.query().get(id_)
115 115
116 116 @classmethod
117 117 def get_or_404(cls, id_):
118 118 try:
119 119 id_ = int(id_)
120 120 except (TypeError, ValueError):
121 121 raise HTTPNotFound
122 122
123 123 res = cls.query().get(id_)
124 124 if not res:
125 125 raise HTTPNotFound
126 126 return res
127 127
128 128 @classmethod
129 129 def getAll(cls):
130 130 # deprecated and left for backward compatibility
131 131 return cls.get_all()
132 132
133 133 @classmethod
134 134 def get_all(cls):
135 135 return cls.query().all()
136 136
137 137 @classmethod
138 138 def delete(cls, id_):
139 139 obj = cls.query().get(id_)
140 140 Session().delete(obj)
141 141
142 142 def __repr__(self):
143 143 if hasattr(self, '__unicode__'):
144 144 # python repr needs to return str
145 145 return safe_str(self.__unicode__())
146 146 return '<DB:%s>' % (self.__class__.__name__)
147 147
148 148
149 149 class RhodeCodeSetting(Base, BaseModel):
150 150 SETTINGS_TYPES = {
151 151 'str': safe_str,
152 152 'int': safe_int,
153 153 'unicode': safe_unicode,
154 154 'bool': str2bool,
155 155 'list': functools.partial(aslist, sep=',')
156 156 }
157 157 __tablename__ = 'rhodecode_settings'
158 158 __table_args__ = (
159 159 UniqueConstraint('app_settings_name'),
160 160 {'extend_existing': True, 'mysql_engine': 'InnoDB',
161 161 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
162 162 )
163 163 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
164 164 app_settings_name = Column("app_settings_name", String(255), nullable=True, unique=None, default=None)
165 165 _app_settings_value = Column("app_settings_value", String(4096), nullable=True, unique=None, default=None)
166 166 _app_settings_type = Column("app_settings_type", String(255), nullable=True, unique=None, default=None)
167 167
168 168 def __init__(self, key='', val='', type='unicode'):
169 169 self.app_settings_name = key
170 170 self.app_settings_value = val
171 171 self.app_settings_type = type
172 172
173 173 @validates('_app_settings_value')
174 174 def validate_settings_value(self, key, val):
175 175 assert type(val) == unicode
176 176 return val
177 177
178 178 @hybrid_property
179 179 def app_settings_value(self):
180 180 v = self._app_settings_value
181 181 _type = self.app_settings_type
182 182 converter = self.SETTINGS_TYPES.get(_type) or self.SETTINGS_TYPES['unicode']
183 183 return converter(v)
184 184
185 185 @app_settings_value.setter
186 186 def app_settings_value(self, val):
187 187 """
188 188 Setter that will always make sure we use unicode in app_settings_value
189 189
190 190 :param val:
191 191 """
192 192 self._app_settings_value = safe_unicode(val)
193 193
194 194 @hybrid_property
195 195 def app_settings_type(self):
196 196 return self._app_settings_type
197 197
198 198 @app_settings_type.setter
199 199 def app_settings_type(self, val):
200 200 if val not in self.SETTINGS_TYPES:
201 201 raise Exception('type must be one of %s got %s'
202 202 % (self.SETTINGS_TYPES.keys(), val))
203 203 self._app_settings_type = val
204 204
205 205 def __unicode__(self):
206 206 return u"<%s('%s:%s[%s]')>" % (
207 207 self.__class__.__name__,
208 208 self.app_settings_name, self.app_settings_value, self.app_settings_type
209 209 )
210 210
211 211
212 212 class RhodeCodeUi(Base, BaseModel):
213 213 __tablename__ = 'rhodecode_ui'
214 214 __table_args__ = (
215 215 UniqueConstraint('ui_key'),
216 216 {'extend_existing': True, 'mysql_engine': 'InnoDB',
217 217 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
218 218 )
219 219
220 220 HOOK_REPO_SIZE = 'changegroup.repo_size'
221 221 HOOK_PUSH = 'changegroup.push_logger'
222 222 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
223 223 HOOK_PULL = 'outgoing.pull_logger'
224 224 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
225 225
226 226 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
227 227 ui_section = Column("ui_section", String(255), nullable=True, unique=None, default=None)
228 228 ui_key = Column("ui_key", String(255), nullable=True, unique=None, default=None)
229 229 ui_value = Column("ui_value", String(255), nullable=True, unique=None, default=None)
230 230 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
231 231
232 232
233 233
234 234 class User(Base, BaseModel):
235 235 __tablename__ = 'users'
236 236 __table_args__ = (
237 237 UniqueConstraint('username'), UniqueConstraint('email'),
238 238 Index('u_username_idx', 'username'),
239 239 Index('u_email_idx', 'email'),
240 240 {'extend_existing': True, 'mysql_engine': 'InnoDB',
241 241 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
242 242 )
243 243 DEFAULT_USER = 'default'
244 244
245 245 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
246 246 username = Column("username", String(255), nullable=True, unique=None, default=None)
247 247 password = Column("password", String(255), nullable=True, unique=None, default=None)
248 248 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
249 249 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
250 250 name = Column("firstname", String(255), nullable=True, unique=None, default=None)
251 251 lastname = Column("lastname", String(255), nullable=True, unique=None, default=None)
252 252 _email = Column("email", String(255), nullable=True, unique=None, default=None)
253 253 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
254 254 extern_type = Column("extern_type", String(255), nullable=True, unique=None, default=None)
255 255 extern_name = Column("extern_name", String(255), nullable=True, unique=None, default=None)
256 256 #for migration reasons, this is going to be later deleted
257 257 ldap_dn = Column("ldap_dn", String(255), nullable=True, unique=None, default=None)
258 258
259 259 api_key = Column("api_key", String(255), nullable=True, unique=None, default=None)
260 260 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
261 261 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
262 262
263 263 user_log = relationship('UserLog')
264 264 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
265 265
266 266 repositories = relationship('Repository')
267 267 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
268 268 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
269 269
270 270 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
271 271 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
272 272
273 273 group_member = relationship('UserGroupMember', cascade='all')
274 274
275 275 notifications = relationship('UserNotification', cascade='all')
276 276 # notifications assigned to this user
277 277 user_created_notifications = relationship('Notification', cascade='all')
278 278 # comments created by this user
279 279 user_comments = relationship('ChangesetComment', cascade='all')
280 280 user_emails = relationship('UserEmailMap', cascade='all')
281 281
282 282 @hybrid_property
283 283 def email(self):
284 284 return self._email
285 285
286 286 @email.setter
287 287 def email(self, val):
288 288 self._email = val.lower() if val else None
289 289
290 290 @property
291 291 def firstname(self):
292 292 # alias for future
293 293 return self.name
294 294
295 295 @property
296 296 def username_and_name(self):
297 297 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
298 298
299 299 @property
300 300 def full_name(self):
301 301 return '%s %s' % (self.firstname, self.lastname)
302 302
303 303 @property
304 304 def full_contact(self):
305 305 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
306 306
307 307 @property
308 308 def short_contact(self):
309 309 return '%s %s' % (self.firstname, self.lastname)
310 310
311 311 @property
312 312 def is_admin(self):
313 313 return self.admin
314 314
315 315 @classmethod
316 316 def get_by_username(cls, username, case_insensitive=False, cache=False):
317 317 if case_insensitive:
318 318 q = cls.query().filter(cls.username.ilike(username))
319 319 else:
320 320 q = cls.query().filter(cls.username == username)
321 321
322 322 if cache:
323 323 q = q.options(FromCache(
324 324 "sql_cache_short",
325 325 "get_user_%s" % _hash_key(username)
326 326 )
327 327 )
328 328 return q.scalar()
329 329
330 330 @classmethod
331 331 def get_by_auth_token(cls, auth_token, cache=False):
332 332 q = cls.query().filter(cls.api_key == auth_token)
333 333
334 334 if cache:
335 335 q = q.options(FromCache("sql_cache_short",
336 336 "get_auth_token_%s" % auth_token))
337 337 return q.scalar()
338 338
339 339 @classmethod
340 340 def get_by_email(cls, email, case_insensitive=False, cache=False):
341 341 if case_insensitive:
342 342 q = cls.query().filter(cls.email.ilike(email))
343 343 else:
344 344 q = cls.query().filter(cls.email == email)
345 345
346 346 if cache:
347 347 q = q.options(FromCache("sql_cache_short",
348 348 "get_email_key_%s" % email))
349 349
350 350 ret = q.scalar()
351 351 if ret is None:
352 352 q = UserEmailMap.query()
353 353 # try fetching in alternate email map
354 354 if case_insensitive:
355 355 q = q.filter(UserEmailMap.email.ilike(email))
356 356 else:
357 357 q = q.filter(UserEmailMap.email == email)
358 358 q = q.options(joinedload(UserEmailMap.user))
359 359 if cache:
360 360 q = q.options(FromCache("sql_cache_short",
361 361 "get_email_map_key_%s" % email))
362 362 ret = getattr(q.scalar(), 'user', None)
363 363
364 364 return ret
365 365
366 366 @classmethod
367 367 def get_first_admin(cls):
368 368 user = User.query().filter(User.admin == True).first()
369 369 if user is None:
370 370 raise Exception('Missing administrative account!')
371 371 return user
372 372
373 373 @classmethod
374 374 def get_default_user(cls, cache=False):
375 375 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
376 376 if user is None:
377 377 raise Exception('Missing default account!')
378 378 return user
379 379
380 380
381 381
382 382
383 383 class UserEmailMap(Base, BaseModel):
384 384 __tablename__ = 'user_email_map'
385 385 __table_args__ = (
386 386 Index('uem_email_idx', 'email'),
387 387 UniqueConstraint('email'),
388 388 {'extend_existing': True, 'mysql_engine': 'InnoDB',
389 389 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
390 390 )
391 391 __mapper_args__ = {}
392 392
393 393 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
394 394 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
395 395 _email = Column("email", String(255), nullable=True, unique=False, default=None)
396 396 user = relationship('User', lazy='joined')
397 397
398 398 @validates('_email')
399 399 def validate_email(self, key, email):
400 400 # check if this email is not main one
401 401 main_email = Session().query(User).filter(User.email == email).scalar()
402 402 if main_email is not None:
403 403 raise AttributeError('email %s is present is user table' % email)
404 404 return email
405 405
406 406 @hybrid_property
407 407 def email(self):
408 408 return self._email
409 409
410 410 @email.setter
411 411 def email(self, val):
412 412 self._email = val.lower() if val else None
413 413
414 414
415 415 class UserIpMap(Base, BaseModel):
416 416 __tablename__ = 'user_ip_map'
417 417 __table_args__ = (
418 418 UniqueConstraint('user_id', 'ip_addr'),
419 419 {'extend_existing': True, 'mysql_engine': 'InnoDB',
420 420 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
421 421 )
422 422 __mapper_args__ = {}
423 423
424 424 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
425 425 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
426 426 ip_addr = Column("ip_addr", String(255), nullable=True, unique=False, default=None)
427 427 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
428 428 user = relationship('User', lazy='joined')
429 429
430 430
431 431 class UserLog(Base, BaseModel):
432 432 __tablename__ = 'user_logs'
433 433 __table_args__ = (
434 434 {'extend_existing': True, 'mysql_engine': 'InnoDB',
435 435 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
436 436 )
437 437 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
438 438 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
439 439 username = Column("username", String(255), nullable=True, unique=None, default=None)
440 440 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
441 441 repository_name = Column("repository_name", String(255), nullable=True, unique=None, default=None)
442 442 user_ip = Column("user_ip", String(255), nullable=True, unique=None, default=None)
443 443 action = Column("action", String(1200000), nullable=True, unique=None, default=None)
444 444 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
445 445
446 446 def __unicode__(self):
447 447 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
448 448 self.repository_name,
449 449 self.action)
450 450
451 451 user = relationship('User')
452 452 repository = relationship('Repository', cascade='')
453 453
454 454
455 455 class UserGroup(Base, BaseModel):
456 456 __tablename__ = 'users_groups'
457 457 __table_args__ = (
458 458 {'extend_existing': True, 'mysql_engine': 'InnoDB',
459 459 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
460 460 )
461 461
462 462 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
463 463 users_group_name = Column("users_group_name", String(255), nullable=False, unique=True, default=None)
464 464 user_group_description = Column("user_group_description", String(10000), nullable=True, unique=None, default=None)
465 465 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
466 466 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
467 467 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
468 468 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
469 469
470 470 # don't trigger lazy load for migrations
471 471 #members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
472 472 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
473 473 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
474 474 users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
475 475 user_user_group_to_perm = relationship('UserUserGroupToPerm', cascade='all')
476 476 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm ', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
477 477
478 478 user = relationship('User')
479 479
480 480 def __unicode__(self):
481 481 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
482 482 self.users_group_id,
483 483 self.users_group_name)
484 484
485 485 @classmethod
486 486 def get_by_group_name(cls, group_name, cache=False,
487 487 case_insensitive=False):
488 488 if case_insensitive:
489 489 q = cls.query().filter(cls.users_group_name.ilike(group_name))
490 490 else:
491 491 q = cls.query().filter(cls.users_group_name == group_name)
492 492 if cache:
493 493 q = q.options(FromCache(
494 494 "sql_cache_short",
495 495 "get_user_%s" % _hash_key(group_name)
496 496 )
497 497 )
498 498 return q.scalar()
499 499
500 500 @classmethod
501 501 def get(cls, user_group_id, cache=False):
502 502 user_group = cls.query()
503 503 if cache:
504 504 user_group = user_group.options(FromCache("sql_cache_short",
505 505 "get_users_group_%s" % user_group_id))
506 506 return user_group.get(user_group_id)
507 507
508 508
509 509 class UserGroupMember(Base, BaseModel):
510 510 __tablename__ = 'users_groups_members'
511 511 __table_args__ = (
512 512 {'extend_existing': True, 'mysql_engine': 'InnoDB',
513 513 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
514 514 )
515 515
516 516 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
517 517 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
518 518 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
519 519
520 520 user = relationship('User', lazy='joined')
521 521 users_group = relationship('UserGroup')
522 522
523 523 def __init__(self, gr_id='', u_id=''):
524 524 self.users_group_id = gr_id
525 525 self.user_id = u_id
526 526
527 527
528 528 class RepositoryField(Base, BaseModel):
529 529 __tablename__ = 'repositories_fields'
530 530 __table_args__ = (
531 531 UniqueConstraint('repository_id', 'field_key'), # no-multi field
532 532 {'extend_existing': True, 'mysql_engine': 'InnoDB',
533 533 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
534 534 )
535 535 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
536 536
537 537 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
538 538 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
539 539 field_key = Column("field_key", String(250))
540 540 field_label = Column("field_label", String(1024), nullable=False)
541 541 field_value = Column("field_value", String(10000), nullable=False)
542 542 field_desc = Column("field_desc", String(1024), nullable=False)
543 543 field_type = Column("field_type", String(256), nullable=False, unique=None)
544 544 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
545 545
546 546 repository = relationship('Repository')
547 547
548 548 @classmethod
549 549 def get_by_key_name(cls, key, repo):
550 550 row = cls.query()\
551 551 .filter(cls.repository == repo)\
552 552 .filter(cls.field_key == key).scalar()
553 553 return row
554 554
555 555
556 556 class Repository(Base, BaseModel):
557 557 __tablename__ = 'repositories'
558 558 __table_args__ = (
559 559 UniqueConstraint('repo_name'),
560 560 Index('r_repo_name_idx', 'repo_name'),
561 561 {'extend_existing': True, 'mysql_engine': 'InnoDB',
562 562 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
563 563 )
564 564
565 565 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
566 566 repo_name = Column("repo_name", String(255), nullable=False, unique=True, default=None)
567 567 clone_uri = Column("clone_uri", String(255), nullable=True, unique=False, default=None)
568 568 repo_type = Column("repo_type", String(255), nullable=False, unique=False, default=None)
569 569 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
570 570 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
571 571 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
572 572 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
573 573 description = Column("description", String(10000), nullable=True, unique=None, default=None)
574 574 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
575 575 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
576 576 landing_rev = Column("landing_revision", String(255), nullable=False, unique=False, default=None)
577 577 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
578 578 _locked = Column("locked", String(255), nullable=True, unique=False, default=None)
579 579 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
580 580
581 581 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
582 582 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
583 583
584 584 user = relationship('User')
585 585 fork = relationship('Repository', remote_side=repo_id)
586 586 group = relationship('RepoGroup')
587 587 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
588 588 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
589 589 stats = relationship('Statistics', cascade='all', uselist=False)
590 590
591 591 followers = relationship('UserFollowing',
592 592 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
593 593 cascade='all')
594 594 extra_fields = relationship('RepositoryField',
595 595 cascade="all, delete, delete-orphan")
596 596
597 597 logs = relationship('UserLog')
598 598 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
599 599
600 600 pull_requests_org = relationship('PullRequest',
601 601 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
602 602 cascade="all, delete, delete-orphan")
603 603
604 604 pull_requests_other = relationship('PullRequest',
605 605 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
606 606 cascade="all, delete, delete-orphan")
607 607
608 608 def __unicode__(self):
609 609 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
610 610 safe_unicode(self.repo_name))
611 611
612 612 @classmethod
613 613 def get_by_repo_name(cls, repo_name):
614 614 q = Session().query(cls).filter(cls.repo_name == repo_name)
615 615 q = q.options(joinedload(Repository.fork))\
616 616 .options(joinedload(Repository.user))\
617 617 .options(joinedload(Repository.group))
618 618 return q.scalar()
619 619
620 620
621 621 class RepoGroup(Base, BaseModel):
622 622 __tablename__ = 'groups'
623 623 __table_args__ = (
624 624 UniqueConstraint('group_name', 'group_parent_id'),
625 CheckConstraint('group_id != group_parent_id'),
626 625 {'extend_existing': True, 'mysql_engine': 'InnoDB',
627 626 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
628 627 )
629 628 __mapper_args__ = {'order_by': 'group_name'}
630 629
631 630 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
632 631 group_name = Column("group_name", String(255), nullable=False, unique=True, default=None)
633 632 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
634 633 group_description = Column("group_description", String(10000), nullable=True, unique=None, default=None)
635 634 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
636 635 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
637 636
638 637 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
639 638 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
640 639 parent_group = relationship('RepoGroup', remote_side=group_id)
641 640 user = relationship('User')
642 641
643 642 def __init__(self, group_name='', parent_group=None):
644 643 self.group_name = group_name
645 644 self.parent_group = parent_group
646 645
647 646 def __unicode__(self):
648 647 return u"<%s('id:%s:%s')>" % (self.__class__.__name__, self.group_id,
649 648 self.group_name)
650 649
651 650 @classmethod
652 651 def url_sep(cls):
653 652 return URL_SEP
654 653
655 654 @classmethod
656 655 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
657 656 if case_insensitive:
658 657 gr = cls.query()\
659 658 .filter(cls.group_name.ilike(group_name))
660 659 else:
661 660 gr = cls.query()\
662 661 .filter(cls.group_name == group_name)
663 662 if cache:
664 663 gr = gr.options(FromCache(
665 664 "sql_cache_short",
666 665 "get_group_%s" % _hash_key(group_name)
667 666 )
668 667 )
669 668 return gr.scalar()
670 669
671 670
672 671 class Permission(Base, BaseModel):
673 672 __tablename__ = 'permissions'
674 673 __table_args__ = (
675 674 Index('p_perm_name_idx', 'permission_name'),
676 675 {'extend_existing': True, 'mysql_engine': 'InnoDB',
677 676 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
678 677 )
679 678 PERMS = [
680 679 ('hg.admin', _('RhodeCode Administrator')),
681 680
682 681 ('repository.none', _('Repository no access')),
683 682 ('repository.read', _('Repository read access')),
684 683 ('repository.write', _('Repository write access')),
685 684 ('repository.admin', _('Repository admin access')),
686 685
687 686 ('group.none', _('Repository group no access')),
688 687 ('group.read', _('Repository group read access')),
689 688 ('group.write', _('Repository group write access')),
690 689 ('group.admin', _('Repository group admin access')),
691 690
692 691 ('usergroup.none', _('User group no access')),
693 692 ('usergroup.read', _('User group read access')),
694 693 ('usergroup.write', _('User group write access')),
695 694 ('usergroup.admin', _('User group admin access')),
696 695
697 696 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
698 697 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
699 698
700 699 ('hg.usergroup.create.false', _('User Group creation disabled')),
701 700 ('hg.usergroup.create.true', _('User Group creation enabled')),
702 701
703 702 ('hg.create.none', _('Repository creation disabled')),
704 703 ('hg.create.repository', _('Repository creation enabled')),
705 704
706 705 ('hg.fork.none', _('Repository forking disabled')),
707 706 ('hg.fork.repository', _('Repository forking enabled')),
708 707
709 708 ('hg.register.none', _('Registration disabled')),
710 709 ('hg.register.manual_activate', _('User Registration with manual account activation')),
711 710 ('hg.register.auto_activate', _('User Registration with automatic account activation')),
712 711
713 712 ('hg.extern_activate.manual', _('Manual activation of external account')),
714 713 ('hg.extern_activate.auto', _('Automatic activation of external account')),
715 714
716 715 ]
717 716
718 717 #definition of system default permissions for DEFAULT user
719 718 DEFAULT_USER_PERMISSIONS = [
720 719 'repository.read',
721 720 'group.read',
722 721 'usergroup.read',
723 722 'hg.create.repository',
724 723 'hg.fork.repository',
725 724 'hg.register.manual_activate',
726 725 'hg.extern_activate.auto',
727 726 ]
728 727
729 728 # defines which permissions are more important higher the more important
730 729 # Weight defines which permissions are more important.
731 730 # The higher number the more important.
732 731 PERM_WEIGHTS = {
733 732 'repository.none': 0,
734 733 'repository.read': 1,
735 734 'repository.write': 3,
736 735 'repository.admin': 4,
737 736
738 737 'group.none': 0,
739 738 'group.read': 1,
740 739 'group.write': 3,
741 740 'group.admin': 4,
742 741
743 742 'usergroup.none': 0,
744 743 'usergroup.read': 1,
745 744 'usergroup.write': 3,
746 745 'usergroup.admin': 4,
747 746 'hg.repogroup.create.false': 0,
748 747 'hg.repogroup.create.true': 1,
749 748
750 749 'hg.usergroup.create.false': 0,
751 750 'hg.usergroup.create.true': 1,
752 751
753 752 'hg.fork.none': 0,
754 753 'hg.fork.repository': 1,
755 754 'hg.create.none': 0,
756 755 'hg.create.repository': 1
757 756 }
758 757
759 758 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
760 759 permission_name = Column("permission_name", String(255), nullable=True, unique=None, default=None)
761 760 permission_longname = Column("permission_longname", String(255), nullable=True, unique=None, default=None)
762 761
763 762 def __unicode__(self):
764 763 return u"<%s('%s:%s')>" % (
765 764 self.__class__.__name__, self.permission_id, self.permission_name
766 765 )
767 766
768 767 @classmethod
769 768 def get_by_key(cls, key):
770 769 return cls.query().filter(cls.permission_name == key).scalar()
771 770
772 771
773 772 class UserRepoToPerm(Base, BaseModel):
774 773 __tablename__ = 'repo_to_perm'
775 774 __table_args__ = (
776 775 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
777 776 {'extend_existing': True, 'mysql_engine': 'InnoDB',
778 777 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
779 778 )
780 779 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
781 780 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
782 781 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
783 782 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
784 783
785 784 user = relationship('User')
786 785 repository = relationship('Repository')
787 786 permission = relationship('Permission')
788 787
789 788 def __unicode__(self):
790 789 return u'<%s => %s >' % (self.user, self.repository)
791 790
792 791
793 792 class UserUserGroupToPerm(Base, BaseModel):
794 793 __tablename__ = 'user_user_group_to_perm'
795 794 __table_args__ = (
796 795 UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
797 796 {'extend_existing': True, 'mysql_engine': 'InnoDB',
798 797 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
799 798 )
800 799 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
801 800 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
802 801 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
803 802 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
804 803
805 804 user = relationship('User')
806 805 user_group = relationship('UserGroup')
807 806 permission = relationship('Permission')
808 807
809 808 def __unicode__(self):
810 809 return u'<%s => %s >' % (self.user, self.user_group)
811 810
812 811
813 812 class UserToPerm(Base, BaseModel):
814 813 __tablename__ = 'user_to_perm'
815 814 __table_args__ = (
816 815 UniqueConstraint('user_id', 'permission_id'),
817 816 {'extend_existing': True, 'mysql_engine': 'InnoDB',
818 817 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
819 818 )
820 819 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
821 820 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
822 821 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
823 822
824 823 user = relationship('User')
825 824 permission = relationship('Permission', lazy='joined')
826 825
827 826 def __unicode__(self):
828 827 return u'<%s => %s >' % (self.user, self.permission)
829 828
830 829
831 830 class UserGroupRepoToPerm(Base, BaseModel):
832 831 __tablename__ = 'users_group_repo_to_perm'
833 832 __table_args__ = (
834 833 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
835 834 {'extend_existing': True, 'mysql_engine': 'InnoDB',
836 835 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
837 836 )
838 837 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
839 838 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
840 839 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
841 840 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
842 841
843 842 users_group = relationship('UserGroup')
844 843 permission = relationship('Permission')
845 844 repository = relationship('Repository')
846 845
847 846 def __unicode__(self):
848 847 return u'<UserGroupRepoToPerm:%s => %s >' % (self.users_group, self.repository)
849 848
850 849
851 850 class UserGroupUserGroupToPerm(Base, BaseModel):
852 851 __tablename__ = 'user_group_user_group_to_perm'
853 852 __table_args__ = (
854 853 UniqueConstraint('target_user_group_id', 'user_group_id', 'permission_id'),
855 854 CheckConstraint('target_user_group_id != user_group_id'),
856 855 {'extend_existing': True, 'mysql_engine': 'InnoDB',
857 856 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
858 857 )
859 858 user_group_user_group_to_perm_id = Column("user_group_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
860 859 target_user_group_id = Column("target_user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
861 860 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
862 861 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
863 862
864 863 target_user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
865 864 user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
866 865 permission = relationship('Permission')
867 866
868 867 def __unicode__(self):
869 868 return u'<UserGroupUserGroup:%s => %s >' % (self.target_user_group, self.user_group)
870 869
871 870
872 871 class UserGroupToPerm(Base, BaseModel):
873 872 __tablename__ = 'users_group_to_perm'
874 873 __table_args__ = (
875 874 UniqueConstraint('users_group_id', 'permission_id',),
876 875 {'extend_existing': True, 'mysql_engine': 'InnoDB',
877 876 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
878 877 )
879 878 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
880 879 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
881 880 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
882 881
883 882 users_group = relationship('UserGroup')
884 883 permission = relationship('Permission')
885 884
886 885
887 886 class UserRepoGroupToPerm(Base, BaseModel):
888 887 __tablename__ = 'user_repo_group_to_perm'
889 888 __table_args__ = (
890 889 UniqueConstraint('user_id', 'group_id', 'permission_id'),
891 890 {'extend_existing': True, 'mysql_engine': 'InnoDB',
892 891 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
893 892 )
894 893
895 894 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
896 895 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
897 896 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
898 897 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
899 898
900 899 user = relationship('User')
901 900 group = relationship('RepoGroup')
902 901 permission = relationship('Permission')
903 902
904 903
905 904 class UserGroupRepoGroupToPerm(Base, BaseModel):
906 905 __tablename__ = 'users_group_repo_group_to_perm'
907 906 __table_args__ = (
908 907 UniqueConstraint('users_group_id', 'group_id'),
909 908 {'extend_existing': True, 'mysql_engine': 'InnoDB',
910 909 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
911 910 )
912 911
913 912 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)
914 913 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
915 914 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
916 915 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
917 916
918 917 users_group = relationship('UserGroup')
919 918 permission = relationship('Permission')
920 919 group = relationship('RepoGroup')
921 920
922 921
923 922 class Statistics(Base, BaseModel):
924 923 __tablename__ = 'statistics'
925 924 __table_args__ = (
926 925 UniqueConstraint('repository_id'),
927 926 {'extend_existing': True, 'mysql_engine': 'InnoDB',
928 927 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
929 928 )
930 929 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
931 930 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
932 931 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
933 932 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
934 933 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
935 934 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
936 935
937 936 repository = relationship('Repository', single_parent=True)
938 937
939 938
940 939 class UserFollowing(Base, BaseModel):
941 940 __tablename__ = 'user_followings'
942 941 __table_args__ = (
943 942 UniqueConstraint('user_id', 'follows_repository_id'),
944 943 UniqueConstraint('user_id', 'follows_user_id'),
945 944 {'extend_existing': True, 'mysql_engine': 'InnoDB',
946 945 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
947 946 )
948 947
949 948 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
950 949 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
951 950 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
952 951 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
953 952 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
954 953
955 954 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
956 955
957 956 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
958 957 follows_repository = relationship('Repository', order_by='Repository.repo_name')
959 958
960 959
961 960 class CacheInvalidation(Base, BaseModel):
962 961 __tablename__ = 'cache_invalidation'
963 962 __table_args__ = (
964 963 UniqueConstraint('cache_key'),
965 964 Index('key_idx', 'cache_key'),
966 965 {'extend_existing': True, 'mysql_engine': 'InnoDB',
967 966 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
968 967 )
969 968 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
970 969 cache_key = Column("cache_key", String(255), nullable=True, unique=None, default=None)
971 970 cache_args = Column("cache_args", String(255), nullable=True, unique=None, default=None)
972 971 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
973 972
974 973 def __init__(self, cache_key, cache_args=''):
975 974 self.cache_key = cache_key
976 975 self.cache_args = cache_args
977 976 self.cache_active = False
978 977
979 978
980 979 class ChangesetComment(Base, BaseModel):
981 980 __tablename__ = 'changeset_comments'
982 981 __table_args__ = (
983 982 Index('cc_revision_idx', 'revision'),
984 983 {'extend_existing': True, 'mysql_engine': 'InnoDB',
985 984 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
986 985 )
987 986 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
988 987 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
989 988 revision = Column('revision', String(40), nullable=True)
990 989 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
991 990 line_no = Column('line_no', Unicode(10), nullable=True)
992 991 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
993 992 f_path = Column('f_path', Unicode(1000), nullable=True)
994 993 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
995 994 text = Column('text', UnicodeText().with_variant(UnicodeText(25000), 'mysql'), nullable=False)
996 995 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
997 996 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
998 997
999 998 author = relationship('User', lazy='joined')
1000 999 repo = relationship('Repository')
1001 1000 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1002 1001 pull_request = relationship('PullRequest', lazy='joined')
1003 1002
1004 1003
1005 1004 class ChangesetStatus(Base, BaseModel):
1006 1005 __tablename__ = 'changeset_statuses'
1007 1006 __table_args__ = (
1008 1007 Index('cs_revision_idx', 'revision'),
1009 1008 Index('cs_version_idx', 'version'),
1010 1009 UniqueConstraint('repo_id', 'revision', 'version'),
1011 1010 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1012 1011 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1013 1012 )
1014 1013 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1015 1014 STATUS_APPROVED = 'approved'
1016 1015 STATUS_REJECTED = 'rejected'
1017 1016 STATUS_UNDER_REVIEW = 'under_review'
1018 1017
1019 1018 STATUSES = [
1020 1019 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1021 1020 (STATUS_APPROVED, _("Approved")),
1022 1021 (STATUS_REJECTED, _("Rejected")),
1023 1022 (STATUS_UNDER_REVIEW, _("Under Review")),
1024 1023 ]
1025 1024
1026 1025 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1027 1026 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1028 1027 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1029 1028 revision = Column('revision', String(40), nullable=False)
1030 1029 status = Column('status', String(128), nullable=False, default=DEFAULT)
1031 1030 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1032 1031 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1033 1032 version = Column('version', Integer(), nullable=False, default=0)
1034 1033 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1035 1034
1036 1035 author = relationship('User', lazy='joined')
1037 1036 repo = relationship('Repository')
1038 1037 comment = relationship('ChangesetComment', lazy='joined')
1039 1038 pull_request = relationship('PullRequest', lazy='joined')
1040 1039
1041 1040
1042 1041
1043 1042 class PullRequest(Base, BaseModel):
1044 1043 __tablename__ = 'pull_requests'
1045 1044 __table_args__ = (
1046 1045 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1047 1046 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1048 1047 )
1049 1048
1050 1049 STATUS_NEW = u'new'
1051 1050 STATUS_OPEN = u'open'
1052 1051 STATUS_CLOSED = u'closed'
1053 1052
1054 1053 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1055 1054 title = Column('title', Unicode(256), nullable=True)
1056 1055 description = Column('description', UnicodeText().with_variant(UnicodeText(10240), 'mysql'), nullable=True)
1057 1056 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1058 1057 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1059 1058 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1060 1059 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1061 1060 _revisions = Column('revisions', UnicodeText().with_variant(UnicodeText(20500), 'mysql'))
1062 1061 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1063 1062 org_ref = Column('org_ref', Unicode(256), nullable=False)
1064 1063 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1065 1064 other_ref = Column('other_ref', Unicode(256), nullable=False)
1066 1065
1067 1066 author = relationship('User', lazy='joined')
1068 1067 reviewers = relationship('PullRequestReviewers',
1069 1068 cascade="all, delete, delete-orphan")
1070 1069 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1071 1070 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1072 1071 statuses = relationship('ChangesetStatus')
1073 1072 comments = relationship('ChangesetComment',
1074 1073 cascade="all, delete, delete-orphan")
1075 1074
1076 1075
1077 1076 class PullRequestReviewers(Base, BaseModel):
1078 1077 __tablename__ = 'pull_request_reviewers'
1079 1078 __table_args__ = (
1080 1079 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1081 1080 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1082 1081 )
1083 1082
1084 1083 def __init__(self, user=None, pull_request=None):
1085 1084 self.user = user
1086 1085 self.pull_request = pull_request
1087 1086
1088 1087 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1089 1088 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1090 1089 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1091 1090
1092 1091 user = relationship('User')
1093 1092 pull_request = relationship('PullRequest')
1094 1093
1095 1094
1096 1095 class Notification(Base, BaseModel):
1097 1096 __tablename__ = 'notifications'
1098 1097 __table_args__ = (
1099 1098 Index('notification_type_idx', 'type'),
1100 1099 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1101 1100 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1102 1101 )
1103 1102
1104 1103 TYPE_CHANGESET_COMMENT = u'cs_comment'
1105 1104 TYPE_MESSAGE = u'message'
1106 1105 TYPE_MENTION = u'mention'
1107 1106 TYPE_REGISTRATION = u'registration'
1108 1107 TYPE_PULL_REQUEST = u'pull_request'
1109 1108 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1110 1109
1111 1110 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1112 1111 subject = Column('subject', Unicode(512), nullable=True)
1113 1112 body = Column('body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True)
1114 1113 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1115 1114 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1116 1115 type_ = Column('type', Unicode(256))
1117 1116
1118 1117 created_by_user = relationship('User')
1119 1118 notifications_to_users = relationship('UserNotification', lazy='joined',
1120 1119 cascade="all, delete, delete-orphan")
1121 1120
1122 1121
1123 1122 class UserNotification(Base, BaseModel):
1124 1123 __tablename__ = 'user_to_notification'
1125 1124 __table_args__ = (
1126 1125 UniqueConstraint('user_id', 'notification_id'),
1127 1126 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1128 1127 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1129 1128 )
1130 1129 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1131 1130 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1132 1131 read = Column('read', Boolean, default=False)
1133 1132 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1134 1133
1135 1134 user = relationship('User', lazy="joined")
1136 1135 notification = relationship('Notification', lazy="joined",
1137 1136 order_by=lambda: Notification.created_on.desc(),)
1138 1137
1139 1138
1140 1139 class Gist(Base, BaseModel):
1141 1140 __tablename__ = 'gists'
1142 1141 __table_args__ = (
1143 1142 Index('g_gist_access_id_idx', 'gist_access_id'),
1144 1143 Index('g_created_on_idx', 'created_on'),
1145 1144 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1146 1145 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1147 1146 )
1148 1147 GIST_PUBLIC = u'public'
1149 1148 GIST_PRIVATE = u'private'
1150 1149
1151 1150 gist_id = Column('gist_id', Integer(), primary_key=True)
1152 1151 gist_access_id = Column('gist_access_id', Unicode(250))
1153 1152 gist_description = Column('gist_description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
1154 1153 gist_owner = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=True)
1155 1154 gist_expires = Column('gist_expires', Float(53), nullable=False)
1156 1155 gist_type = Column('gist_type', Unicode(128), nullable=False)
1157 1156 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1158 1157 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1159 1158
1160 1159 owner = relationship('User')
1161 1160
1162 1161
1163 1162 class DbMigrateVersion(Base, BaseModel):
1164 1163 __tablename__ = 'db_migrate_version'
1165 1164 __table_args__ = (
1166 1165 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1167 1166 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1168 1167 )
1169 1168 repository_id = Column('repository_id', String(250), primary_key=True)
1170 1169 repository_path = Column('repository_path', Text)
1171 1170 version = Column('version', Integer)
@@ -1,1172 +1,1171 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import os
22 22 import time
23 23 import logging
24 24 import datetime
25 25 import traceback
26 26 import hashlib
27 27 import collections
28 28 import functools
29 29
30 30 from sqlalchemy import *
31 31 from sqlalchemy.ext.hybrid import hybrid_property
32 32 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
33 33 from sqlalchemy.exc import DatabaseError
34 34 from beaker.cache import cache_region, region_invalidate
35 35 from webob.exc import HTTPNotFound
36 36
37 37 from rhodecode.translation import _
38 38
39 39 from rhodecode.lib.vcs import get_backend
40 40 from rhodecode.lib.vcs.utils.helpers import get_scm
41 41 from rhodecode.lib.vcs.exceptions import VCSError
42 42 from zope.cachedescriptors.property import Lazy as LazyProperty
43 43 from rhodecode.lib.vcs.backends.base import EmptyCommit
44 44
45 45 from rhodecode.lib.utils2 import str2bool, safe_str, get_commit_safe, \
46 46 safe_unicode, remove_prefix, time_to_datetime, aslist, Optional, safe_int
47 47 from rhodecode.lib.ext_json import json
48 48 from rhodecode.lib.caching_query import FromCache
49 49
50 50 from rhodecode.model.meta import Base, Session
51 51
52 52 URL_SEP = '/'
53 53 log = logging.getLogger(__name__)
54 54
55 55 #==============================================================================
56 56 # BASE CLASSES
57 57 #==============================================================================
58 58
59 59 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
60 60
61 61
62 62 class BaseModel(object):
63 63 """
64 64 Base Model for all classes
65 65 """
66 66
67 67 @classmethod
68 68 def _get_keys(cls):
69 69 """return column names for this model """
70 70 return class_mapper(cls).c.keys()
71 71
72 72 def get_dict(self):
73 73 """
74 74 return dict with keys and values corresponding
75 75 to this model data """
76 76
77 77 d = {}
78 78 for k in self._get_keys():
79 79 d[k] = getattr(self, k)
80 80
81 81 # also use __json__() if present to get additional fields
82 82 _json_attr = getattr(self, '__json__', None)
83 83 if _json_attr:
84 84 # update with attributes from __json__
85 85 if callable(_json_attr):
86 86 _json_attr = _json_attr()
87 87 for k, val in _json_attr.iteritems():
88 88 d[k] = val
89 89 return d
90 90
91 91 def get_appstruct(self):
92 92 """return list with keys and values tupples corresponding
93 93 to this model data """
94 94
95 95 l = []
96 96 for k in self._get_keys():
97 97 l.append((k, getattr(self, k),))
98 98 return l
99 99
100 100 def populate_obj(self, populate_dict):
101 101 """populate model with data from given populate_dict"""
102 102
103 103 for k in self._get_keys():
104 104 if k in populate_dict:
105 105 setattr(self, k, populate_dict[k])
106 106
107 107 @classmethod
108 108 def query(cls):
109 109 return Session().query(cls)
110 110
111 111 @classmethod
112 112 def get(cls, id_):
113 113 if id_:
114 114 return cls.query().get(id_)
115 115
116 116 @classmethod
117 117 def get_or_404(cls, id_):
118 118 try:
119 119 id_ = int(id_)
120 120 except (TypeError, ValueError):
121 121 raise HTTPNotFound
122 122
123 123 res = cls.query().get(id_)
124 124 if not res:
125 125 raise HTTPNotFound
126 126 return res
127 127
128 128 @classmethod
129 129 def getAll(cls):
130 130 # deprecated and left for backward compatibility
131 131 return cls.get_all()
132 132
133 133 @classmethod
134 134 def get_all(cls):
135 135 return cls.query().all()
136 136
137 137 @classmethod
138 138 def delete(cls, id_):
139 139 obj = cls.query().get(id_)
140 140 Session().delete(obj)
141 141
142 142 def __repr__(self):
143 143 if hasattr(self, '__unicode__'):
144 144 # python repr needs to return str
145 145 try:
146 146 return safe_str(self.__unicode__())
147 147 except UnicodeDecodeError:
148 148 pass
149 149 return '<DB:%s>' % (self.__class__.__name__)
150 150
151 151
152 152 class RhodeCodeSetting(Base, BaseModel):
153 153 SETTINGS_TYPES = {
154 154 'str': safe_str,
155 155 'int': safe_int,
156 156 'unicode': safe_unicode,
157 157 'bool': str2bool,
158 158 'list': functools.partial(aslist, sep=',')
159 159 }
160 160 __tablename__ = 'rhodecode_settings'
161 161 __table_args__ = (
162 162 UniqueConstraint('app_settings_name'),
163 163 {'extend_existing': True, 'mysql_engine': 'InnoDB',
164 164 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
165 165 )
166 166 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
167 167 app_settings_name = Column("app_settings_name", String(255), nullable=True, unique=None, default=None)
168 168 _app_settings_value = Column("app_settings_value", String(4096), nullable=True, unique=None, default=None)
169 169 _app_settings_type = Column("app_settings_type", String(255), nullable=True, unique=None, default=None)
170 170
171 171 def __init__(self, key='', val='', type='unicode'):
172 172 self.app_settings_name = key
173 173 self.app_settings_value = val
174 174 self.app_settings_type = type
175 175
176 176 @validates('_app_settings_value')
177 177 def validate_settings_value(self, key, val):
178 178 assert type(val) == unicode
179 179 return val
180 180
181 181 @hybrid_property
182 182 def app_settings_value(self):
183 183 v = self._app_settings_value
184 184 _type = self.app_settings_type
185 185 converter = self.SETTINGS_TYPES.get(_type) or self.SETTINGS_TYPES['unicode']
186 186 return converter(v)
187 187
188 188 @app_settings_value.setter
189 189 def app_settings_value(self, val):
190 190 """
191 191 Setter that will always make sure we use unicode in app_settings_value
192 192
193 193 :param val:
194 194 """
195 195 self._app_settings_value = safe_unicode(val)
196 196
197 197 @hybrid_property
198 198 def app_settings_type(self):
199 199 return self._app_settings_type
200 200
201 201 @app_settings_type.setter
202 202 def app_settings_type(self, val):
203 203 if val not in self.SETTINGS_TYPES:
204 204 raise Exception('type must be one of %s got %s'
205 205 % (self.SETTINGS_TYPES.keys(), val))
206 206 self._app_settings_type = val
207 207
208 208 def __unicode__(self):
209 209 return u"<%s('%s:%s[%s]')>" % (
210 210 self.__class__.__name__,
211 211 self.app_settings_name, self.app_settings_value, self.app_settings_type
212 212 )
213 213
214 214
215 215 class RhodeCodeUi(Base, BaseModel):
216 216 __tablename__ = 'rhodecode_ui'
217 217 __table_args__ = (
218 218 UniqueConstraint('ui_key'),
219 219 {'extend_existing': True, 'mysql_engine': 'InnoDB',
220 220 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
221 221 )
222 222
223 223 HOOK_REPO_SIZE = 'changegroup.repo_size'
224 224 HOOK_PUSH = 'changegroup.push_logger'
225 225 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
226 226 HOOK_PULL = 'outgoing.pull_logger'
227 227 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
228 228
229 229 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
230 230 ui_section = Column("ui_section", String(255), nullable=True, unique=None, default=None)
231 231 ui_key = Column("ui_key", String(255), nullable=True, unique=None, default=None)
232 232 ui_value = Column("ui_value", String(255), nullable=True, unique=None, default=None)
233 233 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
234 234
235 235
236 236
237 237 class User(Base, BaseModel):
238 238 __tablename__ = 'users'
239 239 __table_args__ = (
240 240 UniqueConstraint('username'), UniqueConstraint('email'),
241 241 Index('u_username_idx', 'username'),
242 242 Index('u_email_idx', 'email'),
243 243 {'extend_existing': True, 'mysql_engine': 'InnoDB',
244 244 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
245 245 )
246 246 DEFAULT_USER = 'default'
247 247
248 248 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
249 249 username = Column("username", String(255), nullable=True, unique=None, default=None)
250 250 password = Column("password", String(255), nullable=True, unique=None, default=None)
251 251 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
252 252 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
253 253 name = Column("firstname", String(255), nullable=True, unique=None, default=None)
254 254 lastname = Column("lastname", String(255), nullable=True, unique=None, default=None)
255 255 _email = Column("email", String(255), nullable=True, unique=None, default=None)
256 256 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
257 257 extern_type = Column("extern_type", String(255), nullable=True, unique=None, default=None)
258 258 extern_name = Column("extern_name", String(255), nullable=True, unique=None, default=None)
259 259 api_key = Column("api_key", String(255), nullable=True, unique=None, default=None)
260 260 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
261 261 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
262 262
263 263 user_log = relationship('UserLog')
264 264 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
265 265
266 266 repositories = relationship('Repository')
267 267 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
268 268 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
269 269
270 270 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
271 271 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
272 272
273 273 group_member = relationship('UserGroupMember', cascade='all')
274 274
275 275 notifications = relationship('UserNotification', cascade='all')
276 276 # notifications assigned to this user
277 277 user_created_notifications = relationship('Notification', cascade='all')
278 278 # comments created by this user
279 279 user_comments = relationship('ChangesetComment', cascade='all')
280 280 user_emails = relationship('UserEmailMap', cascade='all')
281 281
282 282 @hybrid_property
283 283 def email(self):
284 284 return self._email
285 285
286 286 @email.setter
287 287 def email(self, val):
288 288 self._email = val.lower() if val else None
289 289
290 290 @property
291 291 def firstname(self):
292 292 # alias for future
293 293 return self.name
294 294
295 295 @property
296 296 def username_and_name(self):
297 297 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
298 298
299 299 @property
300 300 def full_name(self):
301 301 return '%s %s' % (self.firstname, self.lastname)
302 302
303 303 @property
304 304 def full_contact(self):
305 305 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
306 306
307 307 @property
308 308 def short_contact(self):
309 309 return '%s %s' % (self.firstname, self.lastname)
310 310
311 311 @property
312 312 def is_admin(self):
313 313 return self.admin
314 314
315 315 @classmethod
316 316 def get_by_username(cls, username, case_insensitive=False, cache=False):
317 317 if case_insensitive:
318 318 q = cls.query().filter(cls.username.ilike(username))
319 319 else:
320 320 q = cls.query().filter(cls.username == username)
321 321
322 322 if cache:
323 323 q = q.options(FromCache(
324 324 "sql_cache_short",
325 325 "get_user_%s" % _hash_key(username)
326 326 )
327 327 )
328 328 return q.scalar()
329 329
330 330 @classmethod
331 331 def get_by_auth_token(cls, auth_token, cache=False):
332 332 q = cls.query().filter(cls.api_key == auth_token)
333 333
334 334 if cache:
335 335 q = q.options(FromCache("sql_cache_short",
336 336 "get_auth_token_%s" % auth_token))
337 337 return q.scalar()
338 338
339 339 @classmethod
340 340 def get_by_email(cls, email, case_insensitive=False, cache=False):
341 341 if case_insensitive:
342 342 q = cls.query().filter(cls.email.ilike(email))
343 343 else:
344 344 q = cls.query().filter(cls.email == email)
345 345
346 346 if cache:
347 347 q = q.options(FromCache("sql_cache_short",
348 348 "get_email_key_%s" % email))
349 349
350 350 ret = q.scalar()
351 351 if ret is None:
352 352 q = UserEmailMap.query()
353 353 # try fetching in alternate email map
354 354 if case_insensitive:
355 355 q = q.filter(UserEmailMap.email.ilike(email))
356 356 else:
357 357 q = q.filter(UserEmailMap.email == email)
358 358 q = q.options(joinedload(UserEmailMap.user))
359 359 if cache:
360 360 q = q.options(FromCache("sql_cache_short",
361 361 "get_email_map_key_%s" % email))
362 362 ret = getattr(q.scalar(), 'user', None)
363 363
364 364 return ret
365 365
366 366 @classmethod
367 367 def get_first_admin(cls):
368 368 user = User.query().filter(User.admin == True).first()
369 369 if user is None:
370 370 raise Exception('Missing administrative account!')
371 371 return user
372 372
373 373 @classmethod
374 374 def get_default_user(cls, cache=False):
375 375 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
376 376 if user is None:
377 377 raise Exception('Missing default account!')
378 378 return user
379 379
380 380
381 381
382 382
383 383 class UserEmailMap(Base, BaseModel):
384 384 __tablename__ = 'user_email_map'
385 385 __table_args__ = (
386 386 Index('uem_email_idx', 'email'),
387 387 UniqueConstraint('email'),
388 388 {'extend_existing': True, 'mysql_engine': 'InnoDB',
389 389 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
390 390 )
391 391 __mapper_args__ = {}
392 392
393 393 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
394 394 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
395 395 _email = Column("email", String(255), nullable=True, unique=False, default=None)
396 396 user = relationship('User', lazy='joined')
397 397
398 398 @validates('_email')
399 399 def validate_email(self, key, email):
400 400 # check if this email is not main one
401 401 main_email = Session().query(User).filter(User.email == email).scalar()
402 402 if main_email is not None:
403 403 raise AttributeError('email %s is present is user table' % email)
404 404 return email
405 405
406 406 @hybrid_property
407 407 def email(self):
408 408 return self._email
409 409
410 410 @email.setter
411 411 def email(self, val):
412 412 self._email = val.lower() if val else None
413 413
414 414
415 415 class UserIpMap(Base, BaseModel):
416 416 __tablename__ = 'user_ip_map'
417 417 __table_args__ = (
418 418 UniqueConstraint('user_id', 'ip_addr'),
419 419 {'extend_existing': True, 'mysql_engine': 'InnoDB',
420 420 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
421 421 )
422 422 __mapper_args__ = {}
423 423
424 424 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
425 425 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
426 426 ip_addr = Column("ip_addr", String(255), nullable=True, unique=False, default=None)
427 427 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
428 428 user = relationship('User', lazy='joined')
429 429
430 430
431 431 class UserLog(Base, BaseModel):
432 432 __tablename__ = 'user_logs'
433 433 __table_args__ = (
434 434 {'extend_existing': True, 'mysql_engine': 'InnoDB',
435 435 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
436 436 )
437 437 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
438 438 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
439 439 username = Column("username", String(255), nullable=True, unique=None, default=None)
440 440 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
441 441 repository_name = Column("repository_name", String(255), nullable=True, unique=None, default=None)
442 442 user_ip = Column("user_ip", String(255), nullable=True, unique=None, default=None)
443 443 action = Column("action", String(1200000), nullable=True, unique=None, default=None)
444 444 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
445 445
446 446 def __unicode__(self):
447 447 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
448 448 self.repository_name,
449 449 self.action)
450 450
451 451 user = relationship('User')
452 452 repository = relationship('Repository', cascade='')
453 453
454 454
455 455 class UserGroup(Base, BaseModel):
456 456 __tablename__ = 'users_groups'
457 457 __table_args__ = (
458 458 {'extend_existing': True, 'mysql_engine': 'InnoDB',
459 459 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
460 460 )
461 461
462 462 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
463 463 users_group_name = Column("users_group_name", String(255), nullable=False, unique=True, default=None)
464 464 user_group_description = Column("user_group_description", String(10000), nullable=True, unique=None, default=None)
465 465 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
466 466 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
467 467 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
468 468 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
469 469
470 470 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
471 471 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
472 472 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
473 473 users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
474 474 user_user_group_to_perm = relationship('UserUserGroupToPerm', cascade='all')
475 475 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm ', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
476 476
477 477 user = relationship('User')
478 478
479 479 def __unicode__(self):
480 480 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
481 481 self.users_group_id,
482 482 self.users_group_name)
483 483
484 484 @classmethod
485 485 def get_by_group_name(cls, group_name, cache=False,
486 486 case_insensitive=False):
487 487 if case_insensitive:
488 488 q = cls.query().filter(cls.users_group_name.ilike(group_name))
489 489 else:
490 490 q = cls.query().filter(cls.users_group_name == group_name)
491 491 if cache:
492 492 q = q.options(FromCache(
493 493 "sql_cache_short",
494 494 "get_user_%s" % _hash_key(group_name)
495 495 )
496 496 )
497 497 return q.scalar()
498 498
499 499 @classmethod
500 500 def get(cls, user_group_id, cache=False):
501 501 user_group = cls.query()
502 502 if cache:
503 503 user_group = user_group.options(FromCache("sql_cache_short",
504 504 "get_users_group_%s" % user_group_id))
505 505 return user_group.get(user_group_id)
506 506
507 507
508 508 class UserGroupMember(Base, BaseModel):
509 509 __tablename__ = 'users_groups_members'
510 510 __table_args__ = (
511 511 {'extend_existing': True, 'mysql_engine': 'InnoDB',
512 512 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
513 513 )
514 514
515 515 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
516 516 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
517 517 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
518 518
519 519 user = relationship('User', lazy='joined')
520 520 users_group = relationship('UserGroup')
521 521
522 522 def __init__(self, gr_id='', u_id=''):
523 523 self.users_group_id = gr_id
524 524 self.user_id = u_id
525 525
526 526
527 527 class RepositoryField(Base, BaseModel):
528 528 __tablename__ = 'repositories_fields'
529 529 __table_args__ = (
530 530 UniqueConstraint('repository_id', 'field_key'), # no-multi field
531 531 {'extend_existing': True, 'mysql_engine': 'InnoDB',
532 532 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
533 533 )
534 534 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
535 535
536 536 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
537 537 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
538 538 field_key = Column("field_key", String(250))
539 539 field_label = Column("field_label", String(1024), nullable=False)
540 540 field_value = Column("field_value", String(10000), nullable=False)
541 541 field_desc = Column("field_desc", String(1024), nullable=False)
542 542 field_type = Column("field_type", String(256), nullable=False, unique=None)
543 543 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
544 544
545 545 repository = relationship('Repository')
546 546
547 547 @classmethod
548 548 def get_by_key_name(cls, key, repo):
549 549 row = cls.query()\
550 550 .filter(cls.repository == repo)\
551 551 .filter(cls.field_key == key).scalar()
552 552 return row
553 553
554 554
555 555 class Repository(Base, BaseModel):
556 556 __tablename__ = 'repositories'
557 557 __table_args__ = (
558 558 UniqueConstraint('repo_name'),
559 559 Index('r_repo_name_idx', 'repo_name'),
560 560 {'extend_existing': True, 'mysql_engine': 'InnoDB',
561 561 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
562 562 )
563 563
564 564 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
565 565 repo_name = Column("repo_name", String(255), nullable=False, unique=True, default=None)
566 566 clone_uri = Column("clone_uri", String(255), nullable=True, unique=False, default=None)
567 567 repo_type = Column("repo_type", String(255), nullable=False, unique=False, default=None)
568 568 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
569 569 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
570 570 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
571 571 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
572 572 description = Column("description", String(10000), nullable=True, unique=None, default=None)
573 573 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
574 574 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
575 575 landing_rev = Column("landing_revision", String(255), nullable=False, unique=False, default=None)
576 576 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
577 577 _locked = Column("locked", String(255), nullable=True, unique=False, default=None)
578 578 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
579 579
580 580 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
581 581 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
582 582
583 583 user = relationship('User')
584 584 fork = relationship('Repository', remote_side=repo_id)
585 585 group = relationship('RepoGroup')
586 586 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
587 587 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
588 588 stats = relationship('Statistics', cascade='all', uselist=False)
589 589
590 590 followers = relationship('UserFollowing',
591 591 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
592 592 cascade='all')
593 593 extra_fields = relationship('RepositoryField',
594 594 cascade="all, delete, delete-orphan")
595 595
596 596 logs = relationship('UserLog')
597 597 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
598 598
599 599 pull_requests_org = relationship('PullRequest',
600 600 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
601 601 cascade="all, delete, delete-orphan")
602 602
603 603 pull_requests_other = relationship('PullRequest',
604 604 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
605 605 cascade="all, delete, delete-orphan")
606 606
607 607 def __unicode__(self):
608 608 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
609 609 safe_unicode(self.repo_name))
610 610
611 611 @classmethod
612 612 def get_by_repo_name(cls, repo_name):
613 613 q = Session().query(cls).filter(cls.repo_name == repo_name)
614 614 q = q.options(joinedload(Repository.fork))\
615 615 .options(joinedload(Repository.user))\
616 616 .options(joinedload(Repository.group))
617 617 return q.scalar()
618 618
619 619
620 620 class RepoGroup(Base, BaseModel):
621 621 __tablename__ = 'groups'
622 622 __table_args__ = (
623 623 UniqueConstraint('group_name', 'group_parent_id'),
624 CheckConstraint('group_id != group_parent_id'),
625 624 {'extend_existing': True, 'mysql_engine': 'InnoDB',
626 625 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
627 626 )
628 627 __mapper_args__ = {'order_by': 'group_name'}
629 628
630 629 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
631 630 group_name = Column("group_name", String(255), nullable=False, unique=True, default=None)
632 631 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
633 632 group_description = Column("group_description", String(10000), nullable=True, unique=None, default=None)
634 633 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
635 634 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
636 635 #TODO: create this field in migrations
637 636 #created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
638 637
639 638 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
640 639 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
641 640 parent_group = relationship('RepoGroup', remote_side=group_id)
642 641 user = relationship('User')
643 642
644 643 def __init__(self, group_name='', parent_group=None):
645 644 self.group_name = group_name
646 645 self.parent_group = parent_group
647 646
648 647 def __unicode__(self):
649 648 return u"<%s('id:%s:%s')>" % (self.__class__.__name__, self.group_id,
650 649 self.group_name)
651 650
652 651 @classmethod
653 652 def url_sep(cls):
654 653 return URL_SEP
655 654
656 655 @classmethod
657 656 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
658 657 if case_insensitive:
659 658 gr = cls.query()\
660 659 .filter(cls.group_name.ilike(group_name))
661 660 else:
662 661 gr = cls.query()\
663 662 .filter(cls.group_name == group_name)
664 663 if cache:
665 664 gr = gr.options(FromCache(
666 665 "sql_cache_short",
667 666 "get_group_%s" % _hash_key(group_name)
668 667 )
669 668 )
670 669 return gr.scalar()
671 670
672 671
673 672 class Permission(Base, BaseModel):
674 673 __tablename__ = 'permissions'
675 674 __table_args__ = (
676 675 Index('p_perm_name_idx', 'permission_name'),
677 676 {'extend_existing': True, 'mysql_engine': 'InnoDB',
678 677 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
679 678 )
680 679 PERMS = [
681 680 ('hg.admin', _('RhodeCode Administrator')),
682 681
683 682 ('repository.none', _('Repository no access')),
684 683 ('repository.read', _('Repository read access')),
685 684 ('repository.write', _('Repository write access')),
686 685 ('repository.admin', _('Repository admin access')),
687 686
688 687 ('group.none', _('Repository group no access')),
689 688 ('group.read', _('Repository group read access')),
690 689 ('group.write', _('Repository group write access')),
691 690 ('group.admin', _('Repository group admin access')),
692 691
693 692 ('usergroup.none', _('User group no access')),
694 693 ('usergroup.read', _('User group read access')),
695 694 ('usergroup.write', _('User group write access')),
696 695 ('usergroup.admin', _('User group admin access')),
697 696
698 697 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
699 698 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
700 699
701 700 ('hg.usergroup.create.false', _('User Group creation disabled')),
702 701 ('hg.usergroup.create.true', _('User Group creation enabled')),
703 702
704 703 ('hg.create.none', _('Repository creation disabled')),
705 704 ('hg.create.repository', _('Repository creation enabled')),
706 705
707 706 ('hg.fork.none', _('Repository forking disabled')),
708 707 ('hg.fork.repository', _('Repository forking enabled')),
709 708
710 709 ('hg.register.none', _('Registration disabled')),
711 710 ('hg.register.manual_activate', _('User Registration with manual account activation')),
712 711 ('hg.register.auto_activate', _('User Registration with automatic account activation')),
713 712
714 713 ('hg.extern_activate.manual', _('Manual activation of external account')),
715 714 ('hg.extern_activate.auto', _('Automatic activation of external account')),
716 715
717 716 ]
718 717
719 718 #definition of system default permissions for DEFAULT user
720 719 DEFAULT_USER_PERMISSIONS = [
721 720 'repository.read',
722 721 'group.read',
723 722 'usergroup.read',
724 723 'hg.create.repository',
725 724 'hg.fork.repository',
726 725 'hg.register.manual_activate',
727 726 'hg.extern_activate.auto',
728 727 ]
729 728
730 729 # defines which permissions are more important higher the more important
731 730 # Weight defines which permissions are more important.
732 731 # The higher number the more important.
733 732 PERM_WEIGHTS = {
734 733 'repository.none': 0,
735 734 'repository.read': 1,
736 735 'repository.write': 3,
737 736 'repository.admin': 4,
738 737
739 738 'group.none': 0,
740 739 'group.read': 1,
741 740 'group.write': 3,
742 741 'group.admin': 4,
743 742
744 743 'usergroup.none': 0,
745 744 'usergroup.read': 1,
746 745 'usergroup.write': 3,
747 746 'usergroup.admin': 4,
748 747 'hg.repogroup.create.false': 0,
749 748 'hg.repogroup.create.true': 1,
750 749
751 750 'hg.usergroup.create.false': 0,
752 751 'hg.usergroup.create.true': 1,
753 752
754 753 'hg.fork.none': 0,
755 754 'hg.fork.repository': 1,
756 755 'hg.create.none': 0,
757 756 'hg.create.repository': 1
758 757 }
759 758
760 759 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
761 760 permission_name = Column("permission_name", String(255), nullable=True, unique=None, default=None)
762 761 permission_longname = Column("permission_longname", String(255), nullable=True, unique=None, default=None)
763 762
764 763 def __unicode__(self):
765 764 return u"<%s('%s:%s')>" % (
766 765 self.__class__.__name__, self.permission_id, self.permission_name
767 766 )
768 767
769 768 @classmethod
770 769 def get_by_key(cls, key):
771 770 return cls.query().filter(cls.permission_name == key).scalar()
772 771
773 772
774 773 class UserRepoToPerm(Base, BaseModel):
775 774 __tablename__ = 'repo_to_perm'
776 775 __table_args__ = (
777 776 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
778 777 {'extend_existing': True, 'mysql_engine': 'InnoDB',
779 778 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
780 779 )
781 780 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
782 781 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
783 782 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
784 783 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
785 784
786 785 user = relationship('User')
787 786 repository = relationship('Repository')
788 787 permission = relationship('Permission')
789 788
790 789 def __unicode__(self):
791 790 return u'<%s => %s >' % (self.user, self.repository)
792 791
793 792
794 793 class UserUserGroupToPerm(Base, BaseModel):
795 794 __tablename__ = 'user_user_group_to_perm'
796 795 __table_args__ = (
797 796 UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
798 797 {'extend_existing': True, 'mysql_engine': 'InnoDB',
799 798 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
800 799 )
801 800 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
802 801 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
803 802 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
804 803 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
805 804
806 805 user = relationship('User')
807 806 user_group = relationship('UserGroup')
808 807 permission = relationship('Permission')
809 808
810 809 def __unicode__(self):
811 810 return u'<%s => %s >' % (self.user, self.user_group)
812 811
813 812
814 813 class UserToPerm(Base, BaseModel):
815 814 __tablename__ = 'user_to_perm'
816 815 __table_args__ = (
817 816 UniqueConstraint('user_id', 'permission_id'),
818 817 {'extend_existing': True, 'mysql_engine': 'InnoDB',
819 818 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
820 819 )
821 820 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
822 821 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
823 822 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
824 823
825 824 user = relationship('User')
826 825 permission = relationship('Permission', lazy='joined')
827 826
828 827 def __unicode__(self):
829 828 return u'<%s => %s >' % (self.user, self.permission)
830 829
831 830
832 831 class UserGroupRepoToPerm(Base, BaseModel):
833 832 __tablename__ = 'users_group_repo_to_perm'
834 833 __table_args__ = (
835 834 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
836 835 {'extend_existing': True, 'mysql_engine': 'InnoDB',
837 836 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
838 837 )
839 838 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
840 839 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
841 840 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
842 841 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
843 842
844 843 users_group = relationship('UserGroup')
845 844 permission = relationship('Permission')
846 845 repository = relationship('Repository')
847 846
848 847 def __unicode__(self):
849 848 return u'<UserGroupRepoToPerm:%s => %s >' % (self.users_group, self.repository)
850 849
851 850
852 851 class UserGroupUserGroupToPerm(Base, BaseModel):
853 852 __tablename__ = 'user_group_user_group_to_perm'
854 853 __table_args__ = (
855 854 UniqueConstraint('target_user_group_id', 'user_group_id', 'permission_id'),
856 855 CheckConstraint('target_user_group_id != user_group_id'),
857 856 {'extend_existing': True, 'mysql_engine': 'InnoDB',
858 857 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
859 858 )
860 859 user_group_user_group_to_perm_id = Column("user_group_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
861 860 target_user_group_id = Column("target_user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
862 861 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
863 862 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
864 863
865 864 target_user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
866 865 user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
867 866 permission = relationship('Permission')
868 867
869 868 def __unicode__(self):
870 869 return u'<UserGroupUserGroup:%s => %s >' % (self.target_user_group, self.user_group)
871 870
872 871
873 872 class UserGroupToPerm(Base, BaseModel):
874 873 __tablename__ = 'users_group_to_perm'
875 874 __table_args__ = (
876 875 UniqueConstraint('users_group_id', 'permission_id',),
877 876 {'extend_existing': True, 'mysql_engine': 'InnoDB',
878 877 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
879 878 )
880 879 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
881 880 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
882 881 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
883 882
884 883 users_group = relationship('UserGroup')
885 884 permission = relationship('Permission')
886 885
887 886
888 887 class UserRepoGroupToPerm(Base, BaseModel):
889 888 __tablename__ = 'user_repo_group_to_perm'
890 889 __table_args__ = (
891 890 UniqueConstraint('user_id', 'group_id', 'permission_id'),
892 891 {'extend_existing': True, 'mysql_engine': 'InnoDB',
893 892 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
894 893 )
895 894
896 895 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
897 896 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
898 897 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
899 898 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
900 899
901 900 user = relationship('User')
902 901 group = relationship('RepoGroup')
903 902 permission = relationship('Permission')
904 903
905 904
906 905 class UserGroupRepoGroupToPerm(Base, BaseModel):
907 906 __tablename__ = 'users_group_repo_group_to_perm'
908 907 __table_args__ = (
909 908 UniqueConstraint('users_group_id', 'group_id'),
910 909 {'extend_existing': True, 'mysql_engine': 'InnoDB',
911 910 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
912 911 )
913 912
914 913 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)
915 914 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
916 915 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
917 916 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
918 917
919 918 users_group = relationship('UserGroup')
920 919 permission = relationship('Permission')
921 920 group = relationship('RepoGroup')
922 921
923 922
924 923 class Statistics(Base, BaseModel):
925 924 __tablename__ = 'statistics'
926 925 __table_args__ = (
927 926 UniqueConstraint('repository_id'),
928 927 {'extend_existing': True, 'mysql_engine': 'InnoDB',
929 928 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
930 929 )
931 930 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
932 931 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
933 932 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
934 933 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
935 934 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
936 935 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
937 936
938 937 repository = relationship('Repository', single_parent=True)
939 938
940 939
941 940 class UserFollowing(Base, BaseModel):
942 941 __tablename__ = 'user_followings'
943 942 __table_args__ = (
944 943 UniqueConstraint('user_id', 'follows_repository_id'),
945 944 UniqueConstraint('user_id', 'follows_user_id'),
946 945 {'extend_existing': True, 'mysql_engine': 'InnoDB',
947 946 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
948 947 )
949 948
950 949 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
951 950 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
952 951 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
953 952 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
954 953 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
955 954
956 955 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
957 956
958 957 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
959 958 follows_repository = relationship('Repository', order_by='Repository.repo_name')
960 959
961 960
962 961 class CacheInvalidation(Base, BaseModel):
963 962 __tablename__ = 'cache_invalidation'
964 963 __table_args__ = (
965 964 UniqueConstraint('cache_key'),
966 965 Index('key_idx', 'cache_key'),
967 966 {'extend_existing': True, 'mysql_engine': 'InnoDB',
968 967 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
969 968 )
970 969 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
971 970 cache_key = Column("cache_key", String(255), nullable=True, unique=None, default=None)
972 971 cache_args = Column("cache_args", String(255), nullable=True, unique=None, default=None)
973 972 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
974 973
975 974 def __init__(self, cache_key, cache_args=''):
976 975 self.cache_key = cache_key
977 976 self.cache_args = cache_args
978 977 self.cache_active = False
979 978
980 979
981 980 class ChangesetComment(Base, BaseModel):
982 981 __tablename__ = 'changeset_comments'
983 982 __table_args__ = (
984 983 Index('cc_revision_idx', 'revision'),
985 984 {'extend_existing': True, 'mysql_engine': 'InnoDB',
986 985 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
987 986 )
988 987 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
989 988 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
990 989 revision = Column('revision', String(40), nullable=True)
991 990 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
992 991 line_no = Column('line_no', Unicode(10), nullable=True)
993 992 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
994 993 f_path = Column('f_path', Unicode(1000), nullable=True)
995 994 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
996 995 text = Column('text', UnicodeText().with_variant(UnicodeText(25000), 'mysql'), nullable=False)
997 996 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
998 997 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
999 998
1000 999 author = relationship('User', lazy='joined')
1001 1000 repo = relationship('Repository')
1002 1001 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1003 1002 pull_request = relationship('PullRequest', lazy='joined')
1004 1003
1005 1004
1006 1005 class ChangesetStatus(Base, BaseModel):
1007 1006 __tablename__ = 'changeset_statuses'
1008 1007 __table_args__ = (
1009 1008 Index('cs_revision_idx', 'revision'),
1010 1009 Index('cs_version_idx', 'version'),
1011 1010 UniqueConstraint('repo_id', 'revision', 'version'),
1012 1011 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1013 1012 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1014 1013 )
1015 1014 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1016 1015 STATUS_APPROVED = 'approved'
1017 1016 STATUS_REJECTED = 'rejected'
1018 1017 STATUS_UNDER_REVIEW = 'under_review'
1019 1018
1020 1019 STATUSES = [
1021 1020 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1022 1021 (STATUS_APPROVED, _("Approved")),
1023 1022 (STATUS_REJECTED, _("Rejected")),
1024 1023 (STATUS_UNDER_REVIEW, _("Under Review")),
1025 1024 ]
1026 1025
1027 1026 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1028 1027 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1029 1028 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1030 1029 revision = Column('revision', String(40), nullable=False)
1031 1030 status = Column('status', String(128), nullable=False, default=DEFAULT)
1032 1031 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1033 1032 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1034 1033 version = Column('version', Integer(), nullable=False, default=0)
1035 1034 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1036 1035
1037 1036 author = relationship('User', lazy='joined')
1038 1037 repo = relationship('Repository')
1039 1038 comment = relationship('ChangesetComment', lazy='joined')
1040 1039 pull_request = relationship('PullRequest', lazy='joined')
1041 1040
1042 1041
1043 1042
1044 1043 class PullRequest(Base, BaseModel):
1045 1044 __tablename__ = 'pull_requests'
1046 1045 __table_args__ = (
1047 1046 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1048 1047 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1049 1048 )
1050 1049
1051 1050 STATUS_NEW = u'new'
1052 1051 STATUS_OPEN = u'open'
1053 1052 STATUS_CLOSED = u'closed'
1054 1053
1055 1054 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1056 1055 title = Column('title', Unicode(256), nullable=True)
1057 1056 description = Column('description', UnicodeText().with_variant(UnicodeText(10240), 'mysql'), nullable=True)
1058 1057 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1059 1058 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1060 1059 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1061 1060 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1062 1061 _revisions = Column('revisions', UnicodeText().with_variant(UnicodeText(20500), 'mysql'))
1063 1062 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1064 1063 org_ref = Column('org_ref', Unicode(256), nullable=False)
1065 1064 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1066 1065 other_ref = Column('other_ref', Unicode(256), nullable=False)
1067 1066
1068 1067 author = relationship('User', lazy='joined')
1069 1068 reviewers = relationship('PullRequestReviewers',
1070 1069 cascade="all, delete, delete-orphan")
1071 1070 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1072 1071 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1073 1072 statuses = relationship('ChangesetStatus')
1074 1073 comments = relationship('ChangesetComment',
1075 1074 cascade="all, delete, delete-orphan")
1076 1075
1077 1076
1078 1077 class PullRequestReviewers(Base, BaseModel):
1079 1078 __tablename__ = 'pull_request_reviewers'
1080 1079 __table_args__ = (
1081 1080 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1082 1081 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1083 1082 )
1084 1083
1085 1084 def __init__(self, user=None, pull_request=None):
1086 1085 self.user = user
1087 1086 self.pull_request = pull_request
1088 1087
1089 1088 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1090 1089 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1091 1090 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1092 1091
1093 1092 user = relationship('User')
1094 1093 pull_request = relationship('PullRequest')
1095 1094
1096 1095
1097 1096 class Notification(Base, BaseModel):
1098 1097 __tablename__ = 'notifications'
1099 1098 __table_args__ = (
1100 1099 Index('notification_type_idx', 'type'),
1101 1100 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1102 1101 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1103 1102 )
1104 1103
1105 1104 TYPE_CHANGESET_COMMENT = u'cs_comment'
1106 1105 TYPE_MESSAGE = u'message'
1107 1106 TYPE_MENTION = u'mention'
1108 1107 TYPE_REGISTRATION = u'registration'
1109 1108 TYPE_PULL_REQUEST = u'pull_request'
1110 1109 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1111 1110
1112 1111 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1113 1112 subject = Column('subject', Unicode(512), nullable=True)
1114 1113 body = Column('body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True)
1115 1114 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1116 1115 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1117 1116 type_ = Column('type', Unicode(256))
1118 1117
1119 1118 created_by_user = relationship('User')
1120 1119 notifications_to_users = relationship('UserNotification', lazy='joined',
1121 1120 cascade="all, delete, delete-orphan")
1122 1121
1123 1122
1124 1123 class UserNotification(Base, BaseModel):
1125 1124 __tablename__ = 'user_to_notification'
1126 1125 __table_args__ = (
1127 1126 UniqueConstraint('user_id', 'notification_id'),
1128 1127 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1129 1128 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1130 1129 )
1131 1130 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1132 1131 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1133 1132 read = Column('read', Boolean, default=False)
1134 1133 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1135 1134
1136 1135 user = relationship('User', lazy="joined")
1137 1136 notification = relationship('Notification', lazy="joined",
1138 1137 order_by=lambda: Notification.created_on.desc(),)
1139 1138
1140 1139
1141 1140 class Gist(Base, BaseModel):
1142 1141 __tablename__ = 'gists'
1143 1142 __table_args__ = (
1144 1143 Index('g_gist_access_id_idx', 'gist_access_id'),
1145 1144 Index('g_created_on_idx', 'created_on'),
1146 1145 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1147 1146 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1148 1147 )
1149 1148 GIST_PUBLIC = u'public'
1150 1149 GIST_PRIVATE = u'private'
1151 1150
1152 1151 gist_id = Column('gist_id', Integer(), primary_key=True)
1153 1152 gist_access_id = Column('gist_access_id', Unicode(250))
1154 1153 gist_description = Column('gist_description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
1155 1154 gist_owner = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=True)
1156 1155 gist_expires = Column('gist_expires', Float(53), nullable=False)
1157 1156 gist_type = Column('gist_type', Unicode(128), nullable=False)
1158 1157 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1159 1158 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1160 1159
1161 1160 owner = relationship('User')
1162 1161
1163 1162
1164 1163 class DbMigrateVersion(Base, BaseModel):
1165 1164 __tablename__ = 'db_migrate_version'
1166 1165 __table_args__ = (
1167 1166 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1168 1167 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1169 1168 )
1170 1169 repository_id = Column('repository_id', String(250), primary_key=True)
1171 1170 repository_path = Column('repository_path', Text)
1172 1171 version = Column('version', Integer)
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now