##// END OF EJS Templates
dbmigrate: fixed migration problems
super-admin -
r5166:f519a80b default
parent child Browse files
Show More

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

@@ -1,1144 +1,1144 b''
1 1
2 2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 3 #
4 4 # This program is free software: you can redistribute it and/or modify
5 5 # it under the terms of the GNU Affero General Public License, version 3
6 6 # (only), as published by the Free Software Foundation.
7 7 #
8 8 # This program is distributed in the hope that it will be useful,
9 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 11 # GNU General Public License for more details.
12 12 #
13 13 # You should have received a copy of the GNU Affero General Public License
14 14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 15 #
16 16 # This program is dual-licensed. If you wish to learn more about the
17 17 # RhodeCode Enterprise Edition, including its added features, Support services,
18 18 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 19
20 20 import os
21 21 import time
22 22 import logging
23 23 import datetime
24 24 import traceback
25 25 import hashlib
26 26 import collections
27 27
28 28 from sqlalchemy import *
29 29 from sqlalchemy.ext.hybrid import hybrid_property
30 30 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
31 31 from sqlalchemy.exc import DatabaseError
32 32 from beaker.cache import cache_region, region_invalidate
33 33 from webob.exc import HTTPNotFound
34 34
35 35 from rhodecode.translation import _
36 36
37 37 from rhodecode.lib.vcs import get_backend
38 38 from rhodecode.lib.vcs.utils.helpers import get_scm
39 39 from rhodecode.lib.vcs.exceptions import VCSError
40 40 from zope.cachedescriptors.property import Lazy as LazyProperty
41 41 from rhodecode.lib.vcs.backends.base import EmptyCommit
42 42
43 43 from rhodecode.lib.utils2 import str2bool, safe_str, get_commit_safe, \
44 44 remove_suffix, remove_prefix, time_to_datetime
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.items():
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 # deprecated and left for backward compatibility
129 129 return cls.get_all()
130 130
131 131 @classmethod
132 132 def get_all(cls):
133 133 return cls.query().all()
134 134
135 135 @classmethod
136 136 def delete(cls, id_):
137 137 obj = cls.query().get(id_)
138 138 Session().delete(obj)
139 139
140 140 def __repr__(self):
141 141 if hasattr(self, '__unicode__'):
142 142 # python repr needs to return str
143 143 return safe_str(self.__unicode__())
144 144 return '<DB:%s>' % (self.__class__.__name__)
145 145
146 146
147 147 class RhodeCodeSetting(Base, BaseModel):
148 148 __tablename__ = 'rhodecode_settings'
149 149 __table_args__ = (
150 150 UniqueConstraint('app_settings_name'),
151 151 {'extend_existing': True, 'mysql_engine': 'InnoDB',
152 152 'mysql_charset': 'utf8'}
153 153 )
154 154 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
155 155 app_settings_name = Column("app_settings_name", String(255), nullable=True, unique=None, default=None)
156 156 _app_settings_value = Column("app_settings_value", String(255), nullable=True, unique=None, default=None)
157 157
158 158 def __init__(self, k='', v=''):
159 159 self.app_settings_name = k
160 160 self.app_settings_value = v
161 161
162 162 @validates('_app_settings_value')
163 163 def validate_settings_value(self, key, val):
164 164 assert type(val) == str
165 165 return val
166 166
167 167 @hybrid_property
168 168 def app_settings_value(self):
169 169 v = self._app_settings_value
170 170 if self.app_settings_name in ["ldap_active",
171 171 "default_repo_enable_statistics",
172 172 "default_repo_enable_locking",
173 173 "default_repo_private",
174 174 "default_repo_enable_downloads"]:
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_str(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
194 194 class RhodeCodeUi(Base, BaseModel):
195 195 __tablename__ = 'rhodecode_ui'
196 196 __table_args__ = (
197 197 UniqueConstraint('ui_key'),
198 198 {'extend_existing': True, 'mysql_engine': 'InnoDB',
199 199 'mysql_charset': 'utf8'}
200 200 )
201 201
202 202 HOOK_REPO_SIZE = 'changegroup.repo_size'
203 203 HOOK_PUSH = 'changegroup.push_logger'
204 204 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
205 205 HOOK_PULL = 'outgoing.pull_logger'
206 206 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
207 207
208 208 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
209 209 ui_section = Column("ui_section", String(255), nullable=True, unique=None, default=None)
210 210 ui_key = Column("ui_key", String(255), nullable=True, unique=None, default=None)
211 211 ui_value = Column("ui_value", String(255), nullable=True, unique=None, default=None)
212 212 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
213 213
214 214
215 215
216 216 class User(Base, BaseModel):
217 217 __tablename__ = 'users'
218 218 __table_args__ = (
219 219 UniqueConstraint('username'), UniqueConstraint('email'),
220 220 Index('u_username_idx', 'username'),
221 221 Index('u_email_idx', 'email'),
222 222 {'extend_existing': True, 'mysql_engine': 'InnoDB',
223 223 'mysql_charset': 'utf8'}
224 224 )
225 225 DEFAULT_USER = 'default'
226 226
227 227 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
228 228 username = Column("username", String(255), nullable=True, unique=None, default=None)
229 229 password = Column("password", String(255), nullable=True, unique=None, default=None)
230 230 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
231 231 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
232 232 name = Column("firstname", String(255), nullable=True, unique=None, default=None)
233 233 lastname = Column("lastname", String(255), nullable=True, unique=None, default=None)
234 234 _email = Column("email", String(255), nullable=True, unique=None, default=None)
235 235 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
236 236 ldap_dn = Column("ldap_dn", String(255), nullable=True, unique=None, default=None)
237 237 api_key = Column("api_key", String(255), nullable=True, unique=None, default=None)
238 238 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
239 239
240 240 user_log = relationship('UserLog')
241 241 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
242 242
243 243 repositories = relationship('Repository')
244 244 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
245 245 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
246 246
247 247 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
248 248 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
249 249
250 250 group_member = relationship('UserGroupMember', cascade='all')
251 251
252 252 notifications = relationship('UserNotification', cascade='all')
253 253 # notifications assigned to this user
254 254 user_created_notifications = relationship('Notification', cascade='all')
255 255 # comments created by this user
256 256 user_comments = relationship('ChangesetComment', cascade='all')
257 257 user_emails = relationship('UserEmailMap', cascade='all')
258 258
259 259 @hybrid_property
260 260 def email(self):
261 261 return self._email
262 262
263 263 @email.setter
264 264 def email(self, val):
265 265 self._email = val.lower() if val else None
266 266
267 267 @property
268 268 def firstname(self):
269 269 # alias for future
270 270 return self.name
271 271
272 272 @property
273 273 def username_and_name(self):
274 274 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
275 275
276 276 @property
277 277 def full_name(self):
278 278 return '%s %s' % (self.firstname, self.lastname)
279 279
280 280 @property
281 281 def full_contact(self):
282 282 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
283 283
284 284 @property
285 285 def short_contact(self):
286 286 return '%s %s' % (self.firstname, self.lastname)
287 287
288 288 @property
289 289 def is_admin(self):
290 290 return self.admin
291 291
292 292 @classmethod
293 293 def get_by_username(cls, username, case_insensitive=False, cache=False):
294 294 if case_insensitive:
295 295 q = cls.query().filter(cls.username.ilike(username))
296 296 else:
297 297 q = cls.query().filter(cls.username == username)
298 298
299 299 if cache:
300 300 q = q.options(FromCache(
301 301 "sql_cache_short",
302 302 "get_user_%s" % _hash_key(username)
303 303 )
304 304 )
305 305 return q.scalar()
306 306
307 307 @classmethod
308 308 def get_by_auth_token(cls, auth_token, cache=False):
309 309 q = cls.query().filter(cls.api_key == auth_token)
310 310
311 311 if cache:
312 312 q = q.options(FromCache("sql_cache_short",
313 313 "get_auth_token_%s" % auth_token))
314 314 return q.scalar()
315 315
316 316 @classmethod
317 317 def get_by_email(cls, email, case_insensitive=False, cache=False):
318 318 if case_insensitive:
319 319 q = cls.query().filter(cls.email.ilike(email))
320 320 else:
321 321 q = cls.query().filter(cls.email == email)
322 322
323 323 if cache:
324 324 q = q.options(FromCache("sql_cache_short",
325 325 "get_email_key_%s" % email))
326 326
327 327 ret = q.scalar()
328 328 if ret is None:
329 329 q = UserEmailMap.query()
330 330 # try fetching in alternate email map
331 331 if case_insensitive:
332 332 q = q.filter(UserEmailMap.email.ilike(email))
333 333 else:
334 334 q = q.filter(UserEmailMap.email == email)
335 335 q = q.options(joinedload(UserEmailMap.user))
336 336 if cache:
337 337 q = q.options(FromCache("sql_cache_short",
338 338 "get_email_map_key_%s" % email))
339 339 ret = getattr(q.scalar(), 'user', None)
340 340
341 341 return ret
342 342
343 343 @classmethod
344 344 def get_first_admin(cls):
345 345 user = User.query().filter(User.admin == True).first()
346 346 if user is None:
347 347 raise Exception('Missing administrative account!')
348 348 return user
349 349
350 350 @classmethod
351 351 def get_default_user(cls, cache=False):
352 352 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
353 353 if user is None:
354 354 raise Exception('Missing default account!')
355 355 return user
356 356
357 357
358 358
359 359
360 360 class UserEmailMap(Base, BaseModel):
361 361 __tablename__ = 'user_email_map'
362 362 __table_args__ = (
363 363 Index('uem_email_idx', 'email'),
364 364 UniqueConstraint('email'),
365 365 {'extend_existing': True, 'mysql_engine': 'InnoDB',
366 366 'mysql_charset': 'utf8'}
367 367 )
368 368
369 369
370 370 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
371 371 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
372 372 _email = Column("email", String(255), nullable=True, unique=False, default=None)
373 373 user = relationship('User', lazy='joined')
374 374
375 375 @validates('_email')
376 376 def validate_email(self, key, email):
377 377 # check if this email is not main one
378 378 main_email = Session().query(User).filter(User.email == email).scalar()
379 379 if main_email is not None:
380 380 raise AttributeError('email %s is present is user table' % email)
381 381 return email
382 382
383 383 @hybrid_property
384 384 def email(self):
385 385 return self._email
386 386
387 387 @email.setter
388 388 def email(self, val):
389 389 self._email = val.lower() if val else None
390 390
391 391
392 392 class UserIpMap(Base, BaseModel):
393 393 __tablename__ = 'user_ip_map'
394 394 __table_args__ = (
395 395 UniqueConstraint('user_id', 'ip_addr'),
396 396 {'extend_existing': True, 'mysql_engine': 'InnoDB',
397 397 'mysql_charset': 'utf8'}
398 398 )
399 399
400 400
401 401 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
402 402 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
403 403 ip_addr = Column("ip_addr", String(255), nullable=True, unique=False, default=None)
404 404 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
405 405 user = relationship('User', lazy='joined')
406 406
407 407
408 408 class UserLog(Base, BaseModel):
409 409 __tablename__ = 'user_logs'
410 410 __table_args__ = (
411 411 {'extend_existing': True, 'mysql_engine': 'InnoDB',
412 412 'mysql_charset': 'utf8'},
413 413 )
414 414 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
415 415 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
416 416 username = Column("username", String(255), nullable=True, unique=None, default=None)
417 417 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
418 418 repository_name = Column("repository_name", String(255), nullable=True, unique=None, default=None)
419 419 user_ip = Column("user_ip", String(255), nullable=True, unique=None, default=None)
420 420 action = Column("action", String(1200000), nullable=True, unique=None, default=None)
421 421 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
422 422
423 423 def __unicode__(self):
424 424 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
425 425 self.repository_name,
426 426 self.action)
427 427
428 428 user = relationship('User')
429 429 repository = relationship('Repository', cascade='')
430 430
431 431
432 432 class UserGroup(Base, BaseModel):
433 433 __tablename__ = 'users_groups'
434 434 __table_args__ = (
435 435 {'extend_existing': True, 'mysql_engine': 'InnoDB',
436 436 'mysql_charset': 'utf8'},
437 437 )
438 438
439 439 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
440 440 users_group_name = Column("users_group_name", String(255), nullable=False, unique=True, default=None)
441 441 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
442 442 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
443 443 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
444 444
445 445 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
446 446 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
447 447 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
448 448 users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
449 449 user_user_group_to_perm = relationship('UserUserGroupToPerm', cascade='all')
450 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm ', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
450 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
451 451
452 452 user = relationship('User')
453 453
454 454 def __unicode__(self):
455 455 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
456 456 self.users_group_id,
457 457 self.users_group_name)
458 458
459 459 @classmethod
460 460 def get_by_group_name(cls, group_name, cache=False,
461 461 case_insensitive=False):
462 462 if case_insensitive:
463 463 q = cls.query().filter(cls.users_group_name.ilike(group_name))
464 464 else:
465 465 q = cls.query().filter(cls.users_group_name == group_name)
466 466 if cache:
467 467 q = q.options(FromCache(
468 468 "sql_cache_short",
469 469 "get_user_%s" % _hash_key(group_name)
470 470 )
471 471 )
472 472 return q.scalar()
473 473
474 474 @classmethod
475 475 def get(cls, users_group_id, cache=False):
476 476 user_group = cls.query()
477 477 if cache:
478 478 user_group = user_group.options(FromCache("sql_cache_short",
479 479 "get_users_group_%s" % users_group_id))
480 480 return user_group.get(users_group_id)
481 481
482 482
483 483 class UserGroupMember(Base, BaseModel):
484 484 __tablename__ = 'users_groups_members'
485 485 __table_args__ = (
486 486 {'extend_existing': True, 'mysql_engine': 'InnoDB',
487 487 'mysql_charset': 'utf8'},
488 488 )
489 489
490 490 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
491 491 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
492 492 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
493 493
494 494 user = relationship('User', lazy='joined')
495 495 users_group = relationship('UserGroup')
496 496
497 497 def __init__(self, gr_id='', u_id=''):
498 498 self.users_group_id = gr_id
499 499 self.user_id = u_id
500 500
501 501
502 502 class RepositoryField(Base, BaseModel):
503 503 __tablename__ = 'repositories_fields'
504 504 __table_args__ = (
505 505 UniqueConstraint('repository_id', 'field_key'), # no-multi field
506 506 {'extend_existing': True, 'mysql_engine': 'InnoDB',
507 507 'mysql_charset': 'utf8'},
508 508 )
509 509 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
510 510
511 511 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
512 512 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
513 513 field_key = Column("field_key", String(250))
514 514 field_label = Column("field_label", String(1024), nullable=False)
515 515 field_value = Column("field_value", String(10000), nullable=False)
516 516 field_desc = Column("field_desc", String(1024), nullable=False)
517 517 field_type = Column("field_type", String(256), nullable=False, unique=None)
518 518 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
519 519
520 520 repository = relationship('Repository')
521 521
522 522 @classmethod
523 523 def get_by_key_name(cls, key, repo):
524 524 row = cls.query()\
525 525 .filter(cls.repository == repo)\
526 526 .filter(cls.field_key == key).scalar()
527 527 return row
528 528
529 529
530 530 class Repository(Base, BaseModel):
531 531 __tablename__ = 'repositories'
532 532 __table_args__ = (
533 533 UniqueConstraint('repo_name'),
534 534 Index('r_repo_name_idx', 'repo_name'),
535 535 {'extend_existing': True, 'mysql_engine': 'InnoDB',
536 536 'mysql_charset': 'utf8'},
537 537 )
538 538
539 539 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
540 540 repo_name = Column("repo_name", String(255), nullable=False, unique=True, default=None)
541 541 clone_uri = Column("clone_uri", String(255), nullable=True, unique=False, default=None)
542 542 repo_type = Column("repo_type", String(255), nullable=False, unique=False, default=None)
543 543 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
544 544 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
545 545 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
546 546 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
547 547 description = Column("description", String(10000), nullable=True, unique=None, default=None)
548 548 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
549 549 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
550 550 landing_rev = Column("landing_revision", String(255), nullable=False, unique=False, default=None)
551 551 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
552 552 _locked = Column("locked", String(255), nullable=True, unique=False, default=None)
553 553 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
554 554
555 555 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
556 556 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
557 557
558 558 user = relationship('User')
559 559 fork = relationship('Repository', remote_side=repo_id)
560 560 group = relationship('RepoGroup')
561 561 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
562 562 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
563 563 stats = relationship('Statistics', cascade='all', uselist=False)
564 564
565 565 followers = relationship('UserFollowing',
566 566 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
567 567 cascade='all')
568 568 extra_fields = relationship('RepositoryField',
569 569 cascade="all, delete, delete-orphan")
570 570
571 571 logs = relationship('UserLog')
572 572 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
573 573
574 574 pull_requests_org = relationship('PullRequest',
575 575 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
576 576 cascade="all, delete, delete-orphan")
577 577
578 578 pull_requests_other = relationship('PullRequest',
579 579 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
580 580 cascade="all, delete, delete-orphan")
581 581
582 582 def __unicode__(self):
583 583 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
584 584 safe_str(self.repo_name))
585 585
586 586 @classmethod
587 587 def get_by_repo_name(cls, repo_name):
588 588 q = Session().query(cls).filter(cls.repo_name == repo_name)
589 589 q = q.options(joinedload(Repository.fork))\
590 590 .options(joinedload(Repository.user))\
591 591 .options(joinedload(Repository.group))
592 592 return q.scalar()
593 593
594 594
595 595 class RepoGroup(Base, BaseModel):
596 596 __tablename__ = 'groups'
597 597 __table_args__ = (
598 598 UniqueConstraint('group_name', 'group_parent_id'),
599 599 {'extend_existing': True, 'mysql_engine': 'InnoDB',
600 600 'mysql_charset': 'utf8'},
601 601 )
602 602
603 603
604 604 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
605 605 group_name = Column("group_name", String(255), nullable=False, unique=True, default=None)
606 606 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
607 607 group_description = Column("group_description", String(10000), nullable=True, unique=None, default=None)
608 608 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
609 609 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
610 610
611 611 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
612 612 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
613 613 parent_group = relationship('RepoGroup', remote_side=group_id)
614 614 user = relationship('User')
615 615
616 616 def __init__(self, group_name='', parent_group=None):
617 617 self.group_name = group_name
618 618 self.parent_group = parent_group
619 619
620 620 def __unicode__(self):
621 621 return u"<%s('id:%s:%s')>" % (self.__class__.__name__, self.group_id,
622 622 self.group_name)
623 623
624 624 @classmethod
625 625 def url_sep(cls):
626 626 return URL_SEP
627 627
628 628 @classmethod
629 629 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
630 630 if case_insensitive:
631 631 gr = cls.query()\
632 632 .filter(cls.group_name.ilike(group_name))
633 633 else:
634 634 gr = cls.query()\
635 635 .filter(cls.group_name == group_name)
636 636 if cache:
637 637 gr = gr.options(FromCache(
638 638 "sql_cache_short",
639 639 "get_group_%s" % _hash_key(group_name)
640 640 )
641 641 )
642 642 return gr.scalar()
643 643
644 644
645 645 class Permission(Base, BaseModel):
646 646 __tablename__ = 'permissions'
647 647 __table_args__ = (
648 648 Index('p_perm_name_idx', 'permission_name'),
649 649 {'extend_existing': True, 'mysql_engine': 'InnoDB',
650 650 'mysql_charset': 'utf8'},
651 651 )
652 652 PERMS = [
653 653 ('hg.admin', _('RhodeCode Administrator')),
654 654
655 655 ('repository.none', _('Repository no access')),
656 656 ('repository.read', _('Repository read access')),
657 657 ('repository.write', _('Repository write access')),
658 658 ('repository.admin', _('Repository admin access')),
659 659
660 660 ('group.none', _('Repository group no access')),
661 661 ('group.read', _('Repository group read access')),
662 662 ('group.write', _('Repository group write access')),
663 663 ('group.admin', _('Repository group admin access')),
664 664
665 665 ('usergroup.none', _('User group no access')),
666 666 ('usergroup.read', _('User group read access')),
667 667 ('usergroup.write', _('User group write access')),
668 668 ('usergroup.admin', _('User group admin access')),
669 669
670 670 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
671 671 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
672 672
673 673 ('hg.usergroup.create.false', _('User Group creation disabled')),
674 674 ('hg.usergroup.create.true', _('User Group creation enabled')),
675 675
676 676 ('hg.create.none', _('Repository creation disabled')),
677 677 ('hg.create.repository', _('Repository creation enabled')),
678 678
679 679 ('hg.fork.none', _('Repository forking disabled')),
680 680 ('hg.fork.repository', _('Repository forking enabled')),
681 681
682 682 ('hg.register.none', _('Registration disabled')),
683 683 ('hg.register.manual_activate', _('User Registration with manual account activation')),
684 684 ('hg.register.auto_activate', _('User Registration with automatic account activation')),
685 685
686 686 ('hg.extern_activate.manual', _('Manual activation of external account')),
687 687 ('hg.extern_activate.auto', _('Automatic activation of external account')),
688 688
689 689 ]
690 690
691 691 #definition of system default permissions for DEFAULT user
692 692 DEFAULT_USER_PERMISSIONS = [
693 693 'repository.read',
694 694 'group.read',
695 695 'usergroup.read',
696 696 'hg.create.repository',
697 697 'hg.fork.repository',
698 698 'hg.register.manual_activate',
699 699 'hg.extern_activate.auto',
700 700 ]
701 701
702 702 # defines which permissions are more important higher the more important
703 703 # Weight defines which permissions are more important.
704 704 # The higher number the more important.
705 705 PERM_WEIGHTS = {
706 706 'repository.none': 0,
707 707 'repository.read': 1,
708 708 'repository.write': 3,
709 709 'repository.admin': 4,
710 710
711 711 'group.none': 0,
712 712 'group.read': 1,
713 713 'group.write': 3,
714 714 'group.admin': 4,
715 715
716 716 'usergroup.none': 0,
717 717 'usergroup.read': 1,
718 718 'usergroup.write': 3,
719 719 'usergroup.admin': 4,
720 720 'hg.repogroup.create.false': 0,
721 721 'hg.repogroup.create.true': 1,
722 722
723 723 'hg.usergroup.create.false': 0,
724 724 'hg.usergroup.create.true': 1,
725 725
726 726 'hg.fork.none': 0,
727 727 'hg.fork.repository': 1,
728 728 'hg.create.none': 0,
729 729 'hg.create.repository': 1
730 730 }
731 731
732 732 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
733 733 permission_name = Column("permission_name", String(255), nullable=True, unique=None, default=None)
734 734 permission_longname = Column("permission_longname", String(255), nullable=True, unique=None, default=None)
735 735
736 736 def __unicode__(self):
737 737 return u"<%s('%s:%s')>" % (
738 738 self.__class__.__name__, self.permission_id, self.permission_name
739 739 )
740 740
741 741 @classmethod
742 742 def get_by_key(cls, key):
743 743 return cls.query().filter(cls.permission_name == key).scalar()
744 744
745 745
746 746 class UserRepoToPerm(Base, BaseModel):
747 747 __tablename__ = 'repo_to_perm'
748 748 __table_args__ = (
749 749 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
750 750 {'extend_existing': True, 'mysql_engine': 'InnoDB',
751 751 'mysql_charset': 'utf8'}
752 752 )
753 753 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
754 754 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
755 755 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
756 756 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
757 757
758 758 user = relationship('User')
759 759 repository = relationship('Repository')
760 760 permission = relationship('Permission')
761 761
762 762 def __unicode__(self):
763 763 return u'<%s => %s >' % (self.user, self.repository)
764 764
765 765
766 766 class UserUserGroupToPerm(Base, BaseModel):
767 767 __tablename__ = 'user_user_group_to_perm'
768 768 __table_args__ = (
769 769 UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
770 770 {'extend_existing': True, 'mysql_engine': 'InnoDB',
771 771 'mysql_charset': 'utf8'}
772 772 )
773 773 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
774 774 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
775 775 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
776 776 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
777 777
778 778 user = relationship('User')
779 779 user_group = relationship('UserGroup')
780 780 permission = relationship('Permission')
781 781
782 782 def __unicode__(self):
783 783 return u'<%s => %s >' % (self.user, self.user_group)
784 784
785 785
786 786 class UserToPerm(Base, BaseModel):
787 787 __tablename__ = 'user_to_perm'
788 788 __table_args__ = (
789 789 UniqueConstraint('user_id', 'permission_id'),
790 790 {'extend_existing': True, 'mysql_engine': 'InnoDB',
791 791 'mysql_charset': 'utf8'}
792 792 )
793 793 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
794 794 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
795 795 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
796 796
797 797 user = relationship('User')
798 798 permission = relationship('Permission', lazy='joined')
799 799
800 800 def __unicode__(self):
801 801 return u'<%s => %s >' % (self.user, self.permission)
802 802
803 803
804 804 class UserGroupRepoToPerm(Base, BaseModel):
805 805 __tablename__ = 'users_group_repo_to_perm'
806 806 __table_args__ = (
807 807 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
808 808 {'extend_existing': True, 'mysql_engine': 'InnoDB',
809 809 'mysql_charset': 'utf8'}
810 810 )
811 811 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
812 812 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
813 813 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
814 814 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
815 815
816 816 users_group = relationship('UserGroup')
817 817 permission = relationship('Permission')
818 818 repository = relationship('Repository')
819 819
820 820 def __unicode__(self):
821 821 return u'<UserGroupRepoToPerm:%s => %s >' % (self.users_group, self.repository)
822 822
823 823
824 824 class UserGroupUserGroupToPerm(Base, BaseModel):
825 825 __tablename__ = 'user_group_user_group_to_perm'
826 826 __table_args__ = (
827 827 UniqueConstraint('target_user_group_id', 'user_group_id', 'permission_id'),
828 828 CheckConstraint('target_user_group_id != user_group_id'),
829 829 {'extend_existing': True, 'mysql_engine': 'InnoDB',
830 830 'mysql_charset': 'utf8'}
831 831 )
832 832 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)
833 833 target_user_group_id = Column("target_user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
834 834 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
835 835 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
836 836
837 837 target_user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
838 838 user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
839 839 permission = relationship('Permission')
840 840
841 841 def __unicode__(self):
842 842 return u'<UserGroupUserGroup:%s => %s >' % (self.target_user_group, self.user_group)
843 843
844 844
845 845 class UserGroupToPerm(Base, BaseModel):
846 846 __tablename__ = 'users_group_to_perm'
847 847 __table_args__ = (
848 848 UniqueConstraint('users_group_id', 'permission_id',),
849 849 {'extend_existing': True, 'mysql_engine': 'InnoDB',
850 850 'mysql_charset': 'utf8'}
851 851 )
852 852 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
853 853 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
854 854 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
855 855
856 856 users_group = relationship('UserGroup')
857 857 permission = relationship('Permission')
858 858
859 859
860 860 class UserRepoGroupToPerm(Base, BaseModel):
861 861 __tablename__ = 'user_repo_group_to_perm'
862 862 __table_args__ = (
863 863 UniqueConstraint('user_id', 'group_id', 'permission_id'),
864 864 {'extend_existing': True, 'mysql_engine': 'InnoDB',
865 865 'mysql_charset': 'utf8'}
866 866 )
867 867
868 868 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
869 869 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
870 870 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
871 871 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
872 872
873 873 user = relationship('User')
874 874 group = relationship('RepoGroup')
875 875 permission = relationship('Permission')
876 876
877 877
878 878 class UserGroupRepoGroupToPerm(Base, BaseModel):
879 879 __tablename__ = 'users_group_repo_group_to_perm'
880 880 __table_args__ = (
881 881 UniqueConstraint('users_group_id', 'group_id'),
882 882 {'extend_existing': True, 'mysql_engine': 'InnoDB',
883 883 'mysql_charset': 'utf8'}
884 884 )
885 885
886 886 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)
887 887 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
888 888 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
889 889 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
890 890
891 891 users_group = relationship('UserGroup')
892 892 permission = relationship('Permission')
893 893 group = relationship('RepoGroup')
894 894
895 895
896 896 class Statistics(Base, BaseModel):
897 897 __tablename__ = 'statistics'
898 898 __table_args__ = (
899 899 UniqueConstraint('repository_id'),
900 900 {'extend_existing': True, 'mysql_engine': 'InnoDB',
901 901 'mysql_charset': 'utf8'}
902 902 )
903 903 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
904 904 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
905 905 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
906 906 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
907 907 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
908 908 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
909 909
910 910 repository = relationship('Repository', single_parent=True)
911 911
912 912
913 913 class UserFollowing(Base, BaseModel):
914 914 __tablename__ = 'user_followings'
915 915 __table_args__ = (
916 916 UniqueConstraint('user_id', 'follows_repository_id'),
917 917 UniqueConstraint('user_id', 'follows_user_id'),
918 918 {'extend_existing': True, 'mysql_engine': 'InnoDB',
919 919 'mysql_charset': 'utf8'}
920 920 )
921 921
922 922 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
923 923 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
924 924 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
925 925 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
926 926 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
927 927
928 928 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
929 929
930 930 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
931 931 follows_repository = relationship('Repository', order_by='Repository.repo_name')
932 932
933 933
934 934 class CacheInvalidation(Base, BaseModel):
935 935 __tablename__ = 'cache_invalidation'
936 936 __table_args__ = (
937 937 UniqueConstraint('cache_key'),
938 938 Index('key_idx', 'cache_key'),
939 939 {'extend_existing': True, 'mysql_engine': 'InnoDB',
940 940 'mysql_charset': 'utf8'},
941 941 )
942 942 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
943 943 cache_key = Column("cache_key", String(255), nullable=True, unique=None, default=None)
944 944 cache_args = Column("cache_args", String(255), nullable=True, unique=None, default=None)
945 945 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
946 946
947 947 def __init__(self, cache_key, cache_args=''):
948 948 self.cache_key = cache_key
949 949 self.cache_args = cache_args
950 950 self.cache_active = False
951 951
952 952
953 953 class ChangesetComment(Base, BaseModel):
954 954 __tablename__ = 'changeset_comments'
955 955 __table_args__ = (
956 956 Index('cc_revision_idx', 'revision'),
957 957 {'extend_existing': True, 'mysql_engine': 'InnoDB',
958 958 'mysql_charset': 'utf8'},
959 959 )
960 960 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
961 961 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
962 962 revision = Column('revision', String(40), nullable=True)
963 963 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
964 964 line_no = Column('line_no', Unicode(10), nullable=True)
965 965 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
966 966 f_path = Column('f_path', Unicode(1000), nullable=True)
967 967 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
968 968 text = Column('text', UnicodeText().with_variant(UnicodeText(25000), 'mysql'), nullable=False)
969 969 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
970 970 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
971 971
972 972 author = relationship('User', lazy='joined')
973 973 repo = relationship('Repository')
974 974 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
975 975 pull_request = relationship('PullRequest', lazy='joined')
976 976
977 977
978 978 class ChangesetStatus(Base, BaseModel):
979 979 __tablename__ = 'changeset_statuses'
980 980 __table_args__ = (
981 981 Index('cs_revision_idx', 'revision'),
982 982 Index('cs_version_idx', 'version'),
983 983 UniqueConstraint('repo_id', 'revision', 'version'),
984 984 {'extend_existing': True, 'mysql_engine': 'InnoDB',
985 985 'mysql_charset': 'utf8'}
986 986 )
987 987 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
988 988 STATUS_APPROVED = 'approved'
989 989 STATUS_REJECTED = 'rejected'
990 990 STATUS_UNDER_REVIEW = 'under_review'
991 991
992 992 STATUSES = [
993 993 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
994 994 (STATUS_APPROVED, _("Approved")),
995 995 (STATUS_REJECTED, _("Rejected")),
996 996 (STATUS_UNDER_REVIEW, _("Under Review")),
997 997 ]
998 998
999 999 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1000 1000 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1001 1001 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1002 1002 revision = Column('revision', String(40), nullable=False)
1003 1003 status = Column('status', String(128), nullable=False, default=DEFAULT)
1004 1004 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1005 1005 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1006 1006 version = Column('version', Integer(), nullable=False, default=0)
1007 1007 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1008 1008
1009 1009 author = relationship('User', lazy='joined')
1010 1010 repo = relationship('Repository')
1011 1011 comment = relationship('ChangesetComment', lazy='joined')
1012 1012 pull_request = relationship('PullRequest', lazy='joined')
1013 1013
1014 1014
1015 1015
1016 1016 class PullRequest(Base, BaseModel):
1017 1017 __tablename__ = 'pull_requests'
1018 1018 __table_args__ = (
1019 1019 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1020 1020 'mysql_charset': 'utf8'},
1021 1021 )
1022 1022
1023 1023 STATUS_NEW = u'new'
1024 1024 STATUS_OPEN = u'open'
1025 1025 STATUS_CLOSED = u'closed'
1026 1026
1027 1027 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1028 1028 title = Column('title', Unicode(256), nullable=True)
1029 1029 description = Column('description', UnicodeText().with_variant(UnicodeText(10240), 'mysql'), nullable=True)
1030 1030 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1031 1031 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1032 1032 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1033 1033 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1034 1034 _revisions = Column('revisions', UnicodeText().with_variant(UnicodeText(20500), 'mysql'))
1035 1035 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1036 1036 org_ref = Column('org_ref', Unicode(256), nullable=False)
1037 1037 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1038 1038 other_ref = Column('other_ref', Unicode(256), nullable=False)
1039 1039
1040 1040 author = relationship('User', lazy='joined')
1041 1041 reviewers = relationship('PullRequestReviewers',
1042 1042 cascade="all, delete, delete-orphan")
1043 1043 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1044 1044 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1045 1045 statuses = relationship('ChangesetStatus')
1046 1046 comments = relationship('ChangesetComment',
1047 1047 cascade="all, delete, delete-orphan")
1048 1048
1049 1049
1050 1050 class PullRequestReviewers(Base, BaseModel):
1051 1051 __tablename__ = 'pull_request_reviewers'
1052 1052 __table_args__ = (
1053 1053 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1054 1054 'mysql_charset': 'utf8'},
1055 1055 )
1056 1056
1057 1057 def __init__(self, user=None, pull_request=None):
1058 1058 self.user = user
1059 1059 self.pull_request = pull_request
1060 1060
1061 1061 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1062 1062 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1063 1063 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1064 1064
1065 1065 user = relationship('User')
1066 1066 pull_request = relationship('PullRequest')
1067 1067
1068 1068
1069 1069 class Notification(Base, BaseModel):
1070 1070 __tablename__ = 'notifications'
1071 1071 __table_args__ = (
1072 1072 Index('notification_type_idx', 'type'),
1073 1073 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1074 1074 'mysql_charset': 'utf8'},
1075 1075 )
1076 1076
1077 1077 TYPE_CHANGESET_COMMENT = u'cs_comment'
1078 1078 TYPE_MESSAGE = u'message'
1079 1079 TYPE_MENTION = u'mention'
1080 1080 TYPE_REGISTRATION = u'registration'
1081 1081 TYPE_PULL_REQUEST = u'pull_request'
1082 1082 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1083 1083
1084 1084 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1085 1085 subject = Column('subject', Unicode(512), nullable=True)
1086 1086 body = Column('body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True)
1087 1087 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1088 1088 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1089 1089 type_ = Column('type', Unicode(256))
1090 1090
1091 1091 created_by_user = relationship('User')
1092 1092 notifications_to_users = relationship('UserNotification', lazy='joined',
1093 1093 cascade="all, delete, delete-orphan")
1094 1094
1095 1095
1096 1096 class UserNotification(Base, BaseModel):
1097 1097 __tablename__ = 'user_to_notification'
1098 1098 __table_args__ = (
1099 1099 UniqueConstraint('user_id', 'notification_id'),
1100 1100 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1101 1101 'mysql_charset': 'utf8'}
1102 1102 )
1103 1103 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1104 1104 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1105 1105 read = Column('read', Boolean, default=False)
1106 1106 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1107 1107
1108 1108 user = relationship('User', lazy="joined")
1109 1109 notification = relationship('Notification', lazy="joined",
1110 1110 order_by=lambda: Notification.created_on.desc(),)
1111 1111
1112 1112
1113 1113 class Gist(Base, BaseModel):
1114 1114 __tablename__ = 'gists'
1115 1115 __table_args__ = (
1116 1116 Index('g_gist_access_id_idx', 'gist_access_id'),
1117 1117 Index('g_created_on_idx', 'created_on'),
1118 1118 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1119 1119 'mysql_charset': 'utf8'}
1120 1120 )
1121 1121 GIST_PUBLIC = u'public'
1122 1122 GIST_PRIVATE = u'private'
1123 1123
1124 1124 gist_id = Column('gist_id', Integer(), primary_key=True)
1125 1125 gist_access_id = Column('gist_access_id', Unicode(250))
1126 1126 gist_description = Column('gist_description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
1127 1127 gist_owner = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=True)
1128 1128 gist_expires = Column('gist_expires', Float(), nullable=False)
1129 1129 gist_type = Column('gist_type', Unicode(128), nullable=False)
1130 1130 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1131 1131 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1132 1132
1133 1133 owner = relationship('User')
1134 1134
1135 1135
1136 1136 class DbMigrateVersion(Base, BaseModel):
1137 1137 __tablename__ = 'db_migrate_version'
1138 1138 __table_args__ = (
1139 1139 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1140 1140 'mysql_charset': 'utf8'},
1141 1141 )
1142 1142 repository_id = Column('repository_id', String(250), primary_key=True)
1143 1143 repository_path = Column('repository_path', Text)
1144 1144 version = Column('version', Integer)
@@ -1,1146 +1,1146 b''
1 1
2 2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 3 #
4 4 # This program is free software: you can redistribute it and/or modify
5 5 # it under the terms of the GNU Affero General Public License, version 3
6 6 # (only), as published by the Free Software Foundation.
7 7 #
8 8 # This program is distributed in the hope that it will be useful,
9 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 11 # GNU General Public License for more details.
12 12 #
13 13 # You should have received a copy of the GNU Affero General Public License
14 14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 15 #
16 16 # This program is dual-licensed. If you wish to learn more about the
17 17 # RhodeCode Enterprise Edition, including its added features, Support services,
18 18 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 19
20 20 import os
21 21 import time
22 22 import logging
23 23 import datetime
24 24 import traceback
25 25 import hashlib
26 26 import collections
27 27
28 28 from sqlalchemy import *
29 29 from sqlalchemy.ext.hybrid import hybrid_property
30 30 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
31 31 from sqlalchemy.exc import DatabaseError
32 32 from beaker.cache import cache_region, region_invalidate
33 33 from webob.exc import HTTPNotFound
34 34
35 35 from rhodecode.translation import _
36 36
37 37 from rhodecode.lib.vcs import get_backend
38 38 from rhodecode.lib.vcs.utils.helpers import get_scm
39 39 from rhodecode.lib.vcs.exceptions import VCSError
40 40 from zope.cachedescriptors.property import Lazy as LazyProperty
41 41 from rhodecode.lib.vcs.backends.base import EmptyCommit
42 42
43 43 from rhodecode.lib.utils2 import str2bool, safe_str, get_commit_safe, \
44 44 remove_suffix, remove_prefix, time_to_datetime
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.items():
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 # deprecated and left for backward compatibility
129 129 return cls.get_all()
130 130
131 131 @classmethod
132 132 def get_all(cls):
133 133 return cls.query().all()
134 134
135 135 @classmethod
136 136 def delete(cls, id_):
137 137 obj = cls.query().get(id_)
138 138 Session().delete(obj)
139 139
140 140 def __repr__(self):
141 141 if hasattr(self, '__unicode__'):
142 142 # python repr needs to return str
143 143 return safe_str(self.__unicode__())
144 144 return '<DB:%s>' % (self.__class__.__name__)
145 145
146 146
147 147 class RhodeCodeSetting(Base, BaseModel):
148 148 __tablename__ = 'rhodecode_settings'
149 149 __table_args__ = (
150 150 UniqueConstraint('app_settings_name'),
151 151 {'extend_existing': True, 'mysql_engine': 'InnoDB',
152 152 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
153 153 )
154 154 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
155 155 app_settings_name = Column("app_settings_name", String(255), nullable=True, unique=None, default=None)
156 156 _app_settings_value = Column("app_settings_value", String(255), nullable=True, unique=None, default=None)
157 157 _app_settings_type = Column("app_settings_type", String(255), nullable=True, unique=None, default=None)
158 158
159 159 def __init__(self, key='', val='', type='unicode'):
160 160 self.app_settings_name = key
161 161 self.app_settings_value = val
162 162 self.app_settings_type = type
163 163
164 164 @validates('_app_settings_value')
165 165 def validate_settings_value(self, key, val):
166 166 assert type(val) == str
167 167 return val
168 168
169 169 @hybrid_property
170 170 def app_settings_value(self):
171 171 v = self._app_settings_value
172 172 if self.app_settings_name in ["ldap_active",
173 173 "default_repo_enable_statistics",
174 174 "default_repo_enable_locking",
175 175 "default_repo_private",
176 176 "default_repo_enable_downloads"]:
177 177 v = str2bool(v)
178 178 return v
179 179
180 180 @app_settings_value.setter
181 181 def app_settings_value(self, val):
182 182 """
183 183 Setter that will always make sure we use unicode in app_settings_value
184 184
185 185 :param val:
186 186 """
187 187 self._app_settings_value = safe_str(val)
188 188
189 189 def __unicode__(self):
190 190 return u"<%s('%s:%s')>" % (
191 191 self.__class__.__name__,
192 192 self.app_settings_name, self.app_settings_value
193 193 )
194 194
195 195
196 196 class RhodeCodeUi(Base, BaseModel):
197 197 __tablename__ = 'rhodecode_ui'
198 198 __table_args__ = (
199 199 UniqueConstraint('ui_key'),
200 200 {'extend_existing': True, 'mysql_engine': 'InnoDB',
201 201 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
202 202 )
203 203
204 204 HOOK_REPO_SIZE = 'changegroup.repo_size'
205 205 HOOK_PUSH = 'changegroup.push_logger'
206 206 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
207 207 HOOK_PULL = 'outgoing.pull_logger'
208 208 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
209 209
210 210 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
211 211 ui_section = Column("ui_section", String(255), nullable=True, unique=None, default=None)
212 212 ui_key = Column("ui_key", String(255), nullable=True, unique=None, default=None)
213 213 ui_value = Column("ui_value", String(255), nullable=True, unique=None, default=None)
214 214 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
215 215
216 216
217 217
218 218 class User(Base, BaseModel):
219 219 __tablename__ = 'users'
220 220 __table_args__ = (
221 221 UniqueConstraint('username'), UniqueConstraint('email'),
222 222 Index('u_username_idx', 'username'),
223 223 Index('u_email_idx', 'email'),
224 224 {'extend_existing': True, 'mysql_engine': 'InnoDB',
225 225 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
226 226 )
227 227 DEFAULT_USER = 'default'
228 228
229 229 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
230 230 username = Column("username", String(255), nullable=True, unique=None, default=None)
231 231 password = Column("password", String(255), nullable=True, unique=None, default=None)
232 232 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
233 233 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
234 234 name = Column("firstname", String(255), nullable=True, unique=None, default=None)
235 235 lastname = Column("lastname", String(255), nullable=True, unique=None, default=None)
236 236 _email = Column("email", String(255), nullable=True, unique=None, default=None)
237 237 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
238 238 ldap_dn = Column("ldap_dn", String(255), nullable=True, unique=None, default=None)
239 239 api_key = Column("api_key", String(255), nullable=True, unique=None, default=None)
240 240 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
241 241
242 242 user_log = relationship('UserLog')
243 243 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
244 244
245 245 repositories = relationship('Repository')
246 246 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
247 247 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
248 248
249 249 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
250 250 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
251 251
252 252 group_member = relationship('UserGroupMember', cascade='all')
253 253
254 254 notifications = relationship('UserNotification', cascade='all')
255 255 # notifications assigned to this user
256 256 user_created_notifications = relationship('Notification', cascade='all')
257 257 # comments created by this user
258 258 user_comments = relationship('ChangesetComment', cascade='all')
259 259 user_emails = relationship('UserEmailMap', cascade='all')
260 260
261 261 @hybrid_property
262 262 def email(self):
263 263 return self._email
264 264
265 265 @email.setter
266 266 def email(self, val):
267 267 self._email = val.lower() if val else None
268 268
269 269 @property
270 270 def firstname(self):
271 271 # alias for future
272 272 return self.name
273 273
274 274 @property
275 275 def username_and_name(self):
276 276 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
277 277
278 278 @property
279 279 def full_name(self):
280 280 return '%s %s' % (self.firstname, self.lastname)
281 281
282 282 @property
283 283 def full_contact(self):
284 284 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
285 285
286 286 @property
287 287 def short_contact(self):
288 288 return '%s %s' % (self.firstname, self.lastname)
289 289
290 290 @property
291 291 def is_admin(self):
292 292 return self.admin
293 293
294 294 @classmethod
295 295 def get_by_username(cls, username, case_insensitive=False, cache=False):
296 296 if case_insensitive:
297 297 q = cls.query().filter(cls.username.ilike(username))
298 298 else:
299 299 q = cls.query().filter(cls.username == username)
300 300
301 301 if cache:
302 302 q = q.options(FromCache(
303 303 "sql_cache_short",
304 304 "get_user_%s" % _hash_key(username)
305 305 )
306 306 )
307 307 return q.scalar()
308 308
309 309 @classmethod
310 310 def get_by_auth_token(cls, auth_token, cache=False):
311 311 q = cls.query().filter(cls.api_key == auth_token)
312 312
313 313 if cache:
314 314 q = q.options(FromCache("sql_cache_short",
315 315 "get_auth_token_%s" % auth_token))
316 316 return q.scalar()
317 317
318 318 @classmethod
319 319 def get_by_email(cls, email, case_insensitive=False, cache=False):
320 320 if case_insensitive:
321 321 q = cls.query().filter(cls.email.ilike(email))
322 322 else:
323 323 q = cls.query().filter(cls.email == email)
324 324
325 325 if cache:
326 326 q = q.options(FromCache("sql_cache_short",
327 327 "get_email_key_%s" % email))
328 328
329 329 ret = q.scalar()
330 330 if ret is None:
331 331 q = UserEmailMap.query()
332 332 # try fetching in alternate email map
333 333 if case_insensitive:
334 334 q = q.filter(UserEmailMap.email.ilike(email))
335 335 else:
336 336 q = q.filter(UserEmailMap.email == email)
337 337 q = q.options(joinedload(UserEmailMap.user))
338 338 if cache:
339 339 q = q.options(FromCache("sql_cache_short",
340 340 "get_email_map_key_%s" % email))
341 341 ret = getattr(q.scalar(), 'user', None)
342 342
343 343 return ret
344 344
345 345 @classmethod
346 346 def get_first_admin(cls):
347 347 user = User.query().filter(User.admin == True).first()
348 348 if user is None:
349 349 raise Exception('Missing administrative account!')
350 350 return user
351 351
352 352 @classmethod
353 353 def get_default_user(cls, cache=False):
354 354 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
355 355 if user is None:
356 356 raise Exception('Missing default account!')
357 357 return user
358 358
359 359
360 360
361 361
362 362 class UserEmailMap(Base, BaseModel):
363 363 __tablename__ = 'user_email_map'
364 364 __table_args__ = (
365 365 Index('uem_email_idx', 'email'),
366 366 UniqueConstraint('email'),
367 367 {'extend_existing': True, 'mysql_engine': 'InnoDB',
368 368 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
369 369 )
370 370
371 371
372 372 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
373 373 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
374 374 _email = Column("email", String(255), nullable=True, unique=False, default=None)
375 375 user = relationship('User', lazy='joined')
376 376
377 377 @validates('_email')
378 378 def validate_email(self, key, email):
379 379 # check if this email is not main one
380 380 main_email = Session().query(User).filter(User.email == email).scalar()
381 381 if main_email is not None:
382 382 raise AttributeError('email %s is present is user table' % email)
383 383 return email
384 384
385 385 @hybrid_property
386 386 def email(self):
387 387 return self._email
388 388
389 389 @email.setter
390 390 def email(self, val):
391 391 self._email = val.lower() if val else None
392 392
393 393
394 394 class UserIpMap(Base, BaseModel):
395 395 __tablename__ = 'user_ip_map'
396 396 __table_args__ = (
397 397 UniqueConstraint('user_id', 'ip_addr'),
398 398 {'extend_existing': True, 'mysql_engine': 'InnoDB',
399 399 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
400 400 )
401 401
402 402
403 403 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
404 404 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
405 405 ip_addr = Column("ip_addr", String(255), nullable=True, unique=False, default=None)
406 406 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
407 407 user = relationship('User', lazy='joined')
408 408
409 409
410 410 class UserLog(Base, BaseModel):
411 411 __tablename__ = 'user_logs'
412 412 __table_args__ = (
413 413 {'extend_existing': True, 'mysql_engine': 'InnoDB',
414 414 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
415 415 )
416 416 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
417 417 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
418 418 username = Column("username", String(255), nullable=True, unique=None, default=None)
419 419 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
420 420 repository_name = Column("repository_name", String(255), nullable=True, unique=None, default=None)
421 421 user_ip = Column("user_ip", String(255), nullable=True, unique=None, default=None)
422 422 action = Column("action", String(1200000), nullable=True, unique=None, default=None)
423 423 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
424 424
425 425 def __unicode__(self):
426 426 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
427 427 self.repository_name,
428 428 self.action)
429 429
430 430 user = relationship('User')
431 431 repository = relationship('Repository', cascade='')
432 432
433 433
434 434 class UserGroup(Base, BaseModel):
435 435 __tablename__ = 'users_groups'
436 436 __table_args__ = (
437 437 {'extend_existing': True, 'mysql_engine': 'InnoDB',
438 438 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
439 439 )
440 440
441 441 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
442 442 users_group_name = Column("users_group_name", String(255), nullable=False, unique=True, default=None)
443 443 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
444 444 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
445 445 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
446 446
447 447 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
448 448 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
449 449 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
450 450 users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
451 451 user_user_group_to_perm = relationship('UserUserGroupToPerm', cascade='all')
452 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm ', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
452 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
453 453
454 454 user = relationship('User')
455 455
456 456 def __unicode__(self):
457 457 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
458 458 self.users_group_id,
459 459 self.users_group_name)
460 460
461 461 @classmethod
462 462 def get_by_group_name(cls, group_name, cache=False,
463 463 case_insensitive=False):
464 464 if case_insensitive:
465 465 q = cls.query().filter(cls.users_group_name.ilike(group_name))
466 466 else:
467 467 q = cls.query().filter(cls.users_group_name == group_name)
468 468 if cache:
469 469 q = q.options(FromCache(
470 470 "sql_cache_short",
471 471 "get_user_%s" % _hash_key(group_name)
472 472 )
473 473 )
474 474 return q.scalar()
475 475
476 476 @classmethod
477 477 def get(cls, user_group_id, cache=False):
478 478 user_group = cls.query()
479 479 if cache:
480 480 user_group = user_group.options(FromCache("sql_cache_short",
481 481 "get_users_group_%s" % user_group_id))
482 482 return user_group.get(user_group_id)
483 483
484 484
485 485 class UserGroupMember(Base, BaseModel):
486 486 __tablename__ = 'users_groups_members'
487 487 __table_args__ = (
488 488 {'extend_existing': True, 'mysql_engine': 'InnoDB',
489 489 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
490 490 )
491 491
492 492 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
493 493 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
494 494 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
495 495
496 496 user = relationship('User', lazy='joined')
497 497 users_group = relationship('UserGroup')
498 498
499 499 def __init__(self, gr_id='', u_id=''):
500 500 self.users_group_id = gr_id
501 501 self.user_id = u_id
502 502
503 503
504 504 class RepositoryField(Base, BaseModel):
505 505 __tablename__ = 'repositories_fields'
506 506 __table_args__ = (
507 507 UniqueConstraint('repository_id', 'field_key'), # no-multi field
508 508 {'extend_existing': True, 'mysql_engine': 'InnoDB',
509 509 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
510 510 )
511 511 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
512 512
513 513 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
514 514 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
515 515 field_key = Column("field_key", String(250))
516 516 field_label = Column("field_label", String(1024), nullable=False)
517 517 field_value = Column("field_value", String(10000), nullable=False)
518 518 field_desc = Column("field_desc", String(1024), nullable=False)
519 519 field_type = Column("field_type", String(256), nullable=False, unique=None)
520 520 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
521 521
522 522 repository = relationship('Repository')
523 523
524 524 @classmethod
525 525 def get_by_key_name(cls, key, repo):
526 526 row = cls.query()\
527 527 .filter(cls.repository == repo)\
528 528 .filter(cls.field_key == key).scalar()
529 529 return row
530 530
531 531
532 532 class Repository(Base, BaseModel):
533 533 __tablename__ = 'repositories'
534 534 __table_args__ = (
535 535 UniqueConstraint('repo_name'),
536 536 Index('r_repo_name_idx', 'repo_name'),
537 537 {'extend_existing': True, 'mysql_engine': 'InnoDB',
538 538 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
539 539 )
540 540
541 541 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
542 542 repo_name = Column("repo_name", String(255), nullable=False, unique=True, default=None)
543 543 clone_uri = Column("clone_uri", String(255), nullable=True, unique=False, default=None)
544 544 repo_type = Column("repo_type", String(255), nullable=False, unique=False, default=None)
545 545 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
546 546 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
547 547 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
548 548 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
549 549 description = Column("description", String(10000), nullable=True, unique=None, default=None)
550 550 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
551 551 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
552 552 landing_rev = Column("landing_revision", String(255), nullable=False, unique=False, default=None)
553 553 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
554 554 _locked = Column("locked", String(255), nullable=True, unique=False, default=None)
555 555 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
556 556
557 557 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
558 558 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
559 559
560 560 user = relationship('User')
561 561 fork = relationship('Repository', remote_side=repo_id)
562 562 group = relationship('RepoGroup')
563 563 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
564 564 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
565 565 stats = relationship('Statistics', cascade='all', uselist=False)
566 566
567 567 followers = relationship('UserFollowing',
568 568 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
569 569 cascade='all')
570 570 extra_fields = relationship('RepositoryField',
571 571 cascade="all, delete, delete-orphan")
572 572
573 573 logs = relationship('UserLog')
574 574 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
575 575
576 576 pull_requests_org = relationship('PullRequest',
577 577 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
578 578 cascade="all, delete, delete-orphan")
579 579
580 580 pull_requests_other = relationship('PullRequest',
581 581 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
582 582 cascade="all, delete, delete-orphan")
583 583
584 584 def __unicode__(self):
585 585 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
586 586 safe_str(self.repo_name))
587 587
588 588 @classmethod
589 589 def get_by_repo_name(cls, repo_name):
590 590 q = Session().query(cls).filter(cls.repo_name == repo_name)
591 591 q = q.options(joinedload(Repository.fork))\
592 592 .options(joinedload(Repository.user))\
593 593 .options(joinedload(Repository.group))
594 594 return q.scalar()
595 595
596 596
597 597 class RepoGroup(Base, BaseModel):
598 598 __tablename__ = 'groups'
599 599 __table_args__ = (
600 600 UniqueConstraint('group_name', 'group_parent_id'),
601 601 {'extend_existing': True, 'mysql_engine': 'InnoDB',
602 602 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
603 603 )
604 604
605 605
606 606 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
607 607 group_name = Column("group_name", String(255), nullable=False, unique=True, default=None)
608 608 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
609 609 group_description = Column("group_description", String(10000), nullable=True, unique=None, default=None)
610 610 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
611 611 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
612 612
613 613 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
614 614 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
615 615 parent_group = relationship('RepoGroup', remote_side=group_id)
616 616 user = relationship('User')
617 617
618 618 def __init__(self, group_name='', parent_group=None):
619 619 self.group_name = group_name
620 620 self.parent_group = parent_group
621 621
622 622 def __unicode__(self):
623 623 return u"<%s('id:%s:%s')>" % (self.__class__.__name__, self.group_id,
624 624 self.group_name)
625 625
626 626 @classmethod
627 627 def url_sep(cls):
628 628 return URL_SEP
629 629
630 630 @classmethod
631 631 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
632 632 if case_insensitive:
633 633 gr = cls.query()\
634 634 .filter(cls.group_name.ilike(group_name))
635 635 else:
636 636 gr = cls.query()\
637 637 .filter(cls.group_name == group_name)
638 638 if cache:
639 639 gr = gr.options(FromCache(
640 640 "sql_cache_short",
641 641 "get_group_%s" % _hash_key(group_name)
642 642 )
643 643 )
644 644 return gr.scalar()
645 645
646 646
647 647 class Permission(Base, BaseModel):
648 648 __tablename__ = 'permissions'
649 649 __table_args__ = (
650 650 Index('p_perm_name_idx', 'permission_name'),
651 651 {'extend_existing': True, 'mysql_engine': 'InnoDB',
652 652 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
653 653 )
654 654 PERMS = [
655 655 ('hg.admin', _('RhodeCode Administrator')),
656 656
657 657 ('repository.none', _('Repository no access')),
658 658 ('repository.read', _('Repository read access')),
659 659 ('repository.write', _('Repository write access')),
660 660 ('repository.admin', _('Repository admin access')),
661 661
662 662 ('group.none', _('Repository group no access')),
663 663 ('group.read', _('Repository group read access')),
664 664 ('group.write', _('Repository group write access')),
665 665 ('group.admin', _('Repository group admin access')),
666 666
667 667 ('usergroup.none', _('User group no access')),
668 668 ('usergroup.read', _('User group read access')),
669 669 ('usergroup.write', _('User group write access')),
670 670 ('usergroup.admin', _('User group admin access')),
671 671
672 672 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
673 673 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
674 674
675 675 ('hg.usergroup.create.false', _('User Group creation disabled')),
676 676 ('hg.usergroup.create.true', _('User Group creation enabled')),
677 677
678 678 ('hg.create.none', _('Repository creation disabled')),
679 679 ('hg.create.repository', _('Repository creation enabled')),
680 680
681 681 ('hg.fork.none', _('Repository forking disabled')),
682 682 ('hg.fork.repository', _('Repository forking enabled')),
683 683
684 684 ('hg.register.none', _('Registration disabled')),
685 685 ('hg.register.manual_activate', _('User Registration with manual account activation')),
686 686 ('hg.register.auto_activate', _('User Registration with automatic account activation')),
687 687
688 688 ('hg.extern_activate.manual', _('Manual activation of external account')),
689 689 ('hg.extern_activate.auto', _('Automatic activation of external account')),
690 690
691 691 ]
692 692
693 693 #definition of system default permissions for DEFAULT user
694 694 DEFAULT_USER_PERMISSIONS = [
695 695 'repository.read',
696 696 'group.read',
697 697 'usergroup.read',
698 698 'hg.create.repository',
699 699 'hg.fork.repository',
700 700 'hg.register.manual_activate',
701 701 'hg.extern_activate.auto',
702 702 ]
703 703
704 704 # defines which permissions are more important higher the more important
705 705 # Weight defines which permissions are more important.
706 706 # The higher number the more important.
707 707 PERM_WEIGHTS = {
708 708 'repository.none': 0,
709 709 'repository.read': 1,
710 710 'repository.write': 3,
711 711 'repository.admin': 4,
712 712
713 713 'group.none': 0,
714 714 'group.read': 1,
715 715 'group.write': 3,
716 716 'group.admin': 4,
717 717
718 718 'usergroup.none': 0,
719 719 'usergroup.read': 1,
720 720 'usergroup.write': 3,
721 721 'usergroup.admin': 4,
722 722 'hg.repogroup.create.false': 0,
723 723 'hg.repogroup.create.true': 1,
724 724
725 725 'hg.usergroup.create.false': 0,
726 726 'hg.usergroup.create.true': 1,
727 727
728 728 'hg.fork.none': 0,
729 729 'hg.fork.repository': 1,
730 730 'hg.create.none': 0,
731 731 'hg.create.repository': 1
732 732 }
733 733
734 734 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
735 735 permission_name = Column("permission_name", String(255), nullable=True, unique=None, default=None)
736 736 permission_longname = Column("permission_longname", String(255), nullable=True, unique=None, default=None)
737 737
738 738 def __unicode__(self):
739 739 return u"<%s('%s:%s')>" % (
740 740 self.__class__.__name__, self.permission_id, self.permission_name
741 741 )
742 742
743 743 @classmethod
744 744 def get_by_key(cls, key):
745 745 return cls.query().filter(cls.permission_name == key).scalar()
746 746
747 747
748 748 class UserRepoToPerm(Base, BaseModel):
749 749 __tablename__ = 'repo_to_perm'
750 750 __table_args__ = (
751 751 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
752 752 {'extend_existing': True, 'mysql_engine': 'InnoDB',
753 753 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
754 754 )
755 755 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
756 756 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
757 757 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
758 758 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
759 759
760 760 user = relationship('User')
761 761 repository = relationship('Repository')
762 762 permission = relationship('Permission')
763 763
764 764 def __unicode__(self):
765 765 return u'<%s => %s >' % (self.user, self.repository)
766 766
767 767
768 768 class UserUserGroupToPerm(Base, BaseModel):
769 769 __tablename__ = 'user_user_group_to_perm'
770 770 __table_args__ = (
771 771 UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
772 772 {'extend_existing': True, 'mysql_engine': 'InnoDB',
773 773 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
774 774 )
775 775 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
776 776 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
777 777 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
778 778 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
779 779
780 780 user = relationship('User')
781 781 user_group = relationship('UserGroup')
782 782 permission = relationship('Permission')
783 783
784 784 def __unicode__(self):
785 785 return u'<%s => %s >' % (self.user, self.user_group)
786 786
787 787
788 788 class UserToPerm(Base, BaseModel):
789 789 __tablename__ = 'user_to_perm'
790 790 __table_args__ = (
791 791 UniqueConstraint('user_id', 'permission_id'),
792 792 {'extend_existing': True, 'mysql_engine': 'InnoDB',
793 793 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
794 794 )
795 795 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
796 796 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
797 797 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
798 798
799 799 user = relationship('User')
800 800 permission = relationship('Permission', lazy='joined')
801 801
802 802 def __unicode__(self):
803 803 return u'<%s => %s >' % (self.user, self.permission)
804 804
805 805
806 806 class UserGroupRepoToPerm(Base, BaseModel):
807 807 __tablename__ = 'users_group_repo_to_perm'
808 808 __table_args__ = (
809 809 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
810 810 {'extend_existing': True, 'mysql_engine': 'InnoDB',
811 811 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
812 812 )
813 813 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
814 814 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
815 815 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
816 816 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
817 817
818 818 users_group = relationship('UserGroup')
819 819 permission = relationship('Permission')
820 820 repository = relationship('Repository')
821 821
822 822 def __unicode__(self):
823 823 return u'<UserGroupRepoToPerm:%s => %s >' % (self.users_group, self.repository)
824 824
825 825
826 826 class UserGroupUserGroupToPerm(Base, BaseModel):
827 827 __tablename__ = 'user_group_user_group_to_perm'
828 828 __table_args__ = (
829 829 UniqueConstraint('target_user_group_id', 'user_group_id', 'permission_id'),
830 830 CheckConstraint('target_user_group_id != user_group_id'),
831 831 {'extend_existing': True, 'mysql_engine': 'InnoDB',
832 832 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
833 833 )
834 834 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 835 target_user_group_id = Column("target_user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
836 836 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
837 837 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
838 838
839 839 target_user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
840 840 user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
841 841 permission = relationship('Permission')
842 842
843 843 def __unicode__(self):
844 844 return u'<UserGroupUserGroup:%s => %s >' % (self.target_user_group, self.user_group)
845 845
846 846
847 847 class UserGroupToPerm(Base, BaseModel):
848 848 __tablename__ = 'users_group_to_perm'
849 849 __table_args__ = (
850 850 UniqueConstraint('users_group_id', 'permission_id',),
851 851 {'extend_existing': True, 'mysql_engine': 'InnoDB',
852 852 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
853 853 )
854 854 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
855 855 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
856 856 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
857 857
858 858 users_group = relationship('UserGroup')
859 859 permission = relationship('Permission')
860 860
861 861
862 862 class UserRepoGroupToPerm(Base, BaseModel):
863 863 __tablename__ = 'user_repo_group_to_perm'
864 864 __table_args__ = (
865 865 UniqueConstraint('user_id', 'group_id', 'permission_id'),
866 866 {'extend_existing': True, 'mysql_engine': 'InnoDB',
867 867 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
868 868 )
869 869
870 870 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
871 871 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
872 872 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
873 873 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
874 874
875 875 user = relationship('User')
876 876 group = relationship('RepoGroup')
877 877 permission = relationship('Permission')
878 878
879 879
880 880 class UserGroupRepoGroupToPerm(Base, BaseModel):
881 881 __tablename__ = 'users_group_repo_group_to_perm'
882 882 __table_args__ = (
883 883 UniqueConstraint('users_group_id', 'group_id'),
884 884 {'extend_existing': True, 'mysql_engine': 'InnoDB',
885 885 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
886 886 )
887 887
888 888 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 889 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
890 890 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
891 891 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
892 892
893 893 users_group = relationship('UserGroup')
894 894 permission = relationship('Permission')
895 895 group = relationship('RepoGroup')
896 896
897 897
898 898 class Statistics(Base, BaseModel):
899 899 __tablename__ = 'statistics'
900 900 __table_args__ = (
901 901 UniqueConstraint('repository_id'),
902 902 {'extend_existing': True, 'mysql_engine': 'InnoDB',
903 903 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
904 904 )
905 905 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
906 906 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
907 907 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
908 908 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
909 909 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
910 910 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
911 911
912 912 repository = relationship('Repository', single_parent=True)
913 913
914 914
915 915 class UserFollowing(Base, BaseModel):
916 916 __tablename__ = 'user_followings'
917 917 __table_args__ = (
918 918 UniqueConstraint('user_id', 'follows_repository_id'),
919 919 UniqueConstraint('user_id', 'follows_user_id'),
920 920 {'extend_existing': True, 'mysql_engine': 'InnoDB',
921 921 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
922 922 )
923 923
924 924 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
925 925 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
926 926 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
927 927 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
928 928 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
929 929
930 930 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
931 931
932 932 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
933 933 follows_repository = relationship('Repository', order_by='Repository.repo_name')
934 934
935 935
936 936 class CacheInvalidation(Base, BaseModel):
937 937 __tablename__ = 'cache_invalidation'
938 938 __table_args__ = (
939 939 UniqueConstraint('cache_key'),
940 940 Index('key_idx', 'cache_key'),
941 941 {'extend_existing': True, 'mysql_engine': 'InnoDB',
942 942 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
943 943 )
944 944 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
945 945 cache_key = Column("cache_key", String(255), nullable=True, unique=None, default=None)
946 946 cache_args = Column("cache_args", String(255), nullable=True, unique=None, default=None)
947 947 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
948 948
949 949 def __init__(self, cache_key, cache_args=''):
950 950 self.cache_key = cache_key
951 951 self.cache_args = cache_args
952 952 self.cache_active = False
953 953
954 954
955 955 class ChangesetComment(Base, BaseModel):
956 956 __tablename__ = 'changeset_comments'
957 957 __table_args__ = (
958 958 Index('cc_revision_idx', 'revision'),
959 959 {'extend_existing': True, 'mysql_engine': 'InnoDB',
960 960 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
961 961 )
962 962 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
963 963 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
964 964 revision = Column('revision', String(40), nullable=True)
965 965 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
966 966 line_no = Column('line_no', Unicode(10), nullable=True)
967 967 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
968 968 f_path = Column('f_path', Unicode(1000), nullable=True)
969 969 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
970 970 text = Column('text', UnicodeText().with_variant(UnicodeText(25000), 'mysql'), nullable=False)
971 971 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
972 972 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
973 973
974 974 author = relationship('User', lazy='joined')
975 975 repo = relationship('Repository')
976 976 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
977 977 pull_request = relationship('PullRequest', lazy='joined')
978 978
979 979
980 980 class ChangesetStatus(Base, BaseModel):
981 981 __tablename__ = 'changeset_statuses'
982 982 __table_args__ = (
983 983 Index('cs_revision_idx', 'revision'),
984 984 Index('cs_version_idx', 'version'),
985 985 UniqueConstraint('repo_id', 'revision', 'version'),
986 986 {'extend_existing': True, 'mysql_engine': 'InnoDB',
987 987 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
988 988 )
989 989 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
990 990 STATUS_APPROVED = 'approved'
991 991 STATUS_REJECTED = 'rejected'
992 992 STATUS_UNDER_REVIEW = 'under_review'
993 993
994 994 STATUSES = [
995 995 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
996 996 (STATUS_APPROVED, _("Approved")),
997 997 (STATUS_REJECTED, _("Rejected")),
998 998 (STATUS_UNDER_REVIEW, _("Under Review")),
999 999 ]
1000 1000
1001 1001 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1002 1002 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1003 1003 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1004 1004 revision = Column('revision', String(40), nullable=False)
1005 1005 status = Column('status', String(128), nullable=False, default=DEFAULT)
1006 1006 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1007 1007 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1008 1008 version = Column('version', Integer(), nullable=False, default=0)
1009 1009 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1010 1010
1011 1011 author = relationship('User', lazy='joined')
1012 1012 repo = relationship('Repository')
1013 1013 comment = relationship('ChangesetComment', lazy='joined')
1014 1014 pull_request = relationship('PullRequest', lazy='joined')
1015 1015
1016 1016
1017 1017
1018 1018 class PullRequest(Base, BaseModel):
1019 1019 __tablename__ = 'pull_requests'
1020 1020 __table_args__ = (
1021 1021 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1022 1022 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1023 1023 )
1024 1024
1025 1025 STATUS_NEW = u'new'
1026 1026 STATUS_OPEN = u'open'
1027 1027 STATUS_CLOSED = u'closed'
1028 1028
1029 1029 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1030 1030 title = Column('title', Unicode(256), nullable=True)
1031 1031 description = Column('description', UnicodeText().with_variant(UnicodeText(10240), 'mysql'), nullable=True)
1032 1032 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1033 1033 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1034 1034 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1035 1035 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1036 1036 _revisions = Column('revisions', UnicodeText().with_variant(UnicodeText(20500), 'mysql'))
1037 1037 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1038 1038 org_ref = Column('org_ref', Unicode(256), nullable=False)
1039 1039 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1040 1040 other_ref = Column('other_ref', Unicode(256), nullable=False)
1041 1041
1042 1042 author = relationship('User', lazy='joined')
1043 1043 reviewers = relationship('PullRequestReviewers',
1044 1044 cascade="all, delete, delete-orphan")
1045 1045 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1046 1046 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1047 1047 statuses = relationship('ChangesetStatus')
1048 1048 comments = relationship('ChangesetComment',
1049 1049 cascade="all, delete, delete-orphan")
1050 1050
1051 1051
1052 1052 class PullRequestReviewers(Base, BaseModel):
1053 1053 __tablename__ = 'pull_request_reviewers'
1054 1054 __table_args__ = (
1055 1055 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1056 1056 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1057 1057 )
1058 1058
1059 1059 def __init__(self, user=None, pull_request=None):
1060 1060 self.user = user
1061 1061 self.pull_request = pull_request
1062 1062
1063 1063 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1064 1064 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1065 1065 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1066 1066
1067 1067 user = relationship('User')
1068 1068 pull_request = relationship('PullRequest')
1069 1069
1070 1070
1071 1071 class Notification(Base, BaseModel):
1072 1072 __tablename__ = 'notifications'
1073 1073 __table_args__ = (
1074 1074 Index('notification_type_idx', 'type'),
1075 1075 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1076 1076 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1077 1077 )
1078 1078
1079 1079 TYPE_CHANGESET_COMMENT = u'cs_comment'
1080 1080 TYPE_MESSAGE = u'message'
1081 1081 TYPE_MENTION = u'mention'
1082 1082 TYPE_REGISTRATION = u'registration'
1083 1083 TYPE_PULL_REQUEST = u'pull_request'
1084 1084 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1085 1085
1086 1086 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1087 1087 subject = Column('subject', Unicode(512), nullable=True)
1088 1088 body = Column('body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True)
1089 1089 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1090 1090 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1091 1091 type_ = Column('type', Unicode(256))
1092 1092
1093 1093 created_by_user = relationship('User')
1094 1094 notifications_to_users = relationship('UserNotification', lazy='joined',
1095 1095 cascade="all, delete, delete-orphan")
1096 1096
1097 1097
1098 1098 class UserNotification(Base, BaseModel):
1099 1099 __tablename__ = 'user_to_notification'
1100 1100 __table_args__ = (
1101 1101 UniqueConstraint('user_id', 'notification_id'),
1102 1102 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1103 1103 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1104 1104 )
1105 1105 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1106 1106 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1107 1107 read = Column('read', Boolean, default=False)
1108 1108 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1109 1109
1110 1110 user = relationship('User', lazy="joined")
1111 1111 notification = relationship('Notification', lazy="joined",
1112 1112 order_by=lambda: Notification.created_on.desc(),)
1113 1113
1114 1114
1115 1115 class Gist(Base, BaseModel):
1116 1116 __tablename__ = 'gists'
1117 1117 __table_args__ = (
1118 1118 Index('g_gist_access_id_idx', 'gist_access_id'),
1119 1119 Index('g_created_on_idx', 'created_on'),
1120 1120 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1121 1121 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1122 1122 )
1123 1123 GIST_PUBLIC = u'public'
1124 1124 GIST_PRIVATE = u'private'
1125 1125
1126 1126 gist_id = Column('gist_id', Integer(), primary_key=True)
1127 1127 gist_access_id = Column('gist_access_id', Unicode(250))
1128 1128 gist_description = Column('gist_description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
1129 1129 gist_owner = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=True)
1130 1130 gist_expires = Column('gist_expires', Float(53), nullable=False)
1131 1131 gist_type = Column('gist_type', Unicode(128), nullable=False)
1132 1132 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1133 1133 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1134 1134
1135 1135 owner = relationship('User')
1136 1136
1137 1137
1138 1138 class DbMigrateVersion(Base, BaseModel):
1139 1139 __tablename__ = 'db_migrate_version'
1140 1140 __table_args__ = (
1141 1141 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1142 1142 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1143 1143 )
1144 1144 repository_id = Column('repository_id', String(250), primary_key=True)
1145 1145 repository_path = Column('repository_path', Text)
1146 1146 version = Column('version', Integer)
@@ -1,1169 +1,1169 b''
1 1
2 2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 3 #
4 4 # This program is free software: you can redistribute it and/or modify
5 5 # it under the terms of the GNU Affero General Public License, version 3
6 6 # (only), as published by the Free Software Foundation.
7 7 #
8 8 # This program is distributed in the hope that it will be useful,
9 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 11 # GNU General Public License for more details.
12 12 #
13 13 # You should have received a copy of the GNU Affero General Public License
14 14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 15 #
16 16 # This program is dual-licensed. If you wish to learn more about the
17 17 # RhodeCode Enterprise Edition, including its added features, Support services,
18 18 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 19
20 20 import os
21 21 import time
22 22 import logging
23 23 import datetime
24 24 import traceback
25 25 import hashlib
26 26 import collections
27 27 import functools
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 remove_prefix, time_to_datetime, aslist, Optional, safe_int
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.items():
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 SETTINGS_TYPES = {
150 150 'str': safe_str,
151 151 'int': safe_int,
152 152 'unicode': safe_str,
153 153 'bool': str2bool,
154 154 'list': functools.partial(aslist, sep=',')
155 155 }
156 156 __tablename__ = 'rhodecode_settings'
157 157 __table_args__ = (
158 158 UniqueConstraint('app_settings_name'),
159 159 {'extend_existing': True, 'mysql_engine': 'InnoDB',
160 160 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
161 161 )
162 162 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
163 163 app_settings_name = Column("app_settings_name", String(255), nullable=True, unique=None, default=None)
164 164 _app_settings_value = Column("app_settings_value", String(4096), nullable=True, unique=None, default=None)
165 165 _app_settings_type = Column("app_settings_type", String(255), nullable=True, unique=None, default=None)
166 166
167 167 def __init__(self, key='', val='', type='unicode'):
168 168 self.app_settings_name = key
169 169 self.app_settings_value = val
170 170 self.app_settings_type = type
171 171
172 172 @validates('_app_settings_value')
173 173 def validate_settings_value(self, key, val):
174 174 assert type(val) == str
175 175 return val
176 176
177 177 @hybrid_property
178 178 def app_settings_value(self):
179 179 v = self._app_settings_value
180 180 _type = self.app_settings_type
181 181 converter = self.SETTINGS_TYPES.get(_type) or self.SETTINGS_TYPES['unicode']
182 182 return converter(v)
183 183
184 184 @app_settings_value.setter
185 185 def app_settings_value(self, val):
186 186 """
187 187 Setter that will always make sure we use unicode in app_settings_value
188 188
189 189 :param val:
190 190 """
191 191 self._app_settings_value = safe_str(val)
192 192
193 193 @hybrid_property
194 194 def app_settings_type(self):
195 195 return self._app_settings_type
196 196
197 197 @app_settings_type.setter
198 198 def app_settings_type(self, val):
199 199 if val not in self.SETTINGS_TYPES:
200 200 raise Exception('type must be one of %s got %s'
201 201 % (self.SETTINGS_TYPES.keys(), val))
202 202 self._app_settings_type = val
203 203
204 204 def __unicode__(self):
205 205 return u"<%s('%s:%s[%s]')>" % (
206 206 self.__class__.__name__,
207 207 self.app_settings_name, self.app_settings_value, self.app_settings_type
208 208 )
209 209
210 210
211 211 class RhodeCodeUi(Base, BaseModel):
212 212 __tablename__ = 'rhodecode_ui'
213 213 __table_args__ = (
214 214 UniqueConstraint('ui_key'),
215 215 {'extend_existing': True, 'mysql_engine': 'InnoDB',
216 216 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
217 217 )
218 218
219 219 HOOK_REPO_SIZE = 'changegroup.repo_size'
220 220 HOOK_PUSH = 'changegroup.push_logger'
221 221 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
222 222 HOOK_PULL = 'outgoing.pull_logger'
223 223 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
224 224
225 225 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
226 226 ui_section = Column("ui_section", String(255), nullable=True, unique=None, default=None)
227 227 ui_key = Column("ui_key", String(255), nullable=True, unique=None, default=None)
228 228 ui_value = Column("ui_value", String(255), nullable=True, unique=None, default=None)
229 229 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
230 230
231 231
232 232
233 233 class User(Base, BaseModel):
234 234 __tablename__ = 'users'
235 235 __table_args__ = (
236 236 UniqueConstraint('username'), UniqueConstraint('email'),
237 237 Index('u_username_idx', 'username'),
238 238 Index('u_email_idx', 'email'),
239 239 {'extend_existing': True, 'mysql_engine': 'InnoDB',
240 240 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
241 241 )
242 242 DEFAULT_USER = 'default'
243 243
244 244 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
245 245 username = Column("username", String(255), nullable=True, unique=None, default=None)
246 246 password = Column("password", String(255), nullable=True, unique=None, default=None)
247 247 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
248 248 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
249 249 name = Column("firstname", String(255), nullable=True, unique=None, default=None)
250 250 lastname = Column("lastname", String(255), nullable=True, unique=None, default=None)
251 251 _email = Column("email", String(255), nullable=True, unique=None, default=None)
252 252 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
253 253 extern_type = Column("extern_type", String(255), nullable=True, unique=None, default=None)
254 254 extern_name = Column("extern_name", String(255), nullable=True, unique=None, default=None)
255 255 #for migration reasons, this is going to be later deleted
256 256 ldap_dn = Column("ldap_dn", String(255), nullable=True, unique=None, default=None)
257 257
258 258 api_key = Column("api_key", String(255), nullable=True, unique=None, default=None)
259 259 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
260 260 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
261 261
262 262 user_log = relationship('UserLog')
263 263 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
264 264
265 265 repositories = relationship('Repository')
266 266 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
267 267 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
268 268
269 269 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
270 270 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
271 271
272 272 group_member = relationship('UserGroupMember', cascade='all')
273 273
274 274 notifications = relationship('UserNotification', cascade='all')
275 275 # notifications assigned to this user
276 276 user_created_notifications = relationship('Notification', cascade='all')
277 277 # comments created by this user
278 278 user_comments = relationship('ChangesetComment', cascade='all')
279 279 user_emails = relationship('UserEmailMap', cascade='all')
280 280
281 281 @hybrid_property
282 282 def email(self):
283 283 return self._email
284 284
285 285 @email.setter
286 286 def email(self, val):
287 287 self._email = val.lower() if val else None
288 288
289 289 @property
290 290 def firstname(self):
291 291 # alias for future
292 292 return self.name
293 293
294 294 @property
295 295 def username_and_name(self):
296 296 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
297 297
298 298 @property
299 299 def full_name(self):
300 300 return '%s %s' % (self.firstname, self.lastname)
301 301
302 302 @property
303 303 def full_contact(self):
304 304 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
305 305
306 306 @property
307 307 def short_contact(self):
308 308 return '%s %s' % (self.firstname, self.lastname)
309 309
310 310 @property
311 311 def is_admin(self):
312 312 return self.admin
313 313
314 314 @classmethod
315 315 def get_by_username(cls, username, case_insensitive=False, cache=False):
316 316 if case_insensitive:
317 317 q = cls.query().filter(cls.username.ilike(username))
318 318 else:
319 319 q = cls.query().filter(cls.username == username)
320 320
321 321 if cache:
322 322 q = q.options(FromCache(
323 323 "sql_cache_short",
324 324 "get_user_%s" % _hash_key(username)
325 325 )
326 326 )
327 327 return q.scalar()
328 328
329 329 @classmethod
330 330 def get_by_auth_token(cls, auth_token, cache=False):
331 331 q = cls.query().filter(cls.api_key == auth_token)
332 332
333 333 if cache:
334 334 q = q.options(FromCache("sql_cache_short",
335 335 "get_auth_token_%s" % auth_token))
336 336 return q.scalar()
337 337
338 338 @classmethod
339 339 def get_by_email(cls, email, case_insensitive=False, cache=False):
340 340 if case_insensitive:
341 341 q = cls.query().filter(cls.email.ilike(email))
342 342 else:
343 343 q = cls.query().filter(cls.email == email)
344 344
345 345 if cache:
346 346 q = q.options(FromCache("sql_cache_short",
347 347 "get_email_key_%s" % email))
348 348
349 349 ret = q.scalar()
350 350 if ret is None:
351 351 q = UserEmailMap.query()
352 352 # try fetching in alternate email map
353 353 if case_insensitive:
354 354 q = q.filter(UserEmailMap.email.ilike(email))
355 355 else:
356 356 q = q.filter(UserEmailMap.email == email)
357 357 q = q.options(joinedload(UserEmailMap.user))
358 358 if cache:
359 359 q = q.options(FromCache("sql_cache_short",
360 360 "get_email_map_key_%s" % email))
361 361 ret = getattr(q.scalar(), 'user', None)
362 362
363 363 return ret
364 364
365 365 @classmethod
366 366 def get_first_admin(cls):
367 367 user = User.query().filter(User.admin == True).first()
368 368 if user is None:
369 369 raise Exception('Missing administrative account!')
370 370 return user
371 371
372 372 @classmethod
373 373 def get_default_user(cls, cache=False):
374 374 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
375 375 if user is None:
376 376 raise Exception('Missing default account!')
377 377 return user
378 378
379 379
380 380
381 381
382 382 class UserEmailMap(Base, BaseModel):
383 383 __tablename__ = 'user_email_map'
384 384 __table_args__ = (
385 385 Index('uem_email_idx', 'email'),
386 386 UniqueConstraint('email'),
387 387 {'extend_existing': True, 'mysql_engine': 'InnoDB',
388 388 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
389 389 )
390 390
391 391
392 392 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
393 393 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
394 394 _email = Column("email", String(255), nullable=True, unique=False, default=None)
395 395 user = relationship('User', lazy='joined')
396 396
397 397 @validates('_email')
398 398 def validate_email(self, key, email):
399 399 # check if this email is not main one
400 400 main_email = Session().query(User).filter(User.email == email).scalar()
401 401 if main_email is not None:
402 402 raise AttributeError('email %s is present is user table' % email)
403 403 return email
404 404
405 405 @hybrid_property
406 406 def email(self):
407 407 return self._email
408 408
409 409 @email.setter
410 410 def email(self, val):
411 411 self._email = val.lower() if val else None
412 412
413 413
414 414 class UserIpMap(Base, BaseModel):
415 415 __tablename__ = 'user_ip_map'
416 416 __table_args__ = (
417 417 UniqueConstraint('user_id', 'ip_addr'),
418 418 {'extend_existing': True, 'mysql_engine': 'InnoDB',
419 419 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
420 420 )
421 421
422 422
423 423 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
424 424 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
425 425 ip_addr = Column("ip_addr", String(255), nullable=True, unique=False, default=None)
426 426 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
427 427 user = relationship('User', lazy='joined')
428 428
429 429
430 430 class UserLog(Base, BaseModel):
431 431 __tablename__ = 'user_logs'
432 432 __table_args__ = (
433 433 {'extend_existing': True, 'mysql_engine': 'InnoDB',
434 434 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
435 435 )
436 436 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
437 437 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
438 438 username = Column("username", String(255), nullable=True, unique=None, default=None)
439 439 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
440 440 repository_name = Column("repository_name", String(255), nullable=True, unique=None, default=None)
441 441 user_ip = Column("user_ip", String(255), nullable=True, unique=None, default=None)
442 442 action = Column("action", String(1200000), nullable=True, unique=None, default=None)
443 443 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
444 444
445 445 def __unicode__(self):
446 446 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
447 447 self.repository_name,
448 448 self.action)
449 449
450 450 user = relationship('User')
451 451 repository = relationship('Repository', cascade='')
452 452
453 453
454 454 class UserGroup(Base, BaseModel):
455 455 __tablename__ = 'users_groups'
456 456 __table_args__ = (
457 457 {'extend_existing': True, 'mysql_engine': 'InnoDB',
458 458 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
459 459 )
460 460
461 461 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
462 462 users_group_name = Column("users_group_name", String(255), nullable=False, unique=True, default=None)
463 463 user_group_description = Column("user_group_description", String(10000), nullable=True, unique=None, default=None)
464 464 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
465 465 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
466 466 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
467 467 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
468 468
469 469 # don't trigger lazy load for migrations
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 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm ', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
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_str(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 624 {'extend_existing': True, 'mysql_engine': 'InnoDB',
625 625 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
626 626 )
627 627
628 628
629 629 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
630 630 group_name = Column("group_name", String(255), nullable=False, unique=True, default=None)
631 631 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
632 632 group_description = Column("group_description", String(10000), nullable=True, unique=None, default=None)
633 633 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
634 634 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
635 635
636 636 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
637 637 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
638 638 parent_group = relationship('RepoGroup', remote_side=group_id)
639 639 user = relationship('User')
640 640
641 641 def __init__(self, group_name='', parent_group=None):
642 642 self.group_name = group_name
643 643 self.parent_group = parent_group
644 644
645 645 def __unicode__(self):
646 646 return u"<%s('id:%s:%s')>" % (self.__class__.__name__, self.group_id,
647 647 self.group_name)
648 648
649 649 @classmethod
650 650 def url_sep(cls):
651 651 return URL_SEP
652 652
653 653 @classmethod
654 654 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
655 655 if case_insensitive:
656 656 gr = cls.query()\
657 657 .filter(cls.group_name.ilike(group_name))
658 658 else:
659 659 gr = cls.query()\
660 660 .filter(cls.group_name == group_name)
661 661 if cache:
662 662 gr = gr.options(FromCache(
663 663 "sql_cache_short",
664 664 "get_group_%s" % _hash_key(group_name)
665 665 )
666 666 )
667 667 return gr.scalar()
668 668
669 669
670 670 class Permission(Base, BaseModel):
671 671 __tablename__ = 'permissions'
672 672 __table_args__ = (
673 673 Index('p_perm_name_idx', 'permission_name'),
674 674 {'extend_existing': True, 'mysql_engine': 'InnoDB',
675 675 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
676 676 )
677 677 PERMS = [
678 678 ('hg.admin', _('RhodeCode Administrator')),
679 679
680 680 ('repository.none', _('Repository no access')),
681 681 ('repository.read', _('Repository read access')),
682 682 ('repository.write', _('Repository write access')),
683 683 ('repository.admin', _('Repository admin access')),
684 684
685 685 ('group.none', _('Repository group no access')),
686 686 ('group.read', _('Repository group read access')),
687 687 ('group.write', _('Repository group write access')),
688 688 ('group.admin', _('Repository group admin access')),
689 689
690 690 ('usergroup.none', _('User group no access')),
691 691 ('usergroup.read', _('User group read access')),
692 692 ('usergroup.write', _('User group write access')),
693 693 ('usergroup.admin', _('User group admin access')),
694 694
695 695 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
696 696 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
697 697
698 698 ('hg.usergroup.create.false', _('User Group creation disabled')),
699 699 ('hg.usergroup.create.true', _('User Group creation enabled')),
700 700
701 701 ('hg.create.none', _('Repository creation disabled')),
702 702 ('hg.create.repository', _('Repository creation enabled')),
703 703
704 704 ('hg.fork.none', _('Repository forking disabled')),
705 705 ('hg.fork.repository', _('Repository forking enabled')),
706 706
707 707 ('hg.register.none', _('Registration disabled')),
708 708 ('hg.register.manual_activate', _('User Registration with manual account activation')),
709 709 ('hg.register.auto_activate', _('User Registration with automatic account activation')),
710 710
711 711 ('hg.extern_activate.manual', _('Manual activation of external account')),
712 712 ('hg.extern_activate.auto', _('Automatic activation of external account')),
713 713
714 714 ]
715 715
716 716 #definition of system default permissions for DEFAULT user
717 717 DEFAULT_USER_PERMISSIONS = [
718 718 'repository.read',
719 719 'group.read',
720 720 'usergroup.read',
721 721 'hg.create.repository',
722 722 'hg.fork.repository',
723 723 'hg.register.manual_activate',
724 724 'hg.extern_activate.auto',
725 725 ]
726 726
727 727 # defines which permissions are more important higher the more important
728 728 # Weight defines which permissions are more important.
729 729 # The higher number the more important.
730 730 PERM_WEIGHTS = {
731 731 'repository.none': 0,
732 732 'repository.read': 1,
733 733 'repository.write': 3,
734 734 'repository.admin': 4,
735 735
736 736 'group.none': 0,
737 737 'group.read': 1,
738 738 'group.write': 3,
739 739 'group.admin': 4,
740 740
741 741 'usergroup.none': 0,
742 742 'usergroup.read': 1,
743 743 'usergroup.write': 3,
744 744 'usergroup.admin': 4,
745 745 'hg.repogroup.create.false': 0,
746 746 'hg.repogroup.create.true': 1,
747 747
748 748 'hg.usergroup.create.false': 0,
749 749 'hg.usergroup.create.true': 1,
750 750
751 751 'hg.fork.none': 0,
752 752 'hg.fork.repository': 1,
753 753 'hg.create.none': 0,
754 754 'hg.create.repository': 1
755 755 }
756 756
757 757 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
758 758 permission_name = Column("permission_name", String(255), nullable=True, unique=None, default=None)
759 759 permission_longname = Column("permission_longname", String(255), nullable=True, unique=None, default=None)
760 760
761 761 def __unicode__(self):
762 762 return u"<%s('%s:%s')>" % (
763 763 self.__class__.__name__, self.permission_id, self.permission_name
764 764 )
765 765
766 766 @classmethod
767 767 def get_by_key(cls, key):
768 768 return cls.query().filter(cls.permission_name == key).scalar()
769 769
770 770
771 771 class UserRepoToPerm(Base, BaseModel):
772 772 __tablename__ = 'repo_to_perm'
773 773 __table_args__ = (
774 774 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
775 775 {'extend_existing': True, 'mysql_engine': 'InnoDB',
776 776 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
777 777 )
778 778 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
779 779 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
780 780 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
781 781 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
782 782
783 783 user = relationship('User')
784 784 repository = relationship('Repository')
785 785 permission = relationship('Permission')
786 786
787 787 def __unicode__(self):
788 788 return u'<%s => %s >' % (self.user, self.repository)
789 789
790 790
791 791 class UserUserGroupToPerm(Base, BaseModel):
792 792 __tablename__ = 'user_user_group_to_perm'
793 793 __table_args__ = (
794 794 UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
795 795 {'extend_existing': True, 'mysql_engine': 'InnoDB',
796 796 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
797 797 )
798 798 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
799 799 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
800 800 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
801 801 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
802 802
803 803 user = relationship('User')
804 804 user_group = relationship('UserGroup')
805 805 permission = relationship('Permission')
806 806
807 807 def __unicode__(self):
808 808 return u'<%s => %s >' % (self.user, self.user_group)
809 809
810 810
811 811 class UserToPerm(Base, BaseModel):
812 812 __tablename__ = 'user_to_perm'
813 813 __table_args__ = (
814 814 UniqueConstraint('user_id', 'permission_id'),
815 815 {'extend_existing': True, 'mysql_engine': 'InnoDB',
816 816 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
817 817 )
818 818 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
819 819 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
820 820 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
821 821
822 822 user = relationship('User')
823 823 permission = relationship('Permission', lazy='joined')
824 824
825 825 def __unicode__(self):
826 826 return u'<%s => %s >' % (self.user, self.permission)
827 827
828 828
829 829 class UserGroupRepoToPerm(Base, BaseModel):
830 830 __tablename__ = 'users_group_repo_to_perm'
831 831 __table_args__ = (
832 832 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
833 833 {'extend_existing': True, 'mysql_engine': 'InnoDB',
834 834 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
835 835 )
836 836 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
837 837 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
838 838 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
839 839 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
840 840
841 841 users_group = relationship('UserGroup')
842 842 permission = relationship('Permission')
843 843 repository = relationship('Repository')
844 844
845 845 def __unicode__(self):
846 846 return u'<UserGroupRepoToPerm:%s => %s >' % (self.users_group, self.repository)
847 847
848 848
849 849 class UserGroupUserGroupToPerm(Base, BaseModel):
850 850 __tablename__ = 'user_group_user_group_to_perm'
851 851 __table_args__ = (
852 852 UniqueConstraint('target_user_group_id', 'user_group_id', 'permission_id'),
853 853 CheckConstraint('target_user_group_id != user_group_id'),
854 854 {'extend_existing': True, 'mysql_engine': 'InnoDB',
855 855 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
856 856 )
857 857 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)
858 858 target_user_group_id = Column("target_user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
859 859 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
860 860 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
861 861
862 862 target_user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
863 863 user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
864 864 permission = relationship('Permission')
865 865
866 866 def __unicode__(self):
867 867 return u'<UserGroupUserGroup:%s => %s >' % (self.target_user_group, self.user_group)
868 868
869 869
870 870 class UserGroupToPerm(Base, BaseModel):
871 871 __tablename__ = 'users_group_to_perm'
872 872 __table_args__ = (
873 873 UniqueConstraint('users_group_id', 'permission_id',),
874 874 {'extend_existing': True, 'mysql_engine': 'InnoDB',
875 875 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
876 876 )
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 class UserRepoGroupToPerm(Base, BaseModel):
886 886 __tablename__ = 'user_repo_group_to_perm'
887 887 __table_args__ = (
888 888 UniqueConstraint('user_id', 'group_id', 'permission_id'),
889 889 {'extend_existing': True, 'mysql_engine': 'InnoDB',
890 890 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
891 891 )
892 892
893 893 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
894 894 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
895 895 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
896 896 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
897 897
898 898 user = relationship('User')
899 899 group = relationship('RepoGroup')
900 900 permission = relationship('Permission')
901 901
902 902
903 903 class UserGroupRepoGroupToPerm(Base, BaseModel):
904 904 __tablename__ = 'users_group_repo_group_to_perm'
905 905 __table_args__ = (
906 906 UniqueConstraint('users_group_id', 'group_id'),
907 907 {'extend_existing': True, 'mysql_engine': 'InnoDB',
908 908 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
909 909 )
910 910
911 911 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)
912 912 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
913 913 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
914 914 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
915 915
916 916 users_group = relationship('UserGroup')
917 917 permission = relationship('Permission')
918 918 group = relationship('RepoGroup')
919 919
920 920
921 921 class Statistics(Base, BaseModel):
922 922 __tablename__ = 'statistics'
923 923 __table_args__ = (
924 924 UniqueConstraint('repository_id'),
925 925 {'extend_existing': True, 'mysql_engine': 'InnoDB',
926 926 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
927 927 )
928 928 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
929 929 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
930 930 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
931 931 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
932 932 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
933 933 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
934 934
935 935 repository = relationship('Repository', single_parent=True)
936 936
937 937
938 938 class UserFollowing(Base, BaseModel):
939 939 __tablename__ = 'user_followings'
940 940 __table_args__ = (
941 941 UniqueConstraint('user_id', 'follows_repository_id'),
942 942 UniqueConstraint('user_id', 'follows_user_id'),
943 943 {'extend_existing': True, 'mysql_engine': 'InnoDB',
944 944 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
945 945 )
946 946
947 947 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
948 948 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
949 949 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
950 950 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
951 951 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
952 952
953 953 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
954 954
955 955 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
956 956 follows_repository = relationship('Repository', order_by='Repository.repo_name')
957 957
958 958
959 959 class CacheInvalidation(Base, BaseModel):
960 960 __tablename__ = 'cache_invalidation'
961 961 __table_args__ = (
962 962 UniqueConstraint('cache_key'),
963 963 Index('key_idx', 'cache_key'),
964 964 {'extend_existing': True, 'mysql_engine': 'InnoDB',
965 965 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
966 966 )
967 967 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
968 968 cache_key = Column("cache_key", String(255), nullable=True, unique=None, default=None)
969 969 cache_args = Column("cache_args", String(255), nullable=True, unique=None, default=None)
970 970 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
971 971
972 972 def __init__(self, cache_key, cache_args=''):
973 973 self.cache_key = cache_key
974 974 self.cache_args = cache_args
975 975 self.cache_active = False
976 976
977 977
978 978 class ChangesetComment(Base, BaseModel):
979 979 __tablename__ = 'changeset_comments'
980 980 __table_args__ = (
981 981 Index('cc_revision_idx', 'revision'),
982 982 {'extend_existing': True, 'mysql_engine': 'InnoDB',
983 983 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
984 984 )
985 985 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
986 986 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
987 987 revision = Column('revision', String(40), nullable=True)
988 988 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
989 989 line_no = Column('line_no', Unicode(10), nullable=True)
990 990 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
991 991 f_path = Column('f_path', Unicode(1000), nullable=True)
992 992 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
993 993 text = Column('text', UnicodeText().with_variant(UnicodeText(25000), 'mysql'), nullable=False)
994 994 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
995 995 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
996 996
997 997 author = relationship('User', lazy='joined')
998 998 repo = relationship('Repository')
999 999 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1000 1000 pull_request = relationship('PullRequest', lazy='joined')
1001 1001
1002 1002
1003 1003 class ChangesetStatus(Base, BaseModel):
1004 1004 __tablename__ = 'changeset_statuses'
1005 1005 __table_args__ = (
1006 1006 Index('cs_revision_idx', 'revision'),
1007 1007 Index('cs_version_idx', 'version'),
1008 1008 UniqueConstraint('repo_id', 'revision', 'version'),
1009 1009 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1010 1010 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1011 1011 )
1012 1012 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1013 1013 STATUS_APPROVED = 'approved'
1014 1014 STATUS_REJECTED = 'rejected'
1015 1015 STATUS_UNDER_REVIEW = 'under_review'
1016 1016
1017 1017 STATUSES = [
1018 1018 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1019 1019 (STATUS_APPROVED, _("Approved")),
1020 1020 (STATUS_REJECTED, _("Rejected")),
1021 1021 (STATUS_UNDER_REVIEW, _("Under Review")),
1022 1022 ]
1023 1023
1024 1024 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1025 1025 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1026 1026 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1027 1027 revision = Column('revision', String(40), nullable=False)
1028 1028 status = Column('status', String(128), nullable=False, default=DEFAULT)
1029 1029 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1030 1030 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1031 1031 version = Column('version', Integer(), nullable=False, default=0)
1032 1032 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1033 1033
1034 1034 author = relationship('User', lazy='joined')
1035 1035 repo = relationship('Repository')
1036 1036 comment = relationship('ChangesetComment', lazy='joined')
1037 1037 pull_request = relationship('PullRequest', lazy='joined')
1038 1038
1039 1039
1040 1040
1041 1041 class PullRequest(Base, BaseModel):
1042 1042 __tablename__ = 'pull_requests'
1043 1043 __table_args__ = (
1044 1044 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1045 1045 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1046 1046 )
1047 1047
1048 1048 STATUS_NEW = u'new'
1049 1049 STATUS_OPEN = u'open'
1050 1050 STATUS_CLOSED = u'closed'
1051 1051
1052 1052 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1053 1053 title = Column('title', Unicode(256), nullable=True)
1054 1054 description = Column('description', UnicodeText().with_variant(UnicodeText(10240), 'mysql'), nullable=True)
1055 1055 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1056 1056 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1057 1057 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1058 1058 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1059 1059 _revisions = Column('revisions', UnicodeText().with_variant(UnicodeText(20500), 'mysql'))
1060 1060 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1061 1061 org_ref = Column('org_ref', Unicode(256), nullable=False)
1062 1062 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1063 1063 other_ref = Column('other_ref', Unicode(256), nullable=False)
1064 1064
1065 1065 author = relationship('User', lazy='joined')
1066 1066 reviewers = relationship('PullRequestReviewers',
1067 1067 cascade="all, delete, delete-orphan")
1068 1068 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1069 1069 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1070 1070 statuses = relationship('ChangesetStatus')
1071 1071 comments = relationship('ChangesetComment',
1072 1072 cascade="all, delete, delete-orphan")
1073 1073
1074 1074
1075 1075 class PullRequestReviewers(Base, BaseModel):
1076 1076 __tablename__ = 'pull_request_reviewers'
1077 1077 __table_args__ = (
1078 1078 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1079 1079 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1080 1080 )
1081 1081
1082 1082 def __init__(self, user=None, pull_request=None):
1083 1083 self.user = user
1084 1084 self.pull_request = pull_request
1085 1085
1086 1086 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1087 1087 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1088 1088 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1089 1089
1090 1090 user = relationship('User')
1091 1091 pull_request = relationship('PullRequest')
1092 1092
1093 1093
1094 1094 class Notification(Base, BaseModel):
1095 1095 __tablename__ = 'notifications'
1096 1096 __table_args__ = (
1097 1097 Index('notification_type_idx', 'type'),
1098 1098 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1099 1099 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1100 1100 )
1101 1101
1102 1102 TYPE_CHANGESET_COMMENT = u'cs_comment'
1103 1103 TYPE_MESSAGE = u'message'
1104 1104 TYPE_MENTION = u'mention'
1105 1105 TYPE_REGISTRATION = u'registration'
1106 1106 TYPE_PULL_REQUEST = u'pull_request'
1107 1107 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1108 1108
1109 1109 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1110 1110 subject = Column('subject', Unicode(512), nullable=True)
1111 1111 body = Column('body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True)
1112 1112 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1113 1113 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1114 1114 type_ = Column('type', Unicode(256))
1115 1115
1116 1116 created_by_user = relationship('User')
1117 1117 notifications_to_users = relationship('UserNotification', lazy='joined',
1118 1118 cascade="all, delete, delete-orphan")
1119 1119
1120 1120
1121 1121 class UserNotification(Base, BaseModel):
1122 1122 __tablename__ = 'user_to_notification'
1123 1123 __table_args__ = (
1124 1124 UniqueConstraint('user_id', 'notification_id'),
1125 1125 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1126 1126 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1127 1127 )
1128 1128 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1129 1129 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1130 1130 read = Column('read', Boolean, default=False)
1131 1131 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1132 1132
1133 1133 user = relationship('User', lazy="joined")
1134 1134 notification = relationship('Notification', lazy="joined",
1135 1135 order_by=lambda: Notification.created_on.desc(),)
1136 1136
1137 1137
1138 1138 class Gist(Base, BaseModel):
1139 1139 __tablename__ = 'gists'
1140 1140 __table_args__ = (
1141 1141 Index('g_gist_access_id_idx', 'gist_access_id'),
1142 1142 Index('g_created_on_idx', 'created_on'),
1143 1143 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1144 1144 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1145 1145 )
1146 1146 GIST_PUBLIC = u'public'
1147 1147 GIST_PRIVATE = u'private'
1148 1148
1149 1149 gist_id = Column('gist_id', Integer(), primary_key=True)
1150 1150 gist_access_id = Column('gist_access_id', Unicode(250))
1151 1151 gist_description = Column('gist_description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
1152 1152 gist_owner = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=True)
1153 1153 gist_expires = Column('gist_expires', Float(53), nullable=False)
1154 1154 gist_type = Column('gist_type', Unicode(128), nullable=False)
1155 1155 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1156 1156 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1157 1157
1158 1158 owner = relationship('User')
1159 1159
1160 1160
1161 1161 class DbMigrateVersion(Base, BaseModel):
1162 1162 __tablename__ = 'db_migrate_version'
1163 1163 __table_args__ = (
1164 1164 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1165 1165 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1166 1166 )
1167 1167 repository_id = Column('repository_id', String(250), primary_key=True)
1168 1168 repository_path = Column('repository_path', Text)
1169 1169 version = Column('version', Integer)
@@ -1,1170 +1,1170 b''
1 1
2 2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 3 #
4 4 # This program is free software: you can redistribute it and/or modify
5 5 # it under the terms of the GNU Affero General Public License, version 3
6 6 # (only), as published by the Free Software Foundation.
7 7 #
8 8 # This program is distributed in the hope that it will be useful,
9 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 11 # GNU General Public License for more details.
12 12 #
13 13 # You should have received a copy of the GNU Affero General Public License
14 14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 15 #
16 16 # This program is dual-licensed. If you wish to learn more about the
17 17 # RhodeCode Enterprise Edition, including its added features, Support services,
18 18 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 19
20 20 import os
21 21 import time
22 22 import logging
23 23 import datetime
24 24 import traceback
25 25 import hashlib
26 26 import collections
27 27 import functools
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 remove_prefix, time_to_datetime, aslist, Optional, safe_int
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.items():
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 try:
145 145 return safe_str(self.__unicode__())
146 146 except UnicodeDecodeError:
147 147 pass
148 148 return '<DB:%s>' % (self.__class__.__name__)
149 149
150 150
151 151 class RhodeCodeSetting(Base, BaseModel):
152 152 SETTINGS_TYPES = {
153 153 'str': safe_str,
154 154 'int': safe_int,
155 155 'unicode': safe_str,
156 156 'bool': str2bool,
157 157 'list': functools.partial(aslist, sep=',')
158 158 }
159 159 __tablename__ = 'rhodecode_settings'
160 160 __table_args__ = (
161 161 UniqueConstraint('app_settings_name'),
162 162 {'extend_existing': True, 'mysql_engine': 'InnoDB',
163 163 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
164 164 )
165 165 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
166 166 app_settings_name = Column("app_settings_name", String(255), nullable=True, unique=None, default=None)
167 167 _app_settings_value = Column("app_settings_value", String(4096), nullable=True, unique=None, default=None)
168 168 _app_settings_type = Column("app_settings_type", String(255), nullable=True, unique=None, default=None)
169 169
170 170 def __init__(self, key='', val='', type='unicode'):
171 171 self.app_settings_name = key
172 172 self.app_settings_value = val
173 173 self.app_settings_type = type
174 174
175 175 @validates('_app_settings_value')
176 176 def validate_settings_value(self, key, val):
177 177 assert type(val) == str
178 178 return val
179 179
180 180 @hybrid_property
181 181 def app_settings_value(self):
182 182 v = self._app_settings_value
183 183 _type = self.app_settings_type
184 184 converter = self.SETTINGS_TYPES.get(_type) or self.SETTINGS_TYPES['unicode']
185 185 return converter(v)
186 186
187 187 @app_settings_value.setter
188 188 def app_settings_value(self, val):
189 189 """
190 190 Setter that will always make sure we use unicode in app_settings_value
191 191
192 192 :param val:
193 193 """
194 194 self._app_settings_value = safe_str(val)
195 195
196 196 @hybrid_property
197 197 def app_settings_type(self):
198 198 return self._app_settings_type
199 199
200 200 @app_settings_type.setter
201 201 def app_settings_type(self, val):
202 202 if val not in self.SETTINGS_TYPES:
203 203 raise Exception('type must be one of %s got %s'
204 204 % (self.SETTINGS_TYPES.keys(), val))
205 205 self._app_settings_type = val
206 206
207 207 def __unicode__(self):
208 208 return u"<%s('%s:%s[%s]')>" % (
209 209 self.__class__.__name__,
210 210 self.app_settings_name, self.app_settings_value, self.app_settings_type
211 211 )
212 212
213 213
214 214 class RhodeCodeUi(Base, BaseModel):
215 215 __tablename__ = 'rhodecode_ui'
216 216 __table_args__ = (
217 217 UniqueConstraint('ui_key'),
218 218 {'extend_existing': True, 'mysql_engine': 'InnoDB',
219 219 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
220 220 )
221 221
222 222 HOOK_REPO_SIZE = 'changegroup.repo_size'
223 223 HOOK_PUSH = 'changegroup.push_logger'
224 224 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
225 225 HOOK_PULL = 'outgoing.pull_logger'
226 226 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
227 227
228 228 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
229 229 ui_section = Column("ui_section", String(255), nullable=True, unique=None, default=None)
230 230 ui_key = Column("ui_key", String(255), nullable=True, unique=None, default=None)
231 231 ui_value = Column("ui_value", String(255), nullable=True, unique=None, default=None)
232 232 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
233 233
234 234
235 235
236 236 class User(Base, BaseModel):
237 237 __tablename__ = 'users'
238 238 __table_args__ = (
239 239 UniqueConstraint('username'), UniqueConstraint('email'),
240 240 Index('u_username_idx', 'username'),
241 241 Index('u_email_idx', 'email'),
242 242 {'extend_existing': True, 'mysql_engine': 'InnoDB',
243 243 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
244 244 )
245 245 DEFAULT_USER = 'default'
246 246
247 247 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
248 248 username = Column("username", String(255), nullable=True, unique=None, default=None)
249 249 password = Column("password", String(255), nullable=True, unique=None, default=None)
250 250 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
251 251 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
252 252 name = Column("firstname", String(255), nullable=True, unique=None, default=None)
253 253 lastname = Column("lastname", String(255), nullable=True, unique=None, default=None)
254 254 _email = Column("email", String(255), nullable=True, unique=None, default=None)
255 255 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
256 256 extern_type = Column("extern_type", String(255), nullable=True, unique=None, default=None)
257 257 extern_name = Column("extern_name", String(255), nullable=True, unique=None, default=None)
258 258 api_key = Column("api_key", String(255), nullable=True, unique=None, default=None)
259 259 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
260 260 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
261 261
262 262 user_log = relationship('UserLog')
263 263 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
264 264
265 265 repositories = relationship('Repository')
266 266 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
267 267 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
268 268
269 269 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
270 270 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
271 271
272 272 group_member = relationship('UserGroupMember', cascade='all')
273 273
274 274 notifications = relationship('UserNotification', cascade='all')
275 275 # notifications assigned to this user
276 276 user_created_notifications = relationship('Notification', cascade='all')
277 277 # comments created by this user
278 278 user_comments = relationship('ChangesetComment', cascade='all')
279 279 user_emails = relationship('UserEmailMap', cascade='all')
280 280
281 281 @hybrid_property
282 282 def email(self):
283 283 return self._email
284 284
285 285 @email.setter
286 286 def email(self, val):
287 287 self._email = val.lower() if val else None
288 288
289 289 @property
290 290 def firstname(self):
291 291 # alias for future
292 292 return self.name
293 293
294 294 @property
295 295 def username_and_name(self):
296 296 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
297 297
298 298 @property
299 299 def full_name(self):
300 300 return '%s %s' % (self.firstname, self.lastname)
301 301
302 302 @property
303 303 def full_contact(self):
304 304 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
305 305
306 306 @property
307 307 def short_contact(self):
308 308 return '%s %s' % (self.firstname, self.lastname)
309 309
310 310 @property
311 311 def is_admin(self):
312 312 return self.admin
313 313
314 314 @classmethod
315 315 def get_by_username(cls, username, case_insensitive=False, cache=False):
316 316 if case_insensitive:
317 317 q = cls.query().filter(cls.username.ilike(username))
318 318 else:
319 319 q = cls.query().filter(cls.username == username)
320 320
321 321 if cache:
322 322 q = q.options(FromCache(
323 323 "sql_cache_short",
324 324 "get_user_%s" % _hash_key(username)
325 325 )
326 326 )
327 327 return q.scalar()
328 328
329 329 @classmethod
330 330 def get_by_auth_token(cls, auth_token, cache=False):
331 331 q = cls.query().filter(cls.api_key == auth_token)
332 332
333 333 if cache:
334 334 q = q.options(FromCache("sql_cache_short",
335 335 "get_auth_token_%s" % auth_token))
336 336 return q.scalar()
337 337
338 338 @classmethod
339 339 def get_by_email(cls, email, case_insensitive=False, cache=False):
340 340 if case_insensitive:
341 341 q = cls.query().filter(cls.email.ilike(email))
342 342 else:
343 343 q = cls.query().filter(cls.email == email)
344 344
345 345 if cache:
346 346 q = q.options(FromCache("sql_cache_short",
347 347 "get_email_key_%s" % email))
348 348
349 349 ret = q.scalar()
350 350 if ret is None:
351 351 q = UserEmailMap.query()
352 352 # try fetching in alternate email map
353 353 if case_insensitive:
354 354 q = q.filter(UserEmailMap.email.ilike(email))
355 355 else:
356 356 q = q.filter(UserEmailMap.email == email)
357 357 q = q.options(joinedload(UserEmailMap.user))
358 358 if cache:
359 359 q = q.options(FromCache("sql_cache_short",
360 360 "get_email_map_key_%s" % email))
361 361 ret = getattr(q.scalar(), 'user', None)
362 362
363 363 return ret
364 364
365 365 @classmethod
366 366 def get_first_admin(cls):
367 367 user = User.query().filter(User.admin == True).first()
368 368 if user is None:
369 369 raise Exception('Missing administrative account!')
370 370 return user
371 371
372 372 @classmethod
373 373 def get_default_user(cls, cache=False):
374 374 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
375 375 if user is None:
376 376 raise Exception('Missing default account!')
377 377 return user
378 378
379 379
380 380
381 381
382 382 class UserEmailMap(Base, BaseModel):
383 383 __tablename__ = 'user_email_map'
384 384 __table_args__ = (
385 385 Index('uem_email_idx', 'email'),
386 386 UniqueConstraint('email'),
387 387 {'extend_existing': True, 'mysql_engine': 'InnoDB',
388 388 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
389 389 )
390 390
391 391
392 392 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
393 393 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
394 394 _email = Column("email", String(255), nullable=True, unique=False, default=None)
395 395 user = relationship('User', lazy='joined')
396 396
397 397 @validates('_email')
398 398 def validate_email(self, key, email):
399 399 # check if this email is not main one
400 400 main_email = Session().query(User).filter(User.email == email).scalar()
401 401 if main_email is not None:
402 402 raise AttributeError('email %s is present is user table' % email)
403 403 return email
404 404
405 405 @hybrid_property
406 406 def email(self):
407 407 return self._email
408 408
409 409 @email.setter
410 410 def email(self, val):
411 411 self._email = val.lower() if val else None
412 412
413 413
414 414 class UserIpMap(Base, BaseModel):
415 415 __tablename__ = 'user_ip_map'
416 416 __table_args__ = (
417 417 UniqueConstraint('user_id', 'ip_addr'),
418 418 {'extend_existing': True, 'mysql_engine': 'InnoDB',
419 419 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
420 420 )
421 421
422 422
423 423 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
424 424 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
425 425 ip_addr = Column("ip_addr", String(255), nullable=True, unique=False, default=None)
426 426 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
427 427 user = relationship('User', lazy='joined')
428 428
429 429
430 430 class UserLog(Base, BaseModel):
431 431 __tablename__ = 'user_logs'
432 432 __table_args__ = (
433 433 {'extend_existing': True, 'mysql_engine': 'InnoDB',
434 434 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
435 435 )
436 436 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
437 437 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
438 438 username = Column("username", String(255), nullable=True, unique=None, default=None)
439 439 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
440 440 repository_name = Column("repository_name", String(255), nullable=True, unique=None, default=None)
441 441 user_ip = Column("user_ip", String(255), nullable=True, unique=None, default=None)
442 442 action = Column("action", String(1200000), nullable=True, unique=None, default=None)
443 443 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
444 444
445 445 def __unicode__(self):
446 446 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
447 447 self.repository_name,
448 448 self.action)
449 449
450 450 user = relationship('User')
451 451 repository = relationship('Repository', cascade='')
452 452
453 453
454 454 class UserGroup(Base, BaseModel):
455 455 __tablename__ = 'users_groups'
456 456 __table_args__ = (
457 457 {'extend_existing': True, 'mysql_engine': 'InnoDB',
458 458 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
459 459 )
460 460
461 461 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
462 462 users_group_name = Column("users_group_name", String(255), nullable=False, unique=True, default=None)
463 463 user_group_description = Column("user_group_description", String(10000), nullable=True, unique=None, default=None)
464 464 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
465 465 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
466 466 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
467 467 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
468 468
469 469 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
470 470 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
471 471 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
472 472 users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
473 473 user_user_group_to_perm = relationship('UserUserGroupToPerm', cascade='all')
474 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm ', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
474 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
475 475
476 476 user = relationship('User')
477 477
478 478 def __unicode__(self):
479 479 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
480 480 self.users_group_id,
481 481 self.users_group_name)
482 482
483 483 @classmethod
484 484 def get_by_group_name(cls, group_name, cache=False,
485 485 case_insensitive=False):
486 486 if case_insensitive:
487 487 q = cls.query().filter(cls.users_group_name.ilike(group_name))
488 488 else:
489 489 q = cls.query().filter(cls.users_group_name == group_name)
490 490 if cache:
491 491 q = q.options(FromCache(
492 492 "sql_cache_short",
493 493 "get_user_%s" % _hash_key(group_name)
494 494 )
495 495 )
496 496 return q.scalar()
497 497
498 498 @classmethod
499 499 def get(cls, user_group_id, cache=False):
500 500 user_group = cls.query()
501 501 if cache:
502 502 user_group = user_group.options(FromCache("sql_cache_short",
503 503 "get_users_group_%s" % user_group_id))
504 504 return user_group.get(user_group_id)
505 505
506 506
507 507 class UserGroupMember(Base, BaseModel):
508 508 __tablename__ = 'users_groups_members'
509 509 __table_args__ = (
510 510 {'extend_existing': True, 'mysql_engine': 'InnoDB',
511 511 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
512 512 )
513 513
514 514 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
515 515 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
516 516 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
517 517
518 518 user = relationship('User', lazy='joined')
519 519 users_group = relationship('UserGroup')
520 520
521 521 def __init__(self, gr_id='', u_id=''):
522 522 self.users_group_id = gr_id
523 523 self.user_id = u_id
524 524
525 525
526 526 class RepositoryField(Base, BaseModel):
527 527 __tablename__ = 'repositories_fields'
528 528 __table_args__ = (
529 529 UniqueConstraint('repository_id', 'field_key'), # no-multi field
530 530 {'extend_existing': True, 'mysql_engine': 'InnoDB',
531 531 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
532 532 )
533 533 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
534 534
535 535 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
536 536 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
537 537 field_key = Column("field_key", String(250))
538 538 field_label = Column("field_label", String(1024), nullable=False)
539 539 field_value = Column("field_value", String(10000), nullable=False)
540 540 field_desc = Column("field_desc", String(1024), nullable=False)
541 541 field_type = Column("field_type", String(256), nullable=False, unique=None)
542 542 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
543 543
544 544 repository = relationship('Repository')
545 545
546 546 @classmethod
547 547 def get_by_key_name(cls, key, repo):
548 548 row = cls.query()\
549 549 .filter(cls.repository == repo)\
550 550 .filter(cls.field_key == key).scalar()
551 551 return row
552 552
553 553
554 554 class Repository(Base, BaseModel):
555 555 __tablename__ = 'repositories'
556 556 __table_args__ = (
557 557 UniqueConstraint('repo_name'),
558 558 Index('r_repo_name_idx', 'repo_name'),
559 559 {'extend_existing': True, 'mysql_engine': 'InnoDB',
560 560 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
561 561 )
562 562
563 563 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
564 564 repo_name = Column("repo_name", String(255), nullable=False, unique=True, default=None)
565 565 clone_uri = Column("clone_uri", String(255), nullable=True, unique=False, default=None)
566 566 repo_type = Column("repo_type", String(255), nullable=False, unique=False, default=None)
567 567 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
568 568 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
569 569 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
570 570 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
571 571 description = Column("description", String(10000), nullable=True, unique=None, default=None)
572 572 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
573 573 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
574 574 landing_rev = Column("landing_revision", String(255), nullable=False, unique=False, default=None)
575 575 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
576 576 _locked = Column("locked", String(255), nullable=True, unique=False, default=None)
577 577 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
578 578
579 579 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
580 580 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
581 581
582 582 user = relationship('User')
583 583 fork = relationship('Repository', remote_side=repo_id)
584 584 group = relationship('RepoGroup')
585 585 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
586 586 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
587 587 stats = relationship('Statistics', cascade='all', uselist=False)
588 588
589 589 followers = relationship('UserFollowing',
590 590 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
591 591 cascade='all')
592 592 extra_fields = relationship('RepositoryField',
593 593 cascade="all, delete, delete-orphan")
594 594
595 595 logs = relationship('UserLog')
596 596 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
597 597
598 598 pull_requests_org = relationship('PullRequest',
599 599 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
600 600 cascade="all, delete, delete-orphan")
601 601
602 602 pull_requests_other = relationship('PullRequest',
603 603 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
604 604 cascade="all, delete, delete-orphan")
605 605
606 606 def __unicode__(self):
607 607 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
608 608 safe_str(self.repo_name))
609 609
610 610 @classmethod
611 611 def get_by_repo_name(cls, repo_name):
612 612 q = Session().query(cls).filter(cls.repo_name == repo_name)
613 613 q = q.options(joinedload(Repository.fork))\
614 614 .options(joinedload(Repository.user))\
615 615 .options(joinedload(Repository.group))
616 616 return q.scalar()
617 617
618 618
619 619 class RepoGroup(Base, BaseModel):
620 620 __tablename__ = 'groups'
621 621 __table_args__ = (
622 622 UniqueConstraint('group_name', 'group_parent_id'),
623 623 {'extend_existing': True, 'mysql_engine': 'InnoDB',
624 624 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
625 625 )
626 626
627 627
628 628 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
629 629 group_name = Column("group_name", String(255), nullable=False, unique=True, default=None)
630 630 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
631 631 group_description = Column("group_description", String(10000), nullable=True, unique=None, default=None)
632 632 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
633 633 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
634 634 #TODO: create this field in migrations
635 635 #created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
636 636
637 637 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
638 638 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
639 639 parent_group = relationship('RepoGroup', remote_side=group_id)
640 640 user = relationship('User')
641 641
642 642 def __init__(self, group_name='', parent_group=None):
643 643 self.group_name = group_name
644 644 self.parent_group = parent_group
645 645
646 646 def __unicode__(self):
647 647 return u"<%s('id:%s:%s')>" % (self.__class__.__name__, self.group_id,
648 648 self.group_name)
649 649
650 650 @classmethod
651 651 def url_sep(cls):
652 652 return URL_SEP
653 653
654 654 @classmethod
655 655 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
656 656 if case_insensitive:
657 657 gr = cls.query()\
658 658 .filter(cls.group_name.ilike(group_name))
659 659 else:
660 660 gr = cls.query()\
661 661 .filter(cls.group_name == group_name)
662 662 if cache:
663 663 gr = gr.options(FromCache(
664 664 "sql_cache_short",
665 665 "get_group_%s" % _hash_key(group_name)
666 666 )
667 667 )
668 668 return gr.scalar()
669 669
670 670
671 671 class Permission(Base, BaseModel):
672 672 __tablename__ = 'permissions'
673 673 __table_args__ = (
674 674 Index('p_perm_name_idx', 'permission_name'),
675 675 {'extend_existing': True, 'mysql_engine': 'InnoDB',
676 676 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
677 677 )
678 678 PERMS = [
679 679 ('hg.admin', _('RhodeCode Administrator')),
680 680
681 681 ('repository.none', _('Repository no access')),
682 682 ('repository.read', _('Repository read access')),
683 683 ('repository.write', _('Repository write access')),
684 684 ('repository.admin', _('Repository admin access')),
685 685
686 686 ('group.none', _('Repository group no access')),
687 687 ('group.read', _('Repository group read access')),
688 688 ('group.write', _('Repository group write access')),
689 689 ('group.admin', _('Repository group admin access')),
690 690
691 691 ('usergroup.none', _('User group no access')),
692 692 ('usergroup.read', _('User group read access')),
693 693 ('usergroup.write', _('User group write access')),
694 694 ('usergroup.admin', _('User group admin access')),
695 695
696 696 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
697 697 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
698 698
699 699 ('hg.usergroup.create.false', _('User Group creation disabled')),
700 700 ('hg.usergroup.create.true', _('User Group creation enabled')),
701 701
702 702 ('hg.create.none', _('Repository creation disabled')),
703 703 ('hg.create.repository', _('Repository creation enabled')),
704 704
705 705 ('hg.fork.none', _('Repository forking disabled')),
706 706 ('hg.fork.repository', _('Repository forking enabled')),
707 707
708 708 ('hg.register.none', _('Registration disabled')),
709 709 ('hg.register.manual_activate', _('User Registration with manual account activation')),
710 710 ('hg.register.auto_activate', _('User Registration with automatic account activation')),
711 711
712 712 ('hg.extern_activate.manual', _('Manual activation of external account')),
713 713 ('hg.extern_activate.auto', _('Automatic activation of external account')),
714 714
715 715 ]
716 716
717 717 #definition of system default permissions for DEFAULT user
718 718 DEFAULT_USER_PERMISSIONS = [
719 719 'repository.read',
720 720 'group.read',
721 721 'usergroup.read',
722 722 'hg.create.repository',
723 723 'hg.fork.repository',
724 724 'hg.register.manual_activate',
725 725 'hg.extern_activate.auto',
726 726 ]
727 727
728 728 # defines which permissions are more important higher the more important
729 729 # Weight defines which permissions are more important.
730 730 # The higher number the more important.
731 731 PERM_WEIGHTS = {
732 732 'repository.none': 0,
733 733 'repository.read': 1,
734 734 'repository.write': 3,
735 735 'repository.admin': 4,
736 736
737 737 'group.none': 0,
738 738 'group.read': 1,
739 739 'group.write': 3,
740 740 'group.admin': 4,
741 741
742 742 'usergroup.none': 0,
743 743 'usergroup.read': 1,
744 744 'usergroup.write': 3,
745 745 'usergroup.admin': 4,
746 746 'hg.repogroup.create.false': 0,
747 747 'hg.repogroup.create.true': 1,
748 748
749 749 'hg.usergroup.create.false': 0,
750 750 'hg.usergroup.create.true': 1,
751 751
752 752 'hg.fork.none': 0,
753 753 'hg.fork.repository': 1,
754 754 'hg.create.none': 0,
755 755 'hg.create.repository': 1
756 756 }
757 757
758 758 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
759 759 permission_name = Column("permission_name", String(255), nullable=True, unique=None, default=None)
760 760 permission_longname = Column("permission_longname", String(255), nullable=True, unique=None, default=None)
761 761
762 762 def __unicode__(self):
763 763 return u"<%s('%s:%s')>" % (
764 764 self.__class__.__name__, self.permission_id, self.permission_name
765 765 )
766 766
767 767 @classmethod
768 768 def get_by_key(cls, key):
769 769 return cls.query().filter(cls.permission_name == key).scalar()
770 770
771 771
772 772 class UserRepoToPerm(Base, BaseModel):
773 773 __tablename__ = 'repo_to_perm'
774 774 __table_args__ = (
775 775 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
776 776 {'extend_existing': True, 'mysql_engine': 'InnoDB',
777 777 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
778 778 )
779 779 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
780 780 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
781 781 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
782 782 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
783 783
784 784 user = relationship('User')
785 785 repository = relationship('Repository')
786 786 permission = relationship('Permission')
787 787
788 788 def __unicode__(self):
789 789 return u'<%s => %s >' % (self.user, self.repository)
790 790
791 791
792 792 class UserUserGroupToPerm(Base, BaseModel):
793 793 __tablename__ = 'user_user_group_to_perm'
794 794 __table_args__ = (
795 795 UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
796 796 {'extend_existing': True, 'mysql_engine': 'InnoDB',
797 797 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
798 798 )
799 799 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
800 800 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
801 801 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
802 802 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
803 803
804 804 user = relationship('User')
805 805 user_group = relationship('UserGroup')
806 806 permission = relationship('Permission')
807 807
808 808 def __unicode__(self):
809 809 return u'<%s => %s >' % (self.user, self.user_group)
810 810
811 811
812 812 class UserToPerm(Base, BaseModel):
813 813 __tablename__ = 'user_to_perm'
814 814 __table_args__ = (
815 815 UniqueConstraint('user_id', 'permission_id'),
816 816 {'extend_existing': True, 'mysql_engine': 'InnoDB',
817 817 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
818 818 )
819 819 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
820 820 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
821 821 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
822 822
823 823 user = relationship('User')
824 824 permission = relationship('Permission', lazy='joined')
825 825
826 826 def __unicode__(self):
827 827 return u'<%s => %s >' % (self.user, self.permission)
828 828
829 829
830 830 class UserGroupRepoToPerm(Base, BaseModel):
831 831 __tablename__ = 'users_group_repo_to_perm'
832 832 __table_args__ = (
833 833 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
834 834 {'extend_existing': True, 'mysql_engine': 'InnoDB',
835 835 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
836 836 )
837 837 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
838 838 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
839 839 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
840 840 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
841 841
842 842 users_group = relationship('UserGroup')
843 843 permission = relationship('Permission')
844 844 repository = relationship('Repository')
845 845
846 846 def __unicode__(self):
847 847 return u'<UserGroupRepoToPerm:%s => %s >' % (self.users_group, self.repository)
848 848
849 849
850 850 class UserGroupUserGroupToPerm(Base, BaseModel):
851 851 __tablename__ = 'user_group_user_group_to_perm'
852 852 __table_args__ = (
853 853 UniqueConstraint('target_user_group_id', 'user_group_id', 'permission_id'),
854 854 CheckConstraint('target_user_group_id != user_group_id'),
855 855 {'extend_existing': True, 'mysql_engine': 'InnoDB',
856 856 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
857 857 )
858 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)
859 859 target_user_group_id = Column("target_user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
860 860 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
861 861 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
862 862
863 863 target_user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
864 864 user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
865 865 permission = relationship('Permission')
866 866
867 867 def __unicode__(self):
868 868 return u'<UserGroupUserGroup:%s => %s >' % (self.target_user_group, self.user_group)
869 869
870 870
871 871 class UserGroupToPerm(Base, BaseModel):
872 872 __tablename__ = 'users_group_to_perm'
873 873 __table_args__ = (
874 874 UniqueConstraint('users_group_id', 'permission_id',),
875 875 {'extend_existing': True, 'mysql_engine': 'InnoDB',
876 876 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
877 877 )
878 878 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
879 879 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
880 880 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
881 881
882 882 users_group = relationship('UserGroup')
883 883 permission = relationship('Permission')
884 884
885 885
886 886 class UserRepoGroupToPerm(Base, BaseModel):
887 887 __tablename__ = 'user_repo_group_to_perm'
888 888 __table_args__ = (
889 889 UniqueConstraint('user_id', 'group_id', 'permission_id'),
890 890 {'extend_existing': True, 'mysql_engine': 'InnoDB',
891 891 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
892 892 )
893 893
894 894 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
895 895 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
896 896 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
897 897 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
898 898
899 899 user = relationship('User')
900 900 group = relationship('RepoGroup')
901 901 permission = relationship('Permission')
902 902
903 903
904 904 class UserGroupRepoGroupToPerm(Base, BaseModel):
905 905 __tablename__ = 'users_group_repo_group_to_perm'
906 906 __table_args__ = (
907 907 UniqueConstraint('users_group_id', 'group_id'),
908 908 {'extend_existing': True, 'mysql_engine': 'InnoDB',
909 909 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
910 910 )
911 911
912 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)
913 913 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
914 914 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
915 915 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
916 916
917 917 users_group = relationship('UserGroup')
918 918 permission = relationship('Permission')
919 919 group = relationship('RepoGroup')
920 920
921 921
922 922 class Statistics(Base, BaseModel):
923 923 __tablename__ = 'statistics'
924 924 __table_args__ = (
925 925 UniqueConstraint('repository_id'),
926 926 {'extend_existing': True, 'mysql_engine': 'InnoDB',
927 927 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
928 928 )
929 929 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
930 930 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
931 931 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
932 932 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
933 933 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
934 934 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
935 935
936 936 repository = relationship('Repository', single_parent=True)
937 937
938 938
939 939 class UserFollowing(Base, BaseModel):
940 940 __tablename__ = 'user_followings'
941 941 __table_args__ = (
942 942 UniqueConstraint('user_id', 'follows_repository_id'),
943 943 UniqueConstraint('user_id', 'follows_user_id'),
944 944 {'extend_existing': True, 'mysql_engine': 'InnoDB',
945 945 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
946 946 )
947 947
948 948 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
949 949 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
950 950 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
951 951 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
952 952 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
953 953
954 954 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
955 955
956 956 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
957 957 follows_repository = relationship('Repository', order_by='Repository.repo_name')
958 958
959 959
960 960 class CacheInvalidation(Base, BaseModel):
961 961 __tablename__ = 'cache_invalidation'
962 962 __table_args__ = (
963 963 UniqueConstraint('cache_key'),
964 964 Index('key_idx', 'cache_key'),
965 965 {'extend_existing': True, 'mysql_engine': 'InnoDB',
966 966 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
967 967 )
968 968 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
969 969 cache_key = Column("cache_key", String(255), nullable=True, unique=None, default=None)
970 970 cache_args = Column("cache_args", String(255), nullable=True, unique=None, default=None)
971 971 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
972 972
973 973 def __init__(self, cache_key, cache_args=''):
974 974 self.cache_key = cache_key
975 975 self.cache_args = cache_args
976 976 self.cache_active = False
977 977
978 978
979 979 class ChangesetComment(Base, BaseModel):
980 980 __tablename__ = 'changeset_comments'
981 981 __table_args__ = (
982 982 Index('cc_revision_idx', 'revision'),
983 983 {'extend_existing': True, 'mysql_engine': 'InnoDB',
984 984 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
985 985 )
986 986 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
987 987 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
988 988 revision = Column('revision', String(40), nullable=True)
989 989 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
990 990 line_no = Column('line_no', Unicode(10), nullable=True)
991 991 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
992 992 f_path = Column('f_path', Unicode(1000), nullable=True)
993 993 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
994 994 text = Column('text', UnicodeText().with_variant(UnicodeText(25000), 'mysql'), nullable=False)
995 995 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
996 996 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
997 997
998 998 author = relationship('User', lazy='joined')
999 999 repo = relationship('Repository')
1000 1000 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1001 1001 pull_request = relationship('PullRequest', lazy='joined')
1002 1002
1003 1003
1004 1004 class ChangesetStatus(Base, BaseModel):
1005 1005 __tablename__ = 'changeset_statuses'
1006 1006 __table_args__ = (
1007 1007 Index('cs_revision_idx', 'revision'),
1008 1008 Index('cs_version_idx', 'version'),
1009 1009 UniqueConstraint('repo_id', 'revision', 'version'),
1010 1010 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1011 1011 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1012 1012 )
1013 1013 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1014 1014 STATUS_APPROVED = 'approved'
1015 1015 STATUS_REJECTED = 'rejected'
1016 1016 STATUS_UNDER_REVIEW = 'under_review'
1017 1017
1018 1018 STATUSES = [
1019 1019 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1020 1020 (STATUS_APPROVED, _("Approved")),
1021 1021 (STATUS_REJECTED, _("Rejected")),
1022 1022 (STATUS_UNDER_REVIEW, _("Under Review")),
1023 1023 ]
1024 1024
1025 1025 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1026 1026 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1027 1027 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1028 1028 revision = Column('revision', String(40), nullable=False)
1029 1029 status = Column('status', String(128), nullable=False, default=DEFAULT)
1030 1030 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1031 1031 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1032 1032 version = Column('version', Integer(), nullable=False, default=0)
1033 1033 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1034 1034
1035 1035 author = relationship('User', lazy='joined')
1036 1036 repo = relationship('Repository')
1037 1037 comment = relationship('ChangesetComment', lazy='joined')
1038 1038 pull_request = relationship('PullRequest', lazy='joined')
1039 1039
1040 1040
1041 1041
1042 1042 class PullRequest(Base, BaseModel):
1043 1043 __tablename__ = 'pull_requests'
1044 1044 __table_args__ = (
1045 1045 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1046 1046 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1047 1047 )
1048 1048
1049 1049 STATUS_NEW = u'new'
1050 1050 STATUS_OPEN = u'open'
1051 1051 STATUS_CLOSED = u'closed'
1052 1052
1053 1053 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1054 1054 title = Column('title', Unicode(256), nullable=True)
1055 1055 description = Column('description', UnicodeText().with_variant(UnicodeText(10240), 'mysql'), nullable=True)
1056 1056 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1057 1057 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1058 1058 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1059 1059 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1060 1060 _revisions = Column('revisions', UnicodeText().with_variant(UnicodeText(20500), 'mysql'))
1061 1061 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1062 1062 org_ref = Column('org_ref', Unicode(256), nullable=False)
1063 1063 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1064 1064 other_ref = Column('other_ref', Unicode(256), nullable=False)
1065 1065
1066 1066 author = relationship('User', lazy='joined')
1067 1067 reviewers = relationship('PullRequestReviewers',
1068 1068 cascade="all, delete, delete-orphan")
1069 1069 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1070 1070 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1071 1071 statuses = relationship('ChangesetStatus')
1072 1072 comments = relationship('ChangesetComment',
1073 1073 cascade="all, delete, delete-orphan")
1074 1074
1075 1075
1076 1076 class PullRequestReviewers(Base, BaseModel):
1077 1077 __tablename__ = 'pull_request_reviewers'
1078 1078 __table_args__ = (
1079 1079 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1080 1080 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1081 1081 )
1082 1082
1083 1083 def __init__(self, user=None, pull_request=None):
1084 1084 self.user = user
1085 1085 self.pull_request = pull_request
1086 1086
1087 1087 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1088 1088 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1089 1089 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1090 1090
1091 1091 user = relationship('User')
1092 1092 pull_request = relationship('PullRequest')
1093 1093
1094 1094
1095 1095 class Notification(Base, BaseModel):
1096 1096 __tablename__ = 'notifications'
1097 1097 __table_args__ = (
1098 1098 Index('notification_type_idx', 'type'),
1099 1099 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1100 1100 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1101 1101 )
1102 1102
1103 1103 TYPE_CHANGESET_COMMENT = u'cs_comment'
1104 1104 TYPE_MESSAGE = u'message'
1105 1105 TYPE_MENTION = u'mention'
1106 1106 TYPE_REGISTRATION = u'registration'
1107 1107 TYPE_PULL_REQUEST = u'pull_request'
1108 1108 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1109 1109
1110 1110 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1111 1111 subject = Column('subject', Unicode(512), nullable=True)
1112 1112 body = Column('body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True)
1113 1113 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1114 1114 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1115 1115 type_ = Column('type', Unicode(256))
1116 1116
1117 1117 created_by_user = relationship('User')
1118 1118 notifications_to_users = relationship('UserNotification', lazy='joined',
1119 1119 cascade="all, delete, delete-orphan")
1120 1120
1121 1121
1122 1122 class UserNotification(Base, BaseModel):
1123 1123 __tablename__ = 'user_to_notification'
1124 1124 __table_args__ = (
1125 1125 UniqueConstraint('user_id', 'notification_id'),
1126 1126 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1127 1127 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1128 1128 )
1129 1129 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1130 1130 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1131 1131 read = Column('read', Boolean, default=False)
1132 1132 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1133 1133
1134 1134 user = relationship('User', lazy="joined")
1135 1135 notification = relationship('Notification', lazy="joined",
1136 1136 order_by=lambda: Notification.created_on.desc(),)
1137 1137
1138 1138
1139 1139 class Gist(Base, BaseModel):
1140 1140 __tablename__ = 'gists'
1141 1141 __table_args__ = (
1142 1142 Index('g_gist_access_id_idx', 'gist_access_id'),
1143 1143 Index('g_created_on_idx', 'created_on'),
1144 1144 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1145 1145 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1146 1146 )
1147 1147 GIST_PUBLIC = u'public'
1148 1148 GIST_PRIVATE = u'private'
1149 1149
1150 1150 gist_id = Column('gist_id', Integer(), primary_key=True)
1151 1151 gist_access_id = Column('gist_access_id', Unicode(250))
1152 1152 gist_description = Column('gist_description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
1153 1153 gist_owner = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=True)
1154 1154 gist_expires = Column('gist_expires', Float(53), nullable=False)
1155 1155 gist_type = Column('gist_type', Unicode(128), nullable=False)
1156 1156 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1157 1157 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1158 1158
1159 1159 owner = relationship('User')
1160 1160
1161 1161
1162 1162 class DbMigrateVersion(Base, BaseModel):
1163 1163 __tablename__ = 'db_migrate_version'
1164 1164 __table_args__ = (
1165 1165 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1166 1166 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1167 1167 )
1168 1168 repository_id = Column('repository_id', String(250), primary_key=True)
1169 1169 repository_path = Column('repository_path', Text)
1170 1170 version = Column('version', Integer)
@@ -1,1187 +1,1187 b''
1 1
2 2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 3 #
4 4 # This program is free software: you can redistribute it and/or modify
5 5 # it under the terms of the GNU Affero General Public License, version 3
6 6 # (only), as published by the Free Software Foundation.
7 7 #
8 8 # This program is distributed in the hope that it will be useful,
9 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 11 # GNU General Public License for more details.
12 12 #
13 13 # You should have received a copy of the GNU Affero General Public License
14 14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 15 #
16 16 # This program is dual-licensed. If you wish to learn more about the
17 17 # RhodeCode Enterprise Edition, including its added features, Support services,
18 18 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 19
20 20 import os
21 21 import time
22 22 import logging
23 23 import datetime
24 24 import traceback
25 25 import hashlib
26 26 import collections
27 27 import functools
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 remove_prefix, time_to_datetime, aslist, Optional, safe_int
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.items():
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 try:
145 145 return safe_str(self.__unicode__())
146 146 except UnicodeDecodeError:
147 147 pass
148 148 return '<DB:%s>' % (self.__class__.__name__)
149 149
150 150
151 151 class RhodeCodeSetting(Base, BaseModel):
152 152 SETTINGS_TYPES = {
153 153 'str': safe_str,
154 154 'int': safe_int,
155 155 'unicode': safe_str,
156 156 'bool': str2bool,
157 157 'list': functools.partial(aslist, sep=',')
158 158 }
159 159 __tablename__ = 'rhodecode_settings'
160 160 __table_args__ = (
161 161 UniqueConstraint('app_settings_name'),
162 162 {'extend_existing': True, 'mysql_engine': 'InnoDB',
163 163 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
164 164 )
165 165 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
166 166 app_settings_name = Column("app_settings_name", String(255), nullable=True, unique=None, default=None)
167 167 _app_settings_value = Column("app_settings_value", String(4096), nullable=True, unique=None, default=None)
168 168 _app_settings_type = Column("app_settings_type", String(255), nullable=True, unique=None, default=None)
169 169
170 170 def __init__(self, key='', val='', type='unicode'):
171 171 self.app_settings_name = key
172 172 self.app_settings_value = val
173 173 self.app_settings_type = type
174 174
175 175 @validates('_app_settings_value')
176 176 def validate_settings_value(self, key, val):
177 177 assert type(val) == str
178 178 return val
179 179
180 180 @hybrid_property
181 181 def app_settings_value(self):
182 182 v = self._app_settings_value
183 183 _type = self.app_settings_type
184 184 converter = self.SETTINGS_TYPES.get(_type) or self.SETTINGS_TYPES['unicode']
185 185 return converter(v)
186 186
187 187 @app_settings_value.setter
188 188 def app_settings_value(self, val):
189 189 """
190 190 Setter that will always make sure we use unicode in app_settings_value
191 191
192 192 :param val:
193 193 """
194 194 self._app_settings_value = safe_str(val)
195 195
196 196 @hybrid_property
197 197 def app_settings_type(self):
198 198 return self._app_settings_type
199 199
200 200 @app_settings_type.setter
201 201 def app_settings_type(self, val):
202 202 if val not in self.SETTINGS_TYPES:
203 203 raise Exception('type must be one of %s got %s'
204 204 % (self.SETTINGS_TYPES.keys(), val))
205 205 self._app_settings_type = val
206 206
207 207 def __unicode__(self):
208 208 return u"<%s('%s:%s[%s]')>" % (
209 209 self.__class__.__name__,
210 210 self.app_settings_name, self.app_settings_value, self.app_settings_type
211 211 )
212 212
213 213
214 214 class RhodeCodeUi(Base, BaseModel):
215 215 __tablename__ = 'rhodecode_ui'
216 216 __table_args__ = (
217 217 UniqueConstraint('ui_key'),
218 218 {'extend_existing': True, 'mysql_engine': 'InnoDB',
219 219 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
220 220 )
221 221
222 222 HOOK_REPO_SIZE = 'changegroup.repo_size'
223 223 HOOK_PUSH = 'changegroup.push_logger'
224 224 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
225 225 HOOK_PULL = 'outgoing.pull_logger'
226 226 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
227 227
228 228 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
229 229 ui_section = Column("ui_section", String(255), nullable=True, unique=None, default=None)
230 230 ui_key = Column("ui_key", String(255), nullable=True, unique=None, default=None)
231 231 ui_value = Column("ui_value", String(255), nullable=True, unique=None, default=None)
232 232 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
233 233
234 234
235 235
236 236 class User(Base, BaseModel):
237 237 __tablename__ = 'users'
238 238 __table_args__ = (
239 239 UniqueConstraint('username'), UniqueConstraint('email'),
240 240 Index('u_username_idx', 'username'),
241 241 Index('u_email_idx', 'email'),
242 242 {'extend_existing': True, 'mysql_engine': 'InnoDB',
243 243 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
244 244 )
245 245 DEFAULT_USER = 'default'
246 246
247 247 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
248 248 username = Column("username", String(255), nullable=True, unique=None, default=None)
249 249 password = Column("password", String(255), nullable=True, unique=None, default=None)
250 250 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
251 251 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
252 252 name = Column("firstname", String(255), nullable=True, unique=None, default=None)
253 253 lastname = Column("lastname", String(255), nullable=True, unique=None, default=None)
254 254 _email = Column("email", String(255), nullable=True, unique=None, default=None)
255 255 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
256 256 extern_type = Column("extern_type", String(255), nullable=True, unique=None, default=None)
257 257 extern_name = Column("extern_name", String(255), nullable=True, unique=None, default=None)
258 258 api_key = Column("api_key", String(255), nullable=True, unique=None, default=None)
259 259 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
260 260 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
261 261
262 262 user_log = relationship('UserLog')
263 263 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
264 264
265 265 repositories = relationship('Repository')
266 266 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
267 267 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
268 268
269 269 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
270 270 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
271 271
272 272 group_member = relationship('UserGroupMember', cascade='all')
273 273
274 274 notifications = relationship('UserNotification', cascade='all')
275 275 # notifications assigned to this user
276 276 user_created_notifications = relationship('Notification', cascade='all')
277 277 # comments created by this user
278 278 user_comments = relationship('ChangesetComment', cascade='all')
279 279 user_emails = relationship('UserEmailMap', cascade='all')
280 280
281 281 @hybrid_property
282 282 def email(self):
283 283 return self._email
284 284
285 285 @email.setter
286 286 def email(self, val):
287 287 self._email = val.lower() if val else None
288 288
289 289 @property
290 290 def firstname(self):
291 291 # alias for future
292 292 return self.name
293 293
294 294 @property
295 295 def username_and_name(self):
296 296 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
297 297
298 298 @property
299 299 def full_name(self):
300 300 return '%s %s' % (self.firstname, self.lastname)
301 301
302 302 @property
303 303 def full_contact(self):
304 304 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
305 305
306 306 @property
307 307 def short_contact(self):
308 308 return '%s %s' % (self.firstname, self.lastname)
309 309
310 310 @property
311 311 def is_admin(self):
312 312 return self.admin
313 313
314 314 @classmethod
315 315 def get_by_username(cls, username, case_insensitive=False, cache=False):
316 316 if case_insensitive:
317 317 q = cls.query().filter(cls.username.ilike(username))
318 318 else:
319 319 q = cls.query().filter(cls.username == username)
320 320
321 321 if cache:
322 322 q = q.options(FromCache(
323 323 "sql_cache_short",
324 324 "get_user_%s" % _hash_key(username)
325 325 )
326 326 )
327 327 return q.scalar()
328 328
329 329 @classmethod
330 330 def get_by_auth_token(cls, auth_token, cache=False):
331 331 q = cls.query().filter(cls.api_key == auth_token)
332 332
333 333 if cache:
334 334 q = q.options(FromCache("sql_cache_short",
335 335 "get_auth_token_%s" % auth_token))
336 336 return q.scalar()
337 337
338 338 @classmethod
339 339 def get_by_email(cls, email, case_insensitive=False, cache=False):
340 340 if case_insensitive:
341 341 q = cls.query().filter(cls.email.ilike(email))
342 342 else:
343 343 q = cls.query().filter(cls.email == email)
344 344
345 345 if cache:
346 346 q = q.options(FromCache("sql_cache_short",
347 347 "get_email_key_%s" % email))
348 348
349 349 ret = q.scalar()
350 350 if ret is None:
351 351 q = UserEmailMap.query()
352 352 # try fetching in alternate email map
353 353 if case_insensitive:
354 354 q = q.filter(UserEmailMap.email.ilike(email))
355 355 else:
356 356 q = q.filter(UserEmailMap.email == email)
357 357 q = q.options(joinedload(UserEmailMap.user))
358 358 if cache:
359 359 q = q.options(FromCache("sql_cache_short",
360 360 "get_email_map_key_%s" % email))
361 361 ret = getattr(q.scalar(), 'user', None)
362 362
363 363 return ret
364 364
365 365 @classmethod
366 366 def get_first_admin(cls):
367 367 user = User.query().filter(User.admin == True).first()
368 368 if user is None:
369 369 raise Exception('Missing administrative account!')
370 370 return user
371 371
372 372 @classmethod
373 373 def get_default_user(cls, cache=False):
374 374 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
375 375 if user is None:
376 376 raise Exception('Missing default account!')
377 377 return user
378 378
379 379
380 380
381 381
382 382 class UserEmailMap(Base, BaseModel):
383 383 __tablename__ = 'user_email_map'
384 384 __table_args__ = (
385 385 Index('uem_email_idx', 'email'),
386 386 UniqueConstraint('email'),
387 387 {'extend_existing': True, 'mysql_engine': 'InnoDB',
388 388 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
389 389 )
390 390
391 391
392 392 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
393 393 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
394 394 _email = Column("email", String(255), nullable=True, unique=False, default=None)
395 395 user = relationship('User', lazy='joined')
396 396
397 397 @validates('_email')
398 398 def validate_email(self, key, email):
399 399 # check if this email is not main one
400 400 main_email = Session().query(User).filter(User.email == email).scalar()
401 401 if main_email is not None:
402 402 raise AttributeError('email %s is present is user table' % email)
403 403 return email
404 404
405 405 @hybrid_property
406 406 def email(self):
407 407 return self._email
408 408
409 409 @email.setter
410 410 def email(self, val):
411 411 self._email = val.lower() if val else None
412 412
413 413
414 414 class UserIpMap(Base, BaseModel):
415 415 __tablename__ = 'user_ip_map'
416 416 __table_args__ = (
417 417 UniqueConstraint('user_id', 'ip_addr'),
418 418 {'extend_existing': True, 'mysql_engine': 'InnoDB',
419 419 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
420 420 )
421 421
422 422
423 423 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
424 424 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
425 425 ip_addr = Column("ip_addr", String(255), nullable=True, unique=False, default=None)
426 426 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
427 427 user = relationship('User', lazy='joined')
428 428
429 429
430 430 class UserLog(Base, BaseModel):
431 431 __tablename__ = 'user_logs'
432 432 __table_args__ = (
433 433 {'extend_existing': True, 'mysql_engine': 'InnoDB',
434 434 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
435 435 )
436 436 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
437 437 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
438 438 username = Column("username", String(255), nullable=True, unique=None, default=None)
439 439 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
440 440 repository_name = Column("repository_name", String(255), nullable=True, unique=None, default=None)
441 441 user_ip = Column("user_ip", String(255), nullable=True, unique=None, default=None)
442 442 action = Column("action", String(1200000), nullable=True, unique=None, default=None)
443 443 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
444 444
445 445 def __unicode__(self):
446 446 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
447 447 self.repository_name,
448 448 self.action)
449 449
450 450 user = relationship('User')
451 451 repository = relationship('Repository', cascade='')
452 452
453 453
454 454 class UserGroup(Base, BaseModel):
455 455 __tablename__ = 'users_groups'
456 456 __table_args__ = (
457 457 {'extend_existing': True, 'mysql_engine': 'InnoDB',
458 458 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
459 459 )
460 460
461 461 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
462 462 users_group_name = Column("users_group_name", String(255), nullable=False, unique=True, default=None)
463 463 user_group_description = Column("user_group_description", String(10000), nullable=True, unique=None, default=None)
464 464 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
465 465 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
466 466 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
467 467 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
468 468
469 469 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
470 470 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
471 471 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
472 472 users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
473 473 user_user_group_to_perm = relationship('UserUserGroupToPerm', cascade='all')
474 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm ', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
474 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
475 475
476 476 user = relationship('User')
477 477
478 478 def __unicode__(self):
479 479 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
480 480 self.users_group_id,
481 481 self.users_group_name)
482 482
483 483 @classmethod
484 484 def get_by_group_name(cls, group_name, cache=False,
485 485 case_insensitive=False):
486 486 if case_insensitive:
487 487 q = cls.query().filter(cls.users_group_name.ilike(group_name))
488 488 else:
489 489 q = cls.query().filter(cls.users_group_name == group_name)
490 490 if cache:
491 491 q = q.options(FromCache(
492 492 "sql_cache_short",
493 493 "get_user_%s" % _hash_key(group_name)
494 494 )
495 495 )
496 496 return q.scalar()
497 497
498 498 @classmethod
499 499 def get(cls, user_group_id, cache=False):
500 500 user_group = cls.query()
501 501 if cache:
502 502 user_group = user_group.options(FromCache("sql_cache_short",
503 503 "get_users_group_%s" % user_group_id))
504 504 return user_group.get(user_group_id)
505 505
506 506
507 507 class UserGroupMember(Base, BaseModel):
508 508 __tablename__ = 'users_groups_members'
509 509 __table_args__ = (
510 510 {'extend_existing': True, 'mysql_engine': 'InnoDB',
511 511 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
512 512 )
513 513
514 514 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
515 515 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
516 516 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
517 517
518 518 user = relationship('User', lazy='joined')
519 519 users_group = relationship('UserGroup')
520 520
521 521 def __init__(self, gr_id='', u_id=''):
522 522 self.users_group_id = gr_id
523 523 self.user_id = u_id
524 524
525 525
526 526 class RepositoryField(Base, BaseModel):
527 527 __tablename__ = 'repositories_fields'
528 528 __table_args__ = (
529 529 UniqueConstraint('repository_id', 'field_key'), # no-multi field
530 530 {'extend_existing': True, 'mysql_engine': 'InnoDB',
531 531 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
532 532 )
533 533 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
534 534
535 535 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
536 536 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
537 537 field_key = Column("field_key", String(250))
538 538 field_label = Column("field_label", String(1024), nullable=False)
539 539 field_value = Column("field_value", String(10000), nullable=False)
540 540 field_desc = Column("field_desc", String(1024), nullable=False)
541 541 field_type = Column("field_type", String(256), nullable=False, unique=None)
542 542 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
543 543
544 544 repository = relationship('Repository')
545 545
546 546 @classmethod
547 547 def get_by_key_name(cls, key, repo):
548 548 row = cls.query()\
549 549 .filter(cls.repository == repo)\
550 550 .filter(cls.field_key == key).scalar()
551 551 return row
552 552
553 553
554 554 class Repository(Base, BaseModel):
555 555 __tablename__ = 'repositories'
556 556 __table_args__ = (
557 557 UniqueConstraint('repo_name'),
558 558 Index('r_repo_name_idx', 'repo_name'),
559 559 {'extend_existing': True, 'mysql_engine': 'InnoDB',
560 560 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
561 561 )
562 562
563 563 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
564 564 repo_name = Column("repo_name", String(255), nullable=False, unique=True, default=None)
565 565 clone_uri = Column("clone_uri", String(255), nullable=True, unique=False, default=None)
566 566 repo_type = Column("repo_type", String(255), nullable=False, unique=False, default=None)
567 567 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
568 568 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
569 569 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
570 570 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
571 571 description = Column("description", String(10000), nullable=True, unique=None, default=None)
572 572 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
573 573 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
574 574 _landing_revision = Column("landing_revision", String(255), nullable=False, unique=False, default=None)
575 575 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
576 576 _locked = Column("locked", String(255), nullable=True, unique=False, default=None)
577 577 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
578 578
579 579 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
580 580 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
581 581
582 582 user = relationship('User')
583 583 fork = relationship('Repository', remote_side=repo_id)
584 584 group = relationship('RepoGroup')
585 585 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
586 586 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
587 587 stats = relationship('Statistics', cascade='all', uselist=False)
588 588
589 589 followers = relationship('UserFollowing',
590 590 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
591 591 cascade='all')
592 592 extra_fields = relationship('RepositoryField',
593 593 cascade="all, delete, delete-orphan")
594 594
595 595 logs = relationship('UserLog')
596 596 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
597 597
598 598 pull_requests_org = relationship('PullRequest',
599 599 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
600 600 cascade="all, delete, delete-orphan")
601 601
602 602 pull_requests_other = relationship('PullRequest',
603 603 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
604 604 cascade="all, delete, delete-orphan")
605 605
606 606 #NOTE: marcink, DO NOT REMOVE THOSE
607 607 @hybrid_property
608 608 def landing_rev(self):
609 609 # always should return [rev_type, rev]
610 610 if self._landing_revision:
611 611 _rev_info = self._landing_revision.split(':')
612 612 if len(_rev_info) < 2:
613 613 _rev_info.insert(0, 'rev')
614 614 return [_rev_info[0], _rev_info[1]]
615 615 return [None, None]
616 616
617 617 @landing_rev.setter
618 618 def landing_rev(self, val):
619 619 if ':' not in val:
620 620 raise ValueError('value must be delimited with `:` and consist '
621 621 'of <rev_type>:<rev>, got %s instead' % val)
622 622 self._landing_revision = val
623 623
624 624 def __unicode__(self):
625 625 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
626 626 safe_str(self.repo_name))
627 627
628 628 @classmethod
629 629 def get_by_repo_name(cls, repo_name):
630 630 q = Session().query(cls).filter(cls.repo_name == repo_name)
631 631 q = q.options(joinedload(Repository.fork))\
632 632 .options(joinedload(Repository.user))\
633 633 .options(joinedload(Repository.group))
634 634 return q.scalar()
635 635
636 636
637 637 class RepoGroup(Base, BaseModel):
638 638 __tablename__ = 'groups'
639 639 __table_args__ = (
640 640 UniqueConstraint('group_name', 'group_parent_id'),
641 641 {'extend_existing': True, 'mysql_engine': 'InnoDB',
642 642 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
643 643 )
644 644
645 645
646 646 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
647 647 group_name = Column("group_name", String(255), nullable=False, unique=True, default=None)
648 648 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
649 649 group_description = Column("group_description", String(10000), nullable=True, unique=None, default=None)
650 650 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
651 651 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
652 652 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
653 653
654 654 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
655 655 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
656 656 parent_group = relationship('RepoGroup', remote_side=group_id)
657 657 user = relationship('User')
658 658
659 659 def __init__(self, group_name='', parent_group=None):
660 660 self.group_name = group_name
661 661 self.parent_group = parent_group
662 662
663 663 def __unicode__(self):
664 664 return u"<%s('id:%s:%s')>" % (self.__class__.__name__, self.group_id,
665 665 self.group_name)
666 666
667 667 @classmethod
668 668 def url_sep(cls):
669 669 return URL_SEP
670 670
671 671 @classmethod
672 672 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
673 673 if case_insensitive:
674 674 gr = cls.query()\
675 675 .filter(cls.group_name.ilike(group_name))
676 676 else:
677 677 gr = cls.query()\
678 678 .filter(cls.group_name == group_name)
679 679 if cache:
680 680 gr = gr.options(FromCache(
681 681 "sql_cache_short",
682 682 "get_group_%s" % _hash_key(group_name)
683 683 )
684 684 )
685 685 return gr.scalar()
686 686
687 687
688 688 class Permission(Base, BaseModel):
689 689 __tablename__ = 'permissions'
690 690 __table_args__ = (
691 691 Index('p_perm_name_idx', 'permission_name'),
692 692 {'extend_existing': True, 'mysql_engine': 'InnoDB',
693 693 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
694 694 )
695 695 PERMS = [
696 696 ('hg.admin', _('RhodeCode Administrator')),
697 697
698 698 ('repository.none', _('Repository no access')),
699 699 ('repository.read', _('Repository read access')),
700 700 ('repository.write', _('Repository write access')),
701 701 ('repository.admin', _('Repository admin access')),
702 702
703 703 ('group.none', _('Repository group no access')),
704 704 ('group.read', _('Repository group read access')),
705 705 ('group.write', _('Repository group write access')),
706 706 ('group.admin', _('Repository group admin access')),
707 707
708 708 ('usergroup.none', _('User group no access')),
709 709 ('usergroup.read', _('User group read access')),
710 710 ('usergroup.write', _('User group write access')),
711 711 ('usergroup.admin', _('User group admin access')),
712 712
713 713 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
714 714 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
715 715
716 716 ('hg.usergroup.create.false', _('User Group creation disabled')),
717 717 ('hg.usergroup.create.true', _('User Group creation enabled')),
718 718
719 719 ('hg.create.none', _('Repository creation disabled')),
720 720 ('hg.create.repository', _('Repository creation enabled')),
721 721
722 722 ('hg.fork.none', _('Repository forking disabled')),
723 723 ('hg.fork.repository', _('Repository forking enabled')),
724 724
725 725 ('hg.register.none', _('Registration disabled')),
726 726 ('hg.register.manual_activate', _('User Registration with manual account activation')),
727 727 ('hg.register.auto_activate', _('User Registration with automatic account activation')),
728 728
729 729 ('hg.extern_activate.manual', _('Manual activation of external account')),
730 730 ('hg.extern_activate.auto', _('Automatic activation of external account')),
731 731
732 732 ]
733 733
734 734 #definition of system default permissions for DEFAULT user
735 735 DEFAULT_USER_PERMISSIONS = [
736 736 'repository.read',
737 737 'group.read',
738 738 'usergroup.read',
739 739 'hg.create.repository',
740 740 'hg.fork.repository',
741 741 'hg.register.manual_activate',
742 742 'hg.extern_activate.auto',
743 743 ]
744 744
745 745 # defines which permissions are more important higher the more important
746 746 # Weight defines which permissions are more important.
747 747 # The higher number the more important.
748 748 PERM_WEIGHTS = {
749 749 'repository.none': 0,
750 750 'repository.read': 1,
751 751 'repository.write': 3,
752 752 'repository.admin': 4,
753 753
754 754 'group.none': 0,
755 755 'group.read': 1,
756 756 'group.write': 3,
757 757 'group.admin': 4,
758 758
759 759 'usergroup.none': 0,
760 760 'usergroup.read': 1,
761 761 'usergroup.write': 3,
762 762 'usergroup.admin': 4,
763 763 'hg.repogroup.create.false': 0,
764 764 'hg.repogroup.create.true': 1,
765 765
766 766 'hg.usergroup.create.false': 0,
767 767 'hg.usergroup.create.true': 1,
768 768
769 769 'hg.fork.none': 0,
770 770 'hg.fork.repository': 1,
771 771 'hg.create.none': 0,
772 772 'hg.create.repository': 1
773 773 }
774 774
775 775 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
776 776 permission_name = Column("permission_name", String(255), nullable=True, unique=None, default=None)
777 777 permission_longname = Column("permission_longname", String(255), nullable=True, unique=None, default=None)
778 778
779 779 def __unicode__(self):
780 780 return u"<%s('%s:%s')>" % (
781 781 self.__class__.__name__, self.permission_id, self.permission_name
782 782 )
783 783
784 784 @classmethod
785 785 def get_by_key(cls, key):
786 786 return cls.query().filter(cls.permission_name == key).scalar()
787 787
788 788
789 789 class UserRepoToPerm(Base, BaseModel):
790 790 __tablename__ = 'repo_to_perm'
791 791 __table_args__ = (
792 792 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
793 793 {'extend_existing': True, 'mysql_engine': 'InnoDB',
794 794 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
795 795 )
796 796 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
797 797 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
798 798 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
799 799 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
800 800
801 801 user = relationship('User')
802 802 repository = relationship('Repository')
803 803 permission = relationship('Permission')
804 804
805 805 def __unicode__(self):
806 806 return u'<%s => %s >' % (self.user, self.repository)
807 807
808 808
809 809 class UserUserGroupToPerm(Base, BaseModel):
810 810 __tablename__ = 'user_user_group_to_perm'
811 811 __table_args__ = (
812 812 UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
813 813 {'extend_existing': True, 'mysql_engine': 'InnoDB',
814 814 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
815 815 )
816 816 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
817 817 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
818 818 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
819 819 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
820 820
821 821 user = relationship('User')
822 822 user_group = relationship('UserGroup')
823 823 permission = relationship('Permission')
824 824
825 825 def __unicode__(self):
826 826 return u'<%s => %s >' % (self.user, self.user_group)
827 827
828 828
829 829 class UserToPerm(Base, BaseModel):
830 830 __tablename__ = 'user_to_perm'
831 831 __table_args__ = (
832 832 UniqueConstraint('user_id', 'permission_id'),
833 833 {'extend_existing': True, 'mysql_engine': 'InnoDB',
834 834 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
835 835 )
836 836 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
837 837 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
838 838 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
839 839
840 840 user = relationship('User')
841 841 permission = relationship('Permission', lazy='joined')
842 842
843 843 def __unicode__(self):
844 844 return u'<%s => %s >' % (self.user, self.permission)
845 845
846 846
847 847 class UserGroupRepoToPerm(Base, BaseModel):
848 848 __tablename__ = 'users_group_repo_to_perm'
849 849 __table_args__ = (
850 850 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
851 851 {'extend_existing': True, 'mysql_engine': 'InnoDB',
852 852 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
853 853 )
854 854 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
855 855 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
856 856 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
857 857 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
858 858
859 859 users_group = relationship('UserGroup')
860 860 permission = relationship('Permission')
861 861 repository = relationship('Repository')
862 862
863 863 def __unicode__(self):
864 864 return u'<UserGroupRepoToPerm:%s => %s >' % (self.users_group, self.repository)
865 865
866 866
867 867 class UserGroupUserGroupToPerm(Base, BaseModel):
868 868 __tablename__ = 'user_group_user_group_to_perm'
869 869 __table_args__ = (
870 870 UniqueConstraint('target_user_group_id', 'user_group_id', 'permission_id'),
871 871 CheckConstraint('target_user_group_id != user_group_id'),
872 872 {'extend_existing': True, 'mysql_engine': 'InnoDB',
873 873 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
874 874 )
875 875 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)
876 876 target_user_group_id = Column("target_user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
877 877 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
878 878 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
879 879
880 880 target_user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
881 881 user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
882 882 permission = relationship('Permission')
883 883
884 884 def __unicode__(self):
885 885 return u'<UserGroupUserGroup:%s => %s >' % (self.target_user_group, self.user_group)
886 886
887 887
888 888 class UserGroupToPerm(Base, BaseModel):
889 889 __tablename__ = 'users_group_to_perm'
890 890 __table_args__ = (
891 891 UniqueConstraint('users_group_id', 'permission_id',),
892 892 {'extend_existing': True, 'mysql_engine': 'InnoDB',
893 893 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
894 894 )
895 895 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
896 896 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
897 897 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
898 898
899 899 users_group = relationship('UserGroup')
900 900 permission = relationship('Permission')
901 901
902 902
903 903 class UserRepoGroupToPerm(Base, BaseModel):
904 904 __tablename__ = 'user_repo_group_to_perm'
905 905 __table_args__ = (
906 906 UniqueConstraint('user_id', 'group_id', 'permission_id'),
907 907 {'extend_existing': True, 'mysql_engine': 'InnoDB',
908 908 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
909 909 )
910 910
911 911 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
912 912 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
913 913 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
914 914 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
915 915
916 916 user = relationship('User')
917 917 group = relationship('RepoGroup')
918 918 permission = relationship('Permission')
919 919
920 920
921 921 class UserGroupRepoGroupToPerm(Base, BaseModel):
922 922 __tablename__ = 'users_group_repo_group_to_perm'
923 923 __table_args__ = (
924 924 UniqueConstraint('users_group_id', 'group_id'),
925 925 {'extend_existing': True, 'mysql_engine': 'InnoDB',
926 926 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
927 927 )
928 928
929 929 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)
930 930 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
931 931 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
932 932 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
933 933
934 934 users_group = relationship('UserGroup')
935 935 permission = relationship('Permission')
936 936 group = relationship('RepoGroup')
937 937
938 938
939 939 class Statistics(Base, BaseModel):
940 940 __tablename__ = 'statistics'
941 941 __table_args__ = (
942 942 UniqueConstraint('repository_id'),
943 943 {'extend_existing': True, 'mysql_engine': 'InnoDB',
944 944 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
945 945 )
946 946 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
947 947 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
948 948 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
949 949 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
950 950 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
951 951 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
952 952
953 953 repository = relationship('Repository', single_parent=True)
954 954
955 955
956 956 class UserFollowing(Base, BaseModel):
957 957 __tablename__ = 'user_followings'
958 958 __table_args__ = (
959 959 UniqueConstraint('user_id', 'follows_repository_id'),
960 960 UniqueConstraint('user_id', 'follows_user_id'),
961 961 {'extend_existing': True, 'mysql_engine': 'InnoDB',
962 962 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
963 963 )
964 964
965 965 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
966 966 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
967 967 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
968 968 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
969 969 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
970 970
971 971 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
972 972
973 973 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
974 974 follows_repository = relationship('Repository', order_by='Repository.repo_name')
975 975
976 976
977 977 class CacheInvalidation(Base, BaseModel):
978 978 __tablename__ = 'cache_invalidation'
979 979 __table_args__ = (
980 980 UniqueConstraint('cache_key'),
981 981 Index('key_idx', 'cache_key'),
982 982 {'extend_existing': True, 'mysql_engine': 'InnoDB',
983 983 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
984 984 )
985 985 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
986 986 cache_key = Column("cache_key", String(255), nullable=True, unique=None, default=None)
987 987 cache_args = Column("cache_args", String(255), nullable=True, unique=None, default=None)
988 988 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
989 989
990 990 def __init__(self, cache_key, cache_args=''):
991 991 self.cache_key = cache_key
992 992 self.cache_args = cache_args
993 993 self.cache_active = False
994 994
995 995
996 996 class ChangesetComment(Base, BaseModel):
997 997 __tablename__ = 'changeset_comments'
998 998 __table_args__ = (
999 999 Index('cc_revision_idx', 'revision'),
1000 1000 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1001 1001 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1002 1002 )
1003 1003 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1004 1004 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1005 1005 revision = Column('revision', String(40), nullable=True)
1006 1006 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1007 1007 line_no = Column('line_no', Unicode(10), nullable=True)
1008 1008 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1009 1009 f_path = Column('f_path', Unicode(1000), nullable=True)
1010 1010 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1011 1011 text = Column('text', UnicodeText().with_variant(UnicodeText(25000), 'mysql'), nullable=False)
1012 1012 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1013 1013 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1014 1014
1015 1015 author = relationship('User', lazy='joined')
1016 1016 repo = relationship('Repository')
1017 1017 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1018 1018 pull_request = relationship('PullRequest', lazy='joined')
1019 1019
1020 1020
1021 1021 class ChangesetStatus(Base, BaseModel):
1022 1022 __tablename__ = 'changeset_statuses'
1023 1023 __table_args__ = (
1024 1024 Index('cs_revision_idx', 'revision'),
1025 1025 Index('cs_version_idx', 'version'),
1026 1026 UniqueConstraint('repo_id', 'revision', 'version'),
1027 1027 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1028 1028 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1029 1029 )
1030 1030 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1031 1031 STATUS_APPROVED = 'approved'
1032 1032 STATUS_REJECTED = 'rejected'
1033 1033 STATUS_UNDER_REVIEW = 'under_review'
1034 1034
1035 1035 STATUSES = [
1036 1036 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1037 1037 (STATUS_APPROVED, _("Approved")),
1038 1038 (STATUS_REJECTED, _("Rejected")),
1039 1039 (STATUS_UNDER_REVIEW, _("Under Review")),
1040 1040 ]
1041 1041
1042 1042 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1043 1043 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1044 1044 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1045 1045 revision = Column('revision', String(40), nullable=False)
1046 1046 status = Column('status', String(128), nullable=False, default=DEFAULT)
1047 1047 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1048 1048 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1049 1049 version = Column('version', Integer(), nullable=False, default=0)
1050 1050 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1051 1051
1052 1052 author = relationship('User', lazy='joined')
1053 1053 repo = relationship('Repository')
1054 1054 comment = relationship('ChangesetComment', lazy='joined')
1055 1055 pull_request = relationship('PullRequest', lazy='joined')
1056 1056
1057 1057
1058 1058
1059 1059 class PullRequest(Base, BaseModel):
1060 1060 __tablename__ = 'pull_requests'
1061 1061 __table_args__ = (
1062 1062 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1063 1063 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1064 1064 )
1065 1065
1066 1066 STATUS_NEW = u'new'
1067 1067 STATUS_OPEN = u'open'
1068 1068 STATUS_CLOSED = u'closed'
1069 1069
1070 1070 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1071 1071 title = Column('title', Unicode(256), nullable=True)
1072 1072 description = Column('description', UnicodeText().with_variant(UnicodeText(10240), 'mysql'), nullable=True)
1073 1073 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1074 1074 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1075 1075 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1076 1076 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1077 1077 _revisions = Column('revisions', UnicodeText().with_variant(UnicodeText(20500), 'mysql'))
1078 1078 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1079 1079 org_ref = Column('org_ref', Unicode(256), nullable=False)
1080 1080 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1081 1081 other_ref = Column('other_ref', Unicode(256), nullable=False)
1082 1082
1083 1083 author = relationship('User', lazy='joined')
1084 1084 reviewers = relationship('PullRequestReviewers',
1085 1085 cascade="all, delete, delete-orphan")
1086 1086 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1087 1087 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1088 1088 statuses = relationship('ChangesetStatus')
1089 1089 comments = relationship('ChangesetComment',
1090 1090 cascade="all, delete, delete-orphan")
1091 1091
1092 1092
1093 1093 class PullRequestReviewers(Base, BaseModel):
1094 1094 __tablename__ = 'pull_request_reviewers'
1095 1095 __table_args__ = (
1096 1096 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1097 1097 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1098 1098 )
1099 1099
1100 1100 def __init__(self, user=None, pull_request=None):
1101 1101 self.user = user
1102 1102 self.pull_request = pull_request
1103 1103
1104 1104 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1105 1105 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1106 1106 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1107 1107
1108 1108 user = relationship('User')
1109 1109 pull_request = relationship('PullRequest')
1110 1110
1111 1111
1112 1112 class Notification(Base, BaseModel):
1113 1113 __tablename__ = 'notifications'
1114 1114 __table_args__ = (
1115 1115 Index('notification_type_idx', 'type'),
1116 1116 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1117 1117 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1118 1118 )
1119 1119
1120 1120 TYPE_CHANGESET_COMMENT = u'cs_comment'
1121 1121 TYPE_MESSAGE = u'message'
1122 1122 TYPE_MENTION = u'mention'
1123 1123 TYPE_REGISTRATION = u'registration'
1124 1124 TYPE_PULL_REQUEST = u'pull_request'
1125 1125 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1126 1126
1127 1127 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1128 1128 subject = Column('subject', Unicode(512), nullable=True)
1129 1129 body = Column('body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True)
1130 1130 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1131 1131 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1132 1132 type_ = Column('type', Unicode(256))
1133 1133
1134 1134 created_by_user = relationship('User')
1135 1135 notifications_to_users = relationship('UserNotification', lazy='joined',
1136 1136 cascade="all, delete, delete-orphan")
1137 1137
1138 1138
1139 1139 class UserNotification(Base, BaseModel):
1140 1140 __tablename__ = 'user_to_notification'
1141 1141 __table_args__ = (
1142 1142 UniqueConstraint('user_id', 'notification_id'),
1143 1143 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1144 1144 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1145 1145 )
1146 1146 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1147 1147 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1148 1148 read = Column('read', Boolean, default=False)
1149 1149 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1150 1150
1151 1151 user = relationship('User', lazy="joined")
1152 1152 notification = relationship('Notification', lazy="joined",
1153 1153 order_by=lambda: Notification.created_on.desc(),)
1154 1154
1155 1155
1156 1156 class Gist(Base, BaseModel):
1157 1157 __tablename__ = 'gists'
1158 1158 __table_args__ = (
1159 1159 Index('g_gist_access_id_idx', 'gist_access_id'),
1160 1160 Index('g_created_on_idx', 'created_on'),
1161 1161 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1162 1162 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1163 1163 )
1164 1164 GIST_PUBLIC = u'public'
1165 1165 GIST_PRIVATE = u'private'
1166 1166
1167 1167 gist_id = Column('gist_id', Integer(), primary_key=True)
1168 1168 gist_access_id = Column('gist_access_id', Unicode(250))
1169 1169 gist_description = Column('gist_description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
1170 1170 gist_owner = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=True)
1171 1171 gist_expires = Column('gist_expires', Float(53), nullable=False)
1172 1172 gist_type = Column('gist_type', Unicode(128), nullable=False)
1173 1173 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1174 1174 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1175 1175
1176 1176 owner = relationship('User')
1177 1177
1178 1178
1179 1179 class DbMigrateVersion(Base, BaseModel):
1180 1180 __tablename__ = 'db_migrate_version'
1181 1181 __table_args__ = (
1182 1182 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1183 1183 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1184 1184 )
1185 1185 repository_id = Column('repository_id', String(250), primary_key=True)
1186 1186 repository_path = Column('repository_path', Text)
1187 1187 version = Column('version', Integer)
@@ -1,1204 +1,1204 b''
1 1
2 2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 3 #
4 4 # This program is free software: you can redistribute it and/or modify
5 5 # it under the terms of the GNU Affero General Public License, version 3
6 6 # (only), as published by the Free Software Foundation.
7 7 #
8 8 # This program is distributed in the hope that it will be useful,
9 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 11 # GNU General Public License for more details.
12 12 #
13 13 # You should have received a copy of the GNU Affero General Public License
14 14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 15 #
16 16 # This program is dual-licensed. If you wish to learn more about the
17 17 # RhodeCode Enterprise Edition, including its added features, Support services,
18 18 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 19
20 20 import os
21 21 import time
22 22 import logging
23 23 import datetime
24 24 import traceback
25 25 import hashlib
26 26 import collections
27 27 import functools
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 remove_prefix, time_to_datetime, aslist, Optional, safe_int
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.items():
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 try:
145 145 return safe_str(self.__unicode__())
146 146 except UnicodeDecodeError:
147 147 pass
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', 'sqlite_autoincrement': True}
157 157 )
158 158
159 159 SETTINGS_TYPES = {
160 160 'str': safe_str,
161 161 'int': safe_int,
162 162 'unicode': safe_str,
163 163 'bool': str2bool,
164 164 'list': functools.partial(aslist, sep=',')
165 165 }
166 166 DEFAULT_UPDATE_URL = 'https://rhodecode.com/api/v1/info/versions'
167 167
168 168 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
169 169 app_settings_name = Column("app_settings_name", String(255), nullable=True, unique=None, default=None)
170 170 _app_settings_value = Column("app_settings_value", String(4096), nullable=True, unique=None, default=None)
171 171 _app_settings_type = Column("app_settings_type", String(255), nullable=True, unique=None, default=None)
172 172
173 173 def __init__(self, key='', val='', type='unicode'):
174 174 self.app_settings_name = key
175 175 self.app_settings_value = val
176 176 self.app_settings_type = type
177 177
178 178 @validates('_app_settings_value')
179 179 def validate_settings_value(self, key, val):
180 180 assert type(val) == str
181 181 return val
182 182
183 183 @hybrid_property
184 184 def app_settings_value(self):
185 185 v = self._app_settings_value
186 186 _type = self.app_settings_type
187 187 converter = self.SETTINGS_TYPES.get(_type) or self.SETTINGS_TYPES['unicode']
188 188 return converter(v)
189 189
190 190 @app_settings_value.setter
191 191 def app_settings_value(self, val):
192 192 """
193 193 Setter that will always make sure we use unicode in app_settings_value
194 194
195 195 :param val:
196 196 """
197 197 self._app_settings_value = safe_str(val)
198 198
199 199 @hybrid_property
200 200 def app_settings_type(self):
201 201 return self._app_settings_type
202 202
203 203 @app_settings_type.setter
204 204 def app_settings_type(self, val):
205 205 if val not in self.SETTINGS_TYPES:
206 206 raise Exception('type must be one of %s got %s'
207 207 % (self.SETTINGS_TYPES.keys(), val))
208 208 self._app_settings_type = val
209 209
210 210 def __unicode__(self):
211 211 return u"<%s('%s:%s[%s]')>" % (
212 212 self.__class__.__name__,
213 213 self.app_settings_name, self.app_settings_value, self.app_settings_type
214 214 )
215 215
216 216
217 217 class RhodeCodeUi(Base, BaseModel):
218 218 __tablename__ = 'rhodecode_ui'
219 219 __table_args__ = (
220 220 UniqueConstraint('ui_key'),
221 221 {'extend_existing': True, 'mysql_engine': 'InnoDB',
222 222 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
223 223 )
224 224
225 225 HOOK_REPO_SIZE = 'changegroup.repo_size'
226 226 HOOK_PUSH = 'changegroup.push_logger'
227 227 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
228 228 HOOK_PULL = 'outgoing.pull_logger'
229 229 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
230 230
231 231 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
232 232 ui_section = Column("ui_section", String(255), nullable=True, unique=None, default=None)
233 233 ui_key = Column("ui_key", String(255), nullable=True, unique=None, default=None)
234 234 ui_value = Column("ui_value", String(255), nullable=True, unique=None, default=None)
235 235 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
236 236
237 237
238 238
239 239 class User(Base, BaseModel):
240 240 __tablename__ = 'users'
241 241 __table_args__ = (
242 242 UniqueConstraint('username'), UniqueConstraint('email'),
243 243 Index('u_username_idx', 'username'),
244 244 Index('u_email_idx', 'email'),
245 245 {'extend_existing': True, 'mysql_engine': 'InnoDB',
246 246 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
247 247 )
248 248 DEFAULT_USER = 'default'
249 249 DEFAULT_GRAVATAR_URL = 'https://secure.gravatar.com/avatar/{md5email}?d=identicon&s={size}'
250 250
251 251 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
252 252 username = Column("username", String(255), nullable=True, unique=None, default=None)
253 253 password = Column("password", String(255), nullable=True, unique=None, default=None)
254 254 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
255 255 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
256 256 name = Column("firstname", String(255), nullable=True, unique=None, default=None)
257 257 lastname = Column("lastname", String(255), nullable=True, unique=None, default=None)
258 258 _email = Column("email", String(255), nullable=True, unique=None, default=None)
259 259 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
260 260 extern_type = Column("extern_type", String(255), nullable=True, unique=None, default=None)
261 261 extern_name = Column("extern_name", String(255), nullable=True, unique=None, default=None)
262 262 api_key = Column("api_key", String(255), nullable=True, unique=None, default=None)
263 263 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
264 264 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
265 265
266 266 user_log = relationship('UserLog')
267 267 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
268 268
269 269 repositories = relationship('Repository')
270 270 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
271 271 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
272 272
273 273 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
274 274 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
275 275
276 276 group_member = relationship('UserGroupMember', cascade='all')
277 277
278 278 notifications = relationship('UserNotification', cascade='all')
279 279 # notifications assigned to this user
280 280 user_created_notifications = relationship('Notification', cascade='all')
281 281 # comments created by this user
282 282 user_comments = relationship('ChangesetComment', cascade='all')
283 283 user_emails = relationship('UserEmailMap', cascade='all')
284 284
285 285 @hybrid_property
286 286 def email(self):
287 287 return self._email
288 288
289 289 @email.setter
290 290 def email(self, val):
291 291 self._email = val.lower() if val else None
292 292
293 293 @property
294 294 def firstname(self):
295 295 # alias for future
296 296 return self.name
297 297
298 298 @property
299 299 def username_and_name(self):
300 300 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
301 301
302 302 @property
303 303 def full_name(self):
304 304 return '%s %s' % (self.firstname, self.lastname)
305 305
306 306 @property
307 307 def full_contact(self):
308 308 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
309 309
310 310 @property
311 311 def short_contact(self):
312 312 return '%s %s' % (self.firstname, self.lastname)
313 313
314 314 @property
315 315 def is_admin(self):
316 316 return self.admin
317 317
318 318 @classmethod
319 319 def get_by_username(cls, username, case_insensitive=False, cache=False):
320 320 if case_insensitive:
321 321 q = cls.query().filter(cls.username.ilike(username))
322 322 else:
323 323 q = cls.query().filter(cls.username == username)
324 324
325 325 if cache:
326 326 q = q.options(FromCache(
327 327 "sql_cache_short",
328 328 "get_user_%s" % _hash_key(username)
329 329 )
330 330 )
331 331 return q.scalar()
332 332
333 333 @classmethod
334 334 def get_by_auth_token(cls, auth_token, cache=False, fallback=True):
335 335 q = cls.query().filter(cls.api_key == auth_token)
336 336
337 337 if cache:
338 338 q = q.options(FromCache("sql_cache_short",
339 339 "get_auth_token_%s" % auth_token))
340 340 res = q.scalar()
341 341
342 342 if fallback and not res:
343 343 #fallback to additional keys
344 344 _res = UserApiKeys.query()\
345 345 .filter(UserApiKeys.api_key == auth_token)\
346 346 .filter(or_(UserApiKeys.expires == -1,
347 347 UserApiKeys.expires >= time.time()))\
348 348 .first()
349 349 if _res:
350 350 res = _res.user
351 351 return res
352 352
353 353 @classmethod
354 354 def get_by_email(cls, email, case_insensitive=False, cache=False):
355 355
356 356 if case_insensitive:
357 357 q = cls.query().filter(cls.email.ilike(email))
358 358 else:
359 359 q = cls.query().filter(cls.email == email)
360 360
361 361 if cache:
362 362 q = q.options(FromCache("sql_cache_short",
363 363 "get_email_key_%s" % email))
364 364
365 365 ret = q.scalar()
366 366 if ret is None:
367 367 q = UserEmailMap.query()
368 368 # try fetching in alternate email map
369 369 if case_insensitive:
370 370 q = q.filter(UserEmailMap.email.ilike(email))
371 371 else:
372 372 q = q.filter(UserEmailMap.email == email)
373 373 q = q.options(joinedload(UserEmailMap.user))
374 374 if cache:
375 375 q = q.options(FromCache("sql_cache_short",
376 376 "get_email_map_key_%s" % email))
377 377 ret = getattr(q.scalar(), 'user', None)
378 378
379 379 return ret
380 380
381 381 @classmethod
382 382 def get_first_admin(cls):
383 383 user = User.query().filter(User.admin == True).first()
384 384 if user is None:
385 385 raise Exception('Missing administrative account!')
386 386 return user
387 387
388 388 @classmethod
389 389 def get_default_user(cls, cache=False):
390 390 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
391 391 if user is None:
392 392 raise Exception('Missing default account!')
393 393 return user
394 394
395 395
396 396 class UserApiKeys(Base, BaseModel):
397 397 __tablename__ = 'user_api_keys'
398 398 __table_args__ = (
399 399 Index('uak_api_key_idx', 'api_key'),
400 400 UniqueConstraint('api_key'),
401 401 {'extend_existing': True, 'mysql_engine': 'InnoDB',
402 402 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
403 403 )
404 404
405 405
406 406 user_api_key_id = Column("user_api_key_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
407 407 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
408 408 api_key = Column("api_key", String(255), nullable=False, unique=True)
409 409 description = Column('description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
410 410 expires = Column('expires', Float(53), nullable=False)
411 411 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
412 412
413 413 user = relationship('User', lazy='joined')
414 414
415 415
416 416 class UserEmailMap(Base, BaseModel):
417 417 __tablename__ = 'user_email_map'
418 418 __table_args__ = (
419 419 Index('uem_email_idx', 'email'),
420 420 UniqueConstraint('email'),
421 421 {'extend_existing': True, 'mysql_engine': 'InnoDB',
422 422 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
423 423 )
424 424
425 425
426 426 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
427 427 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
428 428 _email = Column("email", String(255), nullable=True, unique=False, default=None)
429 429 user = relationship('User', lazy='joined')
430 430
431 431 @validates('_email')
432 432 def validate_email(self, key, email):
433 433 # check if this email is not main one
434 434 main_email = Session().query(User).filter(User.email == email).scalar()
435 435 if main_email is not None:
436 436 raise AttributeError('email %s is present is user table' % email)
437 437 return email
438 438
439 439 @hybrid_property
440 440 def email(self):
441 441 return self._email
442 442
443 443 @email.setter
444 444 def email(self, val):
445 445 self._email = val.lower() if val else None
446 446
447 447
448 448 class UserIpMap(Base, BaseModel):
449 449 __tablename__ = 'user_ip_map'
450 450 __table_args__ = (
451 451 UniqueConstraint('user_id', 'ip_addr'),
452 452 {'extend_existing': True, 'mysql_engine': 'InnoDB',
453 453 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
454 454 )
455 455
456 456
457 457 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
458 458 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
459 459 ip_addr = Column("ip_addr", String(255), nullable=True, unique=False, default=None)
460 460 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
461 461 user = relationship('User', lazy='joined')
462 462
463 463
464 464 class UserLog(Base, BaseModel):
465 465 __tablename__ = 'user_logs'
466 466 __table_args__ = (
467 467 {'extend_existing': True, 'mysql_engine': 'InnoDB',
468 468 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
469 469 )
470 470 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
471 471 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
472 472 username = Column("username", String(255), nullable=True, unique=None, default=None)
473 473 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
474 474 repository_name = Column("repository_name", String(255), nullable=True, unique=None, default=None)
475 475 user_ip = Column("user_ip", String(255), nullable=True, unique=None, default=None)
476 476 action = Column("action", String(1200000), nullable=True, unique=None, default=None)
477 477 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
478 478
479 479 def __unicode__(self):
480 480 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
481 481 self.repository_name,
482 482 self.action)
483 483
484 484 user = relationship('User')
485 485 repository = relationship('Repository', cascade='')
486 486
487 487
488 488 class UserGroup(Base, BaseModel):
489 489 __tablename__ = 'users_groups'
490 490 __table_args__ = (
491 491 {'extend_existing': True, 'mysql_engine': 'InnoDB',
492 492 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
493 493 )
494 494
495 495 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
496 496 users_group_name = Column("users_group_name", String(255), nullable=False, unique=True, default=None)
497 497 user_group_description = Column("user_group_description", String(10000), nullable=True, unique=None, default=None)
498 498 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
499 499 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
500 500 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
501 501 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
502 502
503 503 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
504 504 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
505 505 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
506 506 users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
507 507 user_user_group_to_perm = relationship('UserUserGroupToPerm', cascade='all')
508 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm ', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
508 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
509 509
510 510 user = relationship('User')
511 511
512 512 def __unicode__(self):
513 513 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
514 514 self.users_group_id,
515 515 self.users_group_name)
516 516
517 517 @classmethod
518 518 def get_by_group_name(cls, group_name, cache=False,
519 519 case_insensitive=False):
520 520 if case_insensitive:
521 521 q = cls.query().filter(cls.users_group_name.ilike(group_name))
522 522 else:
523 523 q = cls.query().filter(cls.users_group_name == group_name)
524 524 if cache:
525 525 q = q.options(FromCache(
526 526 "sql_cache_short",
527 527 "get_user_%s" % _hash_key(group_name)
528 528 )
529 529 )
530 530 return q.scalar()
531 531
532 532 @classmethod
533 533 def get(cls, user_group_id, cache=False):
534 534 user_group = cls.query()
535 535 if cache:
536 536 user_group = user_group.options(FromCache("sql_cache_short",
537 537 "get_users_group_%s" % user_group_id))
538 538 return user_group.get(user_group_id)
539 539
540 540
541 541 class UserGroupMember(Base, BaseModel):
542 542 __tablename__ = 'users_groups_members'
543 543 __table_args__ = (
544 544 {'extend_existing': True, 'mysql_engine': 'InnoDB',
545 545 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
546 546 )
547 547
548 548 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
549 549 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
550 550 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
551 551
552 552 user = relationship('User', lazy='joined')
553 553 users_group = relationship('UserGroup')
554 554
555 555 def __init__(self, gr_id='', u_id=''):
556 556 self.users_group_id = gr_id
557 557 self.user_id = u_id
558 558
559 559
560 560 class RepositoryField(Base, BaseModel):
561 561 __tablename__ = 'repositories_fields'
562 562 __table_args__ = (
563 563 UniqueConstraint('repository_id', 'field_key'), # no-multi field
564 564 {'extend_existing': True, 'mysql_engine': 'InnoDB',
565 565 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
566 566 )
567 567 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
568 568
569 569 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
570 570 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
571 571 field_key = Column("field_key", String(250))
572 572 field_label = Column("field_label", String(1024), nullable=False)
573 573 field_value = Column("field_value", String(10000), nullable=False)
574 574 field_desc = Column("field_desc", String(1024), nullable=False)
575 575 field_type = Column("field_type", String(256), nullable=False, unique=None)
576 576 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
577 577
578 578 repository = relationship('Repository')
579 579
580 580 @classmethod
581 581 def get_by_key_name(cls, key, repo):
582 582 row = cls.query()\
583 583 .filter(cls.repository == repo)\
584 584 .filter(cls.field_key == key).scalar()
585 585 return row
586 586
587 587
588 588 class Repository(Base, BaseModel):
589 589 __tablename__ = 'repositories'
590 590 __table_args__ = (
591 591 UniqueConstraint('repo_name'),
592 592 Index('r_repo_name_idx', 'repo_name'),
593 593 {'extend_existing': True, 'mysql_engine': 'InnoDB',
594 594 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
595 595 )
596 596 DEFAULT_CLONE_URI = '{scheme}://{user}@{netloc}/{repo}'
597 597
598 598 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
599 599 repo_name = Column("repo_name", String(255), nullable=False, unique=True, default=None)
600 600 clone_uri = Column("clone_uri", String(255), nullable=True, unique=False, default=None)
601 601 repo_type = Column("repo_type", String(255), nullable=False, unique=False, default=None)
602 602 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
603 603 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
604 604 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
605 605 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
606 606 description = Column("description", String(10000), nullable=True, unique=None, default=None)
607 607 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
608 608 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
609 609 _landing_revision = Column("landing_revision", String(255), nullable=False, unique=False, default=None)
610 610 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
611 611 _locked = Column("locked", String(255), nullable=True, unique=False, default=None)
612 612 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
613 613
614 614 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
615 615 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
616 616
617 617 user = relationship('User')
618 618 fork = relationship('Repository', remote_side=repo_id)
619 619 group = relationship('RepoGroup')
620 620 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
621 621 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
622 622 stats = relationship('Statistics', cascade='all', uselist=False)
623 623
624 624 followers = relationship('UserFollowing',
625 625 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
626 626 cascade='all')
627 627 extra_fields = relationship('RepositoryField',
628 628 cascade="all, delete, delete-orphan")
629 629
630 630 logs = relationship('UserLog')
631 631 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
632 632
633 633 pull_requests_org = relationship('PullRequest',
634 634 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
635 635 cascade="all, delete, delete-orphan")
636 636
637 637 pull_requests_other = relationship('PullRequest',
638 638 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
639 639 cascade="all, delete, delete-orphan")
640 640
641 641 def __unicode__(self):
642 642 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
643 643 safe_str(self.repo_name))
644 644
645 645 @classmethod
646 646 def get_by_repo_name(cls, repo_name):
647 647 q = Session().query(cls).filter(cls.repo_name == repo_name)
648 648 q = q.options(joinedload(Repository.fork))\
649 649 .options(joinedload(Repository.user))\
650 650 .options(joinedload(Repository.group))
651 651 return q.scalar()
652 652
653 653
654 654 class RepoGroup(Base, BaseModel):
655 655 __tablename__ = 'groups'
656 656 __table_args__ = (
657 657 UniqueConstraint('group_name', 'group_parent_id'),
658 658 {'extend_existing': True, 'mysql_engine': 'InnoDB',
659 659 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
660 660 )
661 661
662 662
663 663 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
664 664 group_name = Column("group_name", String(255), nullable=False, unique=True, default=None)
665 665 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
666 666 group_description = Column("group_description", String(10000), nullable=True, unique=None, default=None)
667 667 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
668 668 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
669 669 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
670 670
671 671 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
672 672 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
673 673 parent_group = relationship('RepoGroup', remote_side=group_id)
674 674 user = relationship('User')
675 675
676 676 def __init__(self, group_name='', parent_group=None):
677 677 self.group_name = group_name
678 678 self.parent_group = parent_group
679 679
680 680 def __unicode__(self):
681 681 return u"<%s('id:%s:%s')>" % (self.__class__.__name__, self.group_id,
682 682 self.group_name)
683 683
684 684 @classmethod
685 685 def url_sep(cls):
686 686 return URL_SEP
687 687
688 688 @classmethod
689 689 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
690 690 if case_insensitive:
691 691 gr = cls.query()\
692 692 .filter(cls.group_name.ilike(group_name))
693 693 else:
694 694 gr = cls.query()\
695 695 .filter(cls.group_name == group_name)
696 696 if cache:
697 697 gr = gr.options(FromCache(
698 698 "sql_cache_short",
699 699 "get_group_%s" % _hash_key(group_name)
700 700 )
701 701 )
702 702 return gr.scalar()
703 703
704 704
705 705 class Permission(Base, BaseModel):
706 706 __tablename__ = 'permissions'
707 707 __table_args__ = (
708 708 Index('p_perm_name_idx', 'permission_name'),
709 709 {'extend_existing': True, 'mysql_engine': 'InnoDB',
710 710 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
711 711 )
712 712 PERMS = [
713 713 ('hg.admin', _('RhodeCode Administrator')),
714 714
715 715 ('repository.none', _('Repository no access')),
716 716 ('repository.read', _('Repository read access')),
717 717 ('repository.write', _('Repository write access')),
718 718 ('repository.admin', _('Repository admin access')),
719 719
720 720 ('group.none', _('Repository group no access')),
721 721 ('group.read', _('Repository group read access')),
722 722 ('group.write', _('Repository group write access')),
723 723 ('group.admin', _('Repository group admin access')),
724 724
725 725 ('usergroup.none', _('User group no access')),
726 726 ('usergroup.read', _('User group read access')),
727 727 ('usergroup.write', _('User group write access')),
728 728 ('usergroup.admin', _('User group admin access')),
729 729
730 730 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
731 731 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
732 732
733 733 ('hg.usergroup.create.false', _('User Group creation disabled')),
734 734 ('hg.usergroup.create.true', _('User Group creation enabled')),
735 735
736 736 ('hg.create.none', _('Repository creation disabled')),
737 737 ('hg.create.repository', _('Repository creation enabled')),
738 738
739 739 ('hg.fork.none', _('Repository forking disabled')),
740 740 ('hg.fork.repository', _('Repository forking enabled')),
741 741
742 742 ('hg.register.none', _('Registration disabled')),
743 743 ('hg.register.manual_activate', _('User Registration with manual account activation')),
744 744 ('hg.register.auto_activate', _('User Registration with automatic account activation')),
745 745
746 746 ('hg.extern_activate.manual', _('Manual activation of external account')),
747 747 ('hg.extern_activate.auto', _('Automatic activation of external account')),
748 748
749 749 ]
750 750
751 751 #definition of system default permissions for DEFAULT user
752 752 DEFAULT_USER_PERMISSIONS = [
753 753 'repository.read',
754 754 'group.read',
755 755 'usergroup.read',
756 756 'hg.create.repository',
757 757 'hg.fork.repository',
758 758 'hg.register.manual_activate',
759 759 'hg.extern_activate.auto',
760 760 ]
761 761
762 762 # defines which permissions are more important higher the more important
763 763 # Weight defines which permissions are more important.
764 764 # The higher number the more important.
765 765 PERM_WEIGHTS = {
766 766 'repository.none': 0,
767 767 'repository.read': 1,
768 768 'repository.write': 3,
769 769 'repository.admin': 4,
770 770
771 771 'group.none': 0,
772 772 'group.read': 1,
773 773 'group.write': 3,
774 774 'group.admin': 4,
775 775
776 776 'usergroup.none': 0,
777 777 'usergroup.read': 1,
778 778 'usergroup.write': 3,
779 779 'usergroup.admin': 4,
780 780 'hg.repogroup.create.false': 0,
781 781 'hg.repogroup.create.true': 1,
782 782
783 783 'hg.usergroup.create.false': 0,
784 784 'hg.usergroup.create.true': 1,
785 785
786 786 'hg.fork.none': 0,
787 787 'hg.fork.repository': 1,
788 788 'hg.create.none': 0,
789 789 'hg.create.repository': 1
790 790 }
791 791
792 792 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
793 793 permission_name = Column("permission_name", String(255), nullable=True, unique=None, default=None)
794 794 permission_longname = Column("permission_longname", String(255), nullable=True, unique=None, default=None)
795 795
796 796 def __unicode__(self):
797 797 return u"<%s('%s:%s')>" % (
798 798 self.__class__.__name__, self.permission_id, self.permission_name
799 799 )
800 800
801 801 @classmethod
802 802 def get_by_key(cls, key):
803 803 return cls.query().filter(cls.permission_name == key).scalar()
804 804
805 805
806 806 class UserRepoToPerm(Base, BaseModel):
807 807 __tablename__ = 'repo_to_perm'
808 808 __table_args__ = (
809 809 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
810 810 {'extend_existing': True, 'mysql_engine': 'InnoDB',
811 811 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
812 812 )
813 813 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
814 814 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
815 815 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
816 816 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
817 817
818 818 user = relationship('User')
819 819 repository = relationship('Repository')
820 820 permission = relationship('Permission')
821 821
822 822 def __unicode__(self):
823 823 return u'<%s => %s >' % (self.user, self.repository)
824 824
825 825
826 826 class UserUserGroupToPerm(Base, BaseModel):
827 827 __tablename__ = 'user_user_group_to_perm'
828 828 __table_args__ = (
829 829 UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
830 830 {'extend_existing': True, 'mysql_engine': 'InnoDB',
831 831 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
832 832 )
833 833 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
834 834 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
835 835 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
836 836 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
837 837
838 838 user = relationship('User')
839 839 user_group = relationship('UserGroup')
840 840 permission = relationship('Permission')
841 841
842 842 def __unicode__(self):
843 843 return u'<%s => %s >' % (self.user, self.user_group)
844 844
845 845
846 846 class UserToPerm(Base, BaseModel):
847 847 __tablename__ = 'user_to_perm'
848 848 __table_args__ = (
849 849 UniqueConstraint('user_id', 'permission_id'),
850 850 {'extend_existing': True, 'mysql_engine': 'InnoDB',
851 851 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
852 852 )
853 853 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
854 854 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
855 855 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
856 856
857 857 user = relationship('User')
858 858 permission = relationship('Permission', lazy='joined')
859 859
860 860 def __unicode__(self):
861 861 return u'<%s => %s >' % (self.user, self.permission)
862 862
863 863
864 864 class UserGroupRepoToPerm(Base, BaseModel):
865 865 __tablename__ = 'users_group_repo_to_perm'
866 866 __table_args__ = (
867 867 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
868 868 {'extend_existing': True, 'mysql_engine': 'InnoDB',
869 869 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
870 870 )
871 871 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
872 872 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
873 873 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
874 874 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
875 875
876 876 users_group = relationship('UserGroup')
877 877 permission = relationship('Permission')
878 878 repository = relationship('Repository')
879 879
880 880 def __unicode__(self):
881 881 return u'<UserGroupRepoToPerm:%s => %s >' % (self.users_group, self.repository)
882 882
883 883
884 884 class UserGroupUserGroupToPerm(Base, BaseModel):
885 885 __tablename__ = 'user_group_user_group_to_perm'
886 886 __table_args__ = (
887 887 UniqueConstraint('target_user_group_id', 'user_group_id', 'permission_id'),
888 888 CheckConstraint('target_user_group_id != user_group_id'),
889 889 {'extend_existing': True, 'mysql_engine': 'InnoDB',
890 890 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
891 891 )
892 892 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)
893 893 target_user_group_id = Column("target_user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
894 894 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
895 895 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
896 896
897 897 target_user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
898 898 user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
899 899 permission = relationship('Permission')
900 900
901 901 def __unicode__(self):
902 902 return u'<UserGroupUserGroup:%s => %s >' % (self.target_user_group, self.user_group)
903 903
904 904
905 905 class UserGroupToPerm(Base, BaseModel):
906 906 __tablename__ = 'users_group_to_perm'
907 907 __table_args__ = (
908 908 UniqueConstraint('users_group_id', 'permission_id',),
909 909 {'extend_existing': True, 'mysql_engine': 'InnoDB',
910 910 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
911 911 )
912 912 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
913 913 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
914 914 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
915 915
916 916 users_group = relationship('UserGroup')
917 917 permission = relationship('Permission')
918 918
919 919
920 920 class UserRepoGroupToPerm(Base, BaseModel):
921 921 __tablename__ = 'user_repo_group_to_perm'
922 922 __table_args__ = (
923 923 UniqueConstraint('user_id', 'group_id', 'permission_id'),
924 924 {'extend_existing': True, 'mysql_engine': 'InnoDB',
925 925 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
926 926 )
927 927
928 928 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
929 929 user_id = Column("user_id", Integer(), ForeignKey('users.user_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 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
932 932
933 933 user = relationship('User')
934 934 group = relationship('RepoGroup')
935 935 permission = relationship('Permission')
936 936
937 937
938 938 class UserGroupRepoGroupToPerm(Base, BaseModel):
939 939 __tablename__ = 'users_group_repo_group_to_perm'
940 940 __table_args__ = (
941 941 UniqueConstraint('users_group_id', 'group_id'),
942 942 {'extend_existing': True, 'mysql_engine': 'InnoDB',
943 943 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
944 944 )
945 945
946 946 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)
947 947 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
948 948 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
949 949 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
950 950
951 951 users_group = relationship('UserGroup')
952 952 permission = relationship('Permission')
953 953 group = relationship('RepoGroup')
954 954
955 955
956 956 class Statistics(Base, BaseModel):
957 957 __tablename__ = 'statistics'
958 958 __table_args__ = (
959 959 UniqueConstraint('repository_id'),
960 960 {'extend_existing': True, 'mysql_engine': 'InnoDB',
961 961 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
962 962 )
963 963 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
964 964 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
965 965 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
966 966 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
967 967 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
968 968 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
969 969
970 970 repository = relationship('Repository', single_parent=True)
971 971
972 972
973 973 class UserFollowing(Base, BaseModel):
974 974 __tablename__ = 'user_followings'
975 975 __table_args__ = (
976 976 UniqueConstraint('user_id', 'follows_repository_id'),
977 977 UniqueConstraint('user_id', 'follows_user_id'),
978 978 {'extend_existing': True, 'mysql_engine': 'InnoDB',
979 979 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
980 980 )
981 981
982 982 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
983 983 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
984 984 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
985 985 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
986 986 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
987 987
988 988 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
989 989
990 990 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
991 991 follows_repository = relationship('Repository', order_by='Repository.repo_name')
992 992
993 993
994 994 class CacheInvalidation(Base, BaseModel):
995 995 __tablename__ = 'cache_invalidation'
996 996 __table_args__ = (
997 997 UniqueConstraint('cache_key'),
998 998 Index('key_idx', 'cache_key'),
999 999 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1000 1000 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1001 1001 )
1002 1002 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1003 1003 cache_key = Column("cache_key", String(255), nullable=True, unique=None, default=None)
1004 1004 cache_args = Column("cache_args", String(255), nullable=True, unique=None, default=None)
1005 1005 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1006 1006
1007 1007 def __init__(self, cache_key, cache_args=''):
1008 1008 self.cache_key = cache_key
1009 1009 self.cache_args = cache_args
1010 1010 self.cache_active = False
1011 1011
1012 1012
1013 1013 class ChangesetComment(Base, BaseModel):
1014 1014 __tablename__ = 'changeset_comments'
1015 1015 __table_args__ = (
1016 1016 Index('cc_revision_idx', 'revision'),
1017 1017 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1018 1018 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1019 1019 )
1020 1020 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1021 1021 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1022 1022 revision = Column('revision', String(40), nullable=True)
1023 1023 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1024 1024 line_no = Column('line_no', Unicode(10), nullable=True)
1025 1025 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1026 1026 f_path = Column('f_path', Unicode(1000), nullable=True)
1027 1027 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1028 1028 text = Column('text', UnicodeText().with_variant(UnicodeText(25000), 'mysql'), nullable=False)
1029 1029 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1030 1030 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1031 1031
1032 1032 author = relationship('User', lazy='joined')
1033 1033 repo = relationship('Repository')
1034 1034 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1035 1035 pull_request = relationship('PullRequest', lazy='joined')
1036 1036
1037 1037
1038 1038 class ChangesetStatus(Base, BaseModel):
1039 1039 __tablename__ = 'changeset_statuses'
1040 1040 __table_args__ = (
1041 1041 Index('cs_revision_idx', 'revision'),
1042 1042 Index('cs_version_idx', 'version'),
1043 1043 UniqueConstraint('repo_id', 'revision', 'version'),
1044 1044 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1045 1045 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1046 1046 )
1047 1047 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1048 1048 STATUS_APPROVED = 'approved'
1049 1049 STATUS_REJECTED = 'rejected'
1050 1050 STATUS_UNDER_REVIEW = 'under_review'
1051 1051
1052 1052 STATUSES = [
1053 1053 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1054 1054 (STATUS_APPROVED, _("Approved")),
1055 1055 (STATUS_REJECTED, _("Rejected")),
1056 1056 (STATUS_UNDER_REVIEW, _("Under Review")),
1057 1057 ]
1058 1058
1059 1059 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1060 1060 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1061 1061 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1062 1062 revision = Column('revision', String(40), nullable=False)
1063 1063 status = Column('status', String(128), nullable=False, default=DEFAULT)
1064 1064 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1065 1065 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1066 1066 version = Column('version', Integer(), nullable=False, default=0)
1067 1067 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1068 1068
1069 1069 author = relationship('User', lazy='joined')
1070 1070 repo = relationship('Repository')
1071 1071 comment = relationship('ChangesetComment', lazy='joined')
1072 1072 pull_request = relationship('PullRequest', lazy='joined')
1073 1073
1074 1074
1075 1075
1076 1076 class PullRequest(Base, BaseModel):
1077 1077 __tablename__ = 'pull_requests'
1078 1078 __table_args__ = (
1079 1079 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1080 1080 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1081 1081 )
1082 1082
1083 1083 STATUS_NEW = u'new'
1084 1084 STATUS_OPEN = u'open'
1085 1085 STATUS_CLOSED = u'closed'
1086 1086
1087 1087 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1088 1088 title = Column('title', Unicode(256), nullable=True)
1089 1089 description = Column('description', UnicodeText().with_variant(UnicodeText(10240), 'mysql'), nullable=True)
1090 1090 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1091 1091 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1092 1092 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1093 1093 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1094 1094 _revisions = Column('revisions', UnicodeText().with_variant(UnicodeText(20500), 'mysql'))
1095 1095 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1096 1096 org_ref = Column('org_ref', Unicode(256), nullable=False)
1097 1097 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1098 1098 other_ref = Column('other_ref', Unicode(256), nullable=False)
1099 1099
1100 1100 author = relationship('User', lazy='joined')
1101 1101 reviewers = relationship('PullRequestReviewers',
1102 1102 cascade="all, delete, delete-orphan")
1103 1103 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1104 1104 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1105 1105 statuses = relationship('ChangesetStatus')
1106 1106 comments = relationship('ChangesetComment',
1107 1107 cascade="all, delete, delete-orphan")
1108 1108
1109 1109
1110 1110 class PullRequestReviewers(Base, BaseModel):
1111 1111 __tablename__ = 'pull_request_reviewers'
1112 1112 __table_args__ = (
1113 1113 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1114 1114 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1115 1115 )
1116 1116
1117 1117 def __init__(self, user=None, pull_request=None):
1118 1118 self.user = user
1119 1119 self.pull_request = pull_request
1120 1120
1121 1121 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1122 1122 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1123 1123 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1124 1124
1125 1125 user = relationship('User')
1126 1126 pull_request = relationship('PullRequest')
1127 1127
1128 1128
1129 1129 class Notification(Base, BaseModel):
1130 1130 __tablename__ = 'notifications'
1131 1131 __table_args__ = (
1132 1132 Index('notification_type_idx', 'type'),
1133 1133 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1134 1134 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1135 1135 )
1136 1136
1137 1137 TYPE_CHANGESET_COMMENT = u'cs_comment'
1138 1138 TYPE_MESSAGE = u'message'
1139 1139 TYPE_MENTION = u'mention'
1140 1140 TYPE_REGISTRATION = u'registration'
1141 1141 TYPE_PULL_REQUEST = u'pull_request'
1142 1142 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1143 1143
1144 1144 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1145 1145 subject = Column('subject', Unicode(512), nullable=True)
1146 1146 body = Column('body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True)
1147 1147 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1148 1148 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1149 1149 type_ = Column('type', Unicode(256))
1150 1150
1151 1151 created_by_user = relationship('User')
1152 1152 notifications_to_users = relationship('UserNotification', lazy='joined',
1153 1153 cascade="all, delete, delete-orphan")
1154 1154
1155 1155
1156 1156 class UserNotification(Base, BaseModel):
1157 1157 __tablename__ = 'user_to_notification'
1158 1158 __table_args__ = (
1159 1159 UniqueConstraint('user_id', 'notification_id'),
1160 1160 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1161 1161 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1162 1162 )
1163 1163 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1164 1164 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1165 1165 read = Column('read', Boolean, default=False)
1166 1166 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1167 1167
1168 1168 user = relationship('User', lazy="joined")
1169 1169 notification = relationship('Notification', lazy="joined",
1170 1170 order_by=lambda: Notification.created_on.desc(),)
1171 1171
1172 1172
1173 1173 class Gist(Base, BaseModel):
1174 1174 __tablename__ = 'gists'
1175 1175 __table_args__ = (
1176 1176 Index('g_gist_access_id_idx', 'gist_access_id'),
1177 1177 Index('g_created_on_idx', 'created_on'),
1178 1178 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1179 1179 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1180 1180 )
1181 1181 GIST_PUBLIC = u'public'
1182 1182 GIST_PRIVATE = u'private'
1183 1183
1184 1184 gist_id = Column('gist_id', Integer(), primary_key=True)
1185 1185 gist_access_id = Column('gist_access_id', Unicode(250))
1186 1186 gist_description = Column('gist_description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
1187 1187 gist_owner = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=True)
1188 1188 gist_expires = Column('gist_expires', Float(53), nullable=False)
1189 1189 gist_type = Column('gist_type', Unicode(128), nullable=False)
1190 1190 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1191 1191 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1192 1192
1193 1193 owner = relationship('User')
1194 1194
1195 1195
1196 1196 class DbMigrateVersion(Base, BaseModel):
1197 1197 __tablename__ = 'db_migrate_version'
1198 1198 __table_args__ = (
1199 1199 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1200 1200 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1201 1201 )
1202 1202 repository_id = Column('repository_id', String(250), primary_key=True)
1203 1203 repository_path = Column('repository_path', Text)
1204 1204 version = Column('version', Integer)
@@ -1,1224 +1,1224 b''
1 1
2 2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 3 #
4 4 # This program is free software: you can redistribute it and/or modify
5 5 # it under the terms of the GNU Affero General Public License, version 3
6 6 # (only), as published by the Free Software Foundation.
7 7 #
8 8 # This program is distributed in the hope that it will be useful,
9 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 11 # GNU General Public License for more details.
12 12 #
13 13 # You should have received a copy of the GNU Affero General Public License
14 14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 15 #
16 16 # This program is dual-licensed. If you wish to learn more about the
17 17 # RhodeCode Enterprise Edition, including its added features, Support services,
18 18 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 19
20 20 import os
21 21 import time
22 22 import logging
23 23 import datetime
24 24 import traceback
25 25 import hashlib
26 26 import collections
27 27 import functools
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 remove_prefix, time_to_datetime, aslist, Optional, safe_int, \
46 46 get_clone_url
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.items():
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 __tablename__ = 'rhodecode_settings'
154 154 __table_args__ = (
155 155 UniqueConstraint('app_settings_name'),
156 156 {'extend_existing': True, 'mysql_engine': 'InnoDB',
157 157 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
158 158 )
159 159
160 160 SETTINGS_TYPES = {
161 161 'str': safe_str,
162 162 'int': safe_int,
163 163 'unicode': safe_str,
164 164 'bool': str2bool,
165 165 'list': functools.partial(aslist, sep=',')
166 166 }
167 167 DEFAULT_UPDATE_URL = 'https://rhodecode.com/api/v1/info/versions'
168 168
169 169 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
170 170 app_settings_name = Column("app_settings_name", String(255), nullable=True, unique=None, default=None)
171 171 _app_settings_value = Column("app_settings_value", String(4096), nullable=True, unique=None, default=None)
172 172 _app_settings_type = Column("app_settings_type", String(255), nullable=True, unique=None, default=None)
173 173
174 174 def __init__(self, key='', val='', type='unicode'):
175 175 self.app_settings_name = key
176 176 self.app_settings_value = val
177 177 self.app_settings_type = type
178 178
179 179 @validates('_app_settings_value')
180 180 def validate_settings_value(self, key, val):
181 181 assert type(val) == str
182 182 return val
183 183
184 184 @hybrid_property
185 185 def app_settings_value(self):
186 186 v = self._app_settings_value
187 187 _type = self.app_settings_type
188 188 converter = self.SETTINGS_TYPES.get(_type) or self.SETTINGS_TYPES['unicode']
189 189 return converter(v)
190 190
191 191 @app_settings_value.setter
192 192 def app_settings_value(self, val):
193 193 """
194 194 Setter that will always make sure we use unicode in app_settings_value
195 195
196 196 :param val:
197 197 """
198 198 self._app_settings_value = safe_str(val)
199 199
200 200 @hybrid_property
201 201 def app_settings_type(self):
202 202 return self._app_settings_type
203 203
204 204 @app_settings_type.setter
205 205 def app_settings_type(self, val):
206 206 if val not in self.SETTINGS_TYPES:
207 207 raise Exception('type must be one of %s got %s'
208 208 % (self.SETTINGS_TYPES.keys(), val))
209 209 self._app_settings_type = val
210 210
211 211 def __unicode__(self):
212 212 return u"<%s('%s:%s[%s]')>" % (
213 213 self.__class__.__name__,
214 214 self.app_settings_name, self.app_settings_value, self.app_settings_type
215 215 )
216 216
217 217
218 218 class RhodeCodeUi(Base, BaseModel):
219 219 __tablename__ = 'rhodecode_ui'
220 220 __table_args__ = (
221 221 UniqueConstraint('ui_key'),
222 222 {'extend_existing': True, 'mysql_engine': 'InnoDB',
223 223 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
224 224 )
225 225
226 226 HOOK_REPO_SIZE = 'changegroup.repo_size'
227 227 HOOK_PUSH = 'changegroup.push_logger'
228 228 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
229 229 HOOK_PULL = 'outgoing.pull_logger'
230 230 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
231 231
232 232 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
233 233 ui_section = Column("ui_section", String(255), nullable=True, unique=None, default=None)
234 234 ui_key = Column("ui_key", String(255), nullable=True, unique=None, default=None)
235 235 ui_value = Column("ui_value", String(255), nullable=True, unique=None, default=None)
236 236 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
237 237
238 238 def __repr__(self):
239 239 return '<%s[%s]%s=>%s]>' % (self.__class__.__name__, self.ui_section,
240 240 self.ui_key, self.ui_value)
241 241
242 242
243 243 class User(Base, BaseModel):
244 244 __tablename__ = 'users'
245 245 __table_args__ = (
246 246 UniqueConstraint('username'), UniqueConstraint('email'),
247 247 Index('u_username_idx', 'username'),
248 248 Index('u_email_idx', 'email'),
249 249 {'extend_existing': True, 'mysql_engine': 'InnoDB',
250 250 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
251 251 )
252 252 DEFAULT_USER = 'default'
253 253 DEFAULT_GRAVATAR_URL = 'https://secure.gravatar.com/avatar/{md5email}?d=identicon&s={size}'
254 254
255 255 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
256 256 username = Column("username", String(255), nullable=True, unique=None, default=None)
257 257 password = Column("password", String(255), nullable=True, unique=None, default=None)
258 258 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
259 259 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
260 260 name = Column("firstname", String(255), nullable=True, unique=None, default=None)
261 261 lastname = Column("lastname", String(255), nullable=True, unique=None, default=None)
262 262 _email = Column("email", String(255), nullable=True, unique=None, default=None)
263 263 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
264 264 extern_type = Column("extern_type", String(255), nullable=True, unique=None, default=None)
265 265 extern_name = Column("extern_name", String(255), nullable=True, unique=None, default=None)
266 266 api_key = Column("api_key", String(255), nullable=True, unique=None, default=None)
267 267 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
268 268 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
269 269 #_user_data = Column("user_data", LargeBinary(), nullable=True) # JSON data
270 270
271 271 user_log = relationship('UserLog')
272 272 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
273 273
274 274 repositories = relationship('Repository')
275 275 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
276 276 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
277 277
278 278 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
279 279 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
280 280
281 281 group_member = relationship('UserGroupMember', cascade='all')
282 282
283 283 notifications = relationship('UserNotification', cascade='all')
284 284 # notifications assigned to this user
285 285 user_created_notifications = relationship('Notification', cascade='all')
286 286 # comments created by this user
287 287 user_comments = relationship('ChangesetComment', cascade='all')
288 288 user_emails = relationship('UserEmailMap', cascade='all')
289 289 user_auth_tokens = relationship('UserApiKeys', cascade='all')
290 290
291 291 @hybrid_property
292 292 def email(self):
293 293 return self._email
294 294
295 295 @email.setter
296 296 def email(self, val):
297 297 self._email = val.lower() if val else None
298 298
299 299 @property
300 300 def firstname(self):
301 301 # alias for future
302 302 return self.name
303 303
304 304 @property
305 305 def username_and_name(self):
306 306 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
307 307
308 308 @property
309 309 def full_name(self):
310 310 return '%s %s' % (self.firstname, self.lastname)
311 311
312 312 @property
313 313 def full_contact(self):
314 314 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
315 315
316 316 @property
317 317 def short_contact(self):
318 318 return '%s %s' % (self.firstname, self.lastname)
319 319
320 320 @property
321 321 def is_admin(self):
322 322 return self.admin
323 323
324 324 @classmethod
325 325 def get_by_username(cls, username, case_insensitive=False, cache=False):
326 326 if case_insensitive:
327 327 q = cls.query().filter(cls.username.ilike(username))
328 328 else:
329 329 q = cls.query().filter(cls.username == username)
330 330
331 331 if cache:
332 332 q = q.options(FromCache(
333 333 "sql_cache_short",
334 334 "get_user_%s" % _hash_key(username)
335 335 )
336 336 )
337 337 return q.scalar()
338 338
339 339 @classmethod
340 340 def get_by_auth_token(cls, auth_token, cache=False, fallback=True):
341 341 q = cls.query().filter(cls.api_key == auth_token)
342 342
343 343 if cache:
344 344 q = q.options(FromCache("sql_cache_short",
345 345 "get_auth_token_%s" % auth_token))
346 346 res = q.scalar()
347 347
348 348 if fallback and not res:
349 349 #fallback to additional keys
350 350 _res = UserApiKeys.query()\
351 351 .filter(UserApiKeys.api_key == auth_token)\
352 352 .filter(or_(UserApiKeys.expires == -1,
353 353 UserApiKeys.expires >= time.time()))\
354 354 .first()
355 355 if _res:
356 356 res = _res.user
357 357 return res
358 358
359 359 @classmethod
360 360 def get_by_email(cls, email, case_insensitive=False, cache=False):
361 361
362 362 if case_insensitive:
363 363 q = cls.query().filter(cls.email.ilike(email))
364 364 else:
365 365 q = cls.query().filter(cls.email == email)
366 366
367 367 if cache:
368 368 q = q.options(FromCache("sql_cache_short",
369 369 "get_email_key_%s" % email))
370 370
371 371 ret = q.scalar()
372 372 if ret is None:
373 373 q = UserEmailMap.query()
374 374 # try fetching in alternate email map
375 375 if case_insensitive:
376 376 q = q.filter(UserEmailMap.email.ilike(email))
377 377 else:
378 378 q = q.filter(UserEmailMap.email == email)
379 379 q = q.options(joinedload(UserEmailMap.user))
380 380 if cache:
381 381 q = q.options(FromCache("sql_cache_short",
382 382 "get_email_map_key_%s" % email))
383 383 ret = getattr(q.scalar(), 'user', None)
384 384
385 385 return ret
386 386
387 387 @classmethod
388 388 def get_first_admin(cls):
389 389 user = User.query().filter(User.admin == True).first()
390 390 if user is None:
391 391 raise Exception('Missing administrative account!')
392 392 return user
393 393
394 394 @classmethod
395 395 def get_default_user(cls, cache=False):
396 396 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
397 397 if user is None:
398 398 raise Exception('Missing default account!')
399 399 return user
400 400
401 401
402 402 class UserApiKeys(Base, BaseModel):
403 403 __tablename__ = 'user_api_keys'
404 404 __table_args__ = (
405 405 Index('uak_api_key_idx', 'api_key'),
406 406 Index('uak_api_key_expires_idx', 'api_key', 'expires'),
407 407 UniqueConstraint('api_key'),
408 408 {'extend_existing': True, 'mysql_engine': 'InnoDB',
409 409 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
410 410 )
411 411
412 412
413 413 user_api_key_id = Column("user_api_key_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
414 414 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
415 415 api_key = Column("api_key", String(255), nullable=False, unique=True)
416 416 description = Column('description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
417 417 expires = Column('expires', Float(53), nullable=False)
418 418 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
419 419
420 420 user = relationship('User', lazy='joined')
421 421
422 422
423 423 class UserEmailMap(Base, BaseModel):
424 424 __tablename__ = 'user_email_map'
425 425 __table_args__ = (
426 426 Index('uem_email_idx', 'email'),
427 427 UniqueConstraint('email'),
428 428 {'extend_existing': True, 'mysql_engine': 'InnoDB',
429 429 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
430 430 )
431 431
432 432
433 433 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
434 434 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
435 435 _email = Column("email", String(255), nullable=True, unique=False, default=None)
436 436 user = relationship('User', lazy='joined')
437 437
438 438 @validates('_email')
439 439 def validate_email(self, key, email):
440 440 # check if this email is not main one
441 441 main_email = Session().query(User).filter(User.email == email).scalar()
442 442 if main_email is not None:
443 443 raise AttributeError('email %s is present is user table' % email)
444 444 return email
445 445
446 446 @hybrid_property
447 447 def email(self):
448 448 return self._email
449 449
450 450 @email.setter
451 451 def email(self, val):
452 452 self._email = val.lower() if val else None
453 453
454 454
455 455 class UserIpMap(Base, BaseModel):
456 456 __tablename__ = 'user_ip_map'
457 457 __table_args__ = (
458 458 UniqueConstraint('user_id', 'ip_addr'),
459 459 {'extend_existing': True, 'mysql_engine': 'InnoDB',
460 460 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
461 461 )
462 462
463 463
464 464 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
465 465 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
466 466 ip_addr = Column("ip_addr", String(255), nullable=True, unique=False, default=None)
467 467 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
468 468 user = relationship('User', lazy='joined')
469 469
470 470 def __unicode__(self):
471 471 return u"<%s('user_id:%s=>%s')>" % (self.__class__.__name__,
472 472 self.user_id, self.ip_addr)
473 473
474 474
475 475 class UserLog(Base, BaseModel):
476 476 __tablename__ = 'user_logs'
477 477 __table_args__ = (
478 478 {'extend_existing': True, 'mysql_engine': 'InnoDB',
479 479 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
480 480 )
481 481 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
482 482 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
483 483 username = Column("username", String(255), nullable=True, unique=None, default=None)
484 484 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
485 485 repository_name = Column("repository_name", String(255), nullable=True, unique=None, default=None)
486 486 user_ip = Column("user_ip", String(255), nullable=True, unique=None, default=None)
487 487 action = Column("action", String(1200000), nullable=True, unique=None, default=None)
488 488 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
489 489
490 490 def __unicode__(self):
491 491 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
492 492 self.repository_name,
493 493 self.action)
494 494
495 495 user = relationship('User')
496 496 repository = relationship('Repository', cascade='')
497 497
498 498
499 499 class UserGroup(Base, BaseModel):
500 500 __tablename__ = 'users_groups'
501 501 __table_args__ = (
502 502 {'extend_existing': True, 'mysql_engine': 'InnoDB',
503 503 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
504 504 )
505 505
506 506 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
507 507 users_group_name = Column("users_group_name", String(255), nullable=False, unique=True, default=None)
508 508 user_group_description = Column("user_group_description", String(10000), nullable=True, unique=None, default=None)
509 509 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
510 510 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
511 511 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
512 512 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
513 513
514 514 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
515 515 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
516 516 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
517 517 users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
518 518 user_user_group_to_perm = relationship('UserUserGroupToPerm', cascade='all')
519 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm ', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
519 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
520 520
521 521 user = relationship('User')
522 522
523 523 def __unicode__(self):
524 524 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
525 525 self.users_group_id,
526 526 self.users_group_name)
527 527
528 528 @classmethod
529 529 def get_by_group_name(cls, group_name, cache=False,
530 530 case_insensitive=False):
531 531 if case_insensitive:
532 532 q = cls.query().filter(cls.users_group_name.ilike(group_name))
533 533 else:
534 534 q = cls.query().filter(cls.users_group_name == group_name)
535 535 if cache:
536 536 q = q.options(FromCache(
537 537 "sql_cache_short",
538 538 "get_user_%s" % _hash_key(group_name)
539 539 )
540 540 )
541 541 return q.scalar()
542 542
543 543 @classmethod
544 544 def get(cls, user_group_id, cache=False):
545 545 user_group = cls.query()
546 546 if cache:
547 547 user_group = user_group.options(FromCache("sql_cache_short",
548 548 "get_users_group_%s" % user_group_id))
549 549 return user_group.get(user_group_id)
550 550
551 551
552 552 class UserGroupMember(Base, BaseModel):
553 553 __tablename__ = 'users_groups_members'
554 554 __table_args__ = (
555 555 {'extend_existing': True, 'mysql_engine': 'InnoDB',
556 556 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
557 557 )
558 558
559 559 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
560 560 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
561 561 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
562 562
563 563 user = relationship('User', lazy='joined')
564 564 users_group = relationship('UserGroup')
565 565
566 566 def __init__(self, gr_id='', u_id=''):
567 567 self.users_group_id = gr_id
568 568 self.user_id = u_id
569 569
570 570
571 571 class RepositoryField(Base, BaseModel):
572 572 __tablename__ = 'repositories_fields'
573 573 __table_args__ = (
574 574 UniqueConstraint('repository_id', 'field_key'), # no-multi field
575 575 {'extend_existing': True, 'mysql_engine': 'InnoDB',
576 576 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
577 577 )
578 578 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
579 579
580 580 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
581 581 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
582 582 field_key = Column("field_key", String(250))
583 583 field_label = Column("field_label", String(1024), nullable=False)
584 584 field_value = Column("field_value", String(10000), nullable=False)
585 585 field_desc = Column("field_desc", String(1024), nullable=False)
586 586 field_type = Column("field_type", String(256), nullable=False, unique=None)
587 587 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
588 588
589 589 repository = relationship('Repository')
590 590
591 591 @classmethod
592 592 def get_by_key_name(cls, key, repo):
593 593 row = cls.query()\
594 594 .filter(cls.repository == repo)\
595 595 .filter(cls.field_key == key).scalar()
596 596 return row
597 597
598 598
599 599 class Repository(Base, BaseModel):
600 600 __tablename__ = 'repositories'
601 601 __table_args__ = (
602 602 UniqueConstraint('repo_name'),
603 603 Index('r_repo_name_idx', 'repo_name'),
604 604 {'extend_existing': True, 'mysql_engine': 'InnoDB',
605 605 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
606 606 )
607 607 DEFAULT_CLONE_URI = '{scheme}://{user}@{netloc}/{repo}'
608 608 DEFAULT_CLONE_URI_ID = '{scheme}://{user}@{netloc}/_{repoid}'
609 609
610 610 STATE_CREATED = 'repo_state_created'
611 611 STATE_PENDING = 'repo_state_pending'
612 612 STATE_ERROR = 'repo_state_error'
613 613
614 614 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
615 615 repo_name = Column("repo_name", String(255), nullable=False, unique=True, default=None)
616 616 clone_uri = Column("clone_uri", String(255), nullable=True, unique=False, default=None)
617 617 repo_type = Column("repo_type", String(255), nullable=False, unique=False, default=None)
618 618 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
619 619 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
620 620 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
621 621 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
622 622 description = Column("description", String(10000), nullable=True, unique=None, default=None)
623 623 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
624 624 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
625 625 _landing_revision = Column("landing_revision", String(255), nullable=False, unique=False, default=None)
626 626 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
627 627 _locked = Column("locked", String(255), nullable=True, unique=False, default=None)
628 628 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
629 629
630 630 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
631 631 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
632 632
633 633 user = relationship('User')
634 634 fork = relationship('Repository', remote_side=repo_id)
635 635 group = relationship('RepoGroup')
636 636 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
637 637 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
638 638 stats = relationship('Statistics', cascade='all', uselist=False)
639 639
640 640 followers = relationship('UserFollowing',
641 641 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
642 642 cascade='all')
643 643 extra_fields = relationship('RepositoryField',
644 644 cascade="all, delete, delete-orphan")
645 645
646 646 logs = relationship('UserLog')
647 647 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
648 648
649 649 pull_requests_org = relationship('PullRequest',
650 650 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
651 651 cascade="all, delete, delete-orphan")
652 652
653 653 pull_requests_other = relationship('PullRequest',
654 654 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
655 655 cascade="all, delete, delete-orphan")
656 656
657 657 def __unicode__(self):
658 658 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
659 659 safe_str(self.repo_name))
660 660
661 661 @classmethod
662 662 def get_by_repo_name(cls, repo_name):
663 663 q = Session().query(cls).filter(cls.repo_name == repo_name)
664 664 q = q.options(joinedload(Repository.fork))\
665 665 .options(joinedload(Repository.user))\
666 666 .options(joinedload(Repository.group))
667 667 return q.scalar()
668 668
669 669
670 670 class RepoGroup(Base, BaseModel):
671 671 __tablename__ = 'groups'
672 672 __table_args__ = (
673 673 UniqueConstraint('group_name', 'group_parent_id'),
674 674 {'extend_existing': True, 'mysql_engine': 'InnoDB',
675 675 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
676 676 )
677 677
678 678
679 679 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
680 680 group_name = Column("group_name", String(255), nullable=False, unique=True, default=None)
681 681 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
682 682 group_description = Column("group_description", String(10000), nullable=True, unique=None, default=None)
683 683 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
684 684 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
685 685 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
686 686
687 687 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
688 688 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
689 689 parent_group = relationship('RepoGroup', remote_side=group_id)
690 690 user = relationship('User')
691 691
692 692 def __init__(self, group_name='', parent_group=None):
693 693 self.group_name = group_name
694 694 self.parent_group = parent_group
695 695
696 696 def __unicode__(self):
697 697 return u"<%s('id:%s:%s')>" % (self.__class__.__name__, self.group_id,
698 698 self.group_name)
699 699
700 700 @classmethod
701 701 def url_sep(cls):
702 702 return URL_SEP
703 703
704 704 @classmethod
705 705 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
706 706 if case_insensitive:
707 707 gr = cls.query()\
708 708 .filter(cls.group_name.ilike(group_name))
709 709 else:
710 710 gr = cls.query()\
711 711 .filter(cls.group_name == group_name)
712 712 if cache:
713 713 gr = gr.options(FromCache(
714 714 "sql_cache_short",
715 715 "get_group_%s" % _hash_key(group_name)
716 716 )
717 717 )
718 718 return gr.scalar()
719 719
720 720
721 721 class Permission(Base, BaseModel):
722 722 __tablename__ = 'permissions'
723 723 __table_args__ = (
724 724 Index('p_perm_name_idx', 'permission_name'),
725 725 {'extend_existing': True, 'mysql_engine': 'InnoDB',
726 726 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
727 727 )
728 728 PERMS = [
729 729 ('hg.admin', _('RhodeCode Administrator')),
730 730
731 731 ('repository.none', _('Repository no access')),
732 732 ('repository.read', _('Repository read access')),
733 733 ('repository.write', _('Repository write access')),
734 734 ('repository.admin', _('Repository admin access')),
735 735
736 736 ('group.none', _('Repository group no access')),
737 737 ('group.read', _('Repository group read access')),
738 738 ('group.write', _('Repository group write access')),
739 739 ('group.admin', _('Repository group admin access')),
740 740
741 741 ('usergroup.none', _('User group no access')),
742 742 ('usergroup.read', _('User group read access')),
743 743 ('usergroup.write', _('User group write access')),
744 744 ('usergroup.admin', _('User group admin access')),
745 745
746 746 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
747 747 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
748 748
749 749 ('hg.usergroup.create.false', _('User Group creation disabled')),
750 750 ('hg.usergroup.create.true', _('User Group creation enabled')),
751 751
752 752 ('hg.create.none', _('Repository creation disabled')),
753 753 ('hg.create.repository', _('Repository creation enabled')),
754 754 ('hg.create.write_on_repogroup.true', _('Repository creation enabled with write permission to a repository group')),
755 755 ('hg.create.write_on_repogroup.false', _('Repository creation disabled with write permission to a repository group')),
756 756
757 757 ('hg.fork.none', _('Repository forking disabled')),
758 758 ('hg.fork.repository', _('Repository forking enabled')),
759 759
760 760 ('hg.register.none', _('Registration disabled')),
761 761 ('hg.register.manual_activate', _('User Registration with manual account activation')),
762 762 ('hg.register.auto_activate', _('User Registration with automatic account activation')),
763 763
764 764 ('hg.extern_activate.manual', _('Manual activation of external account')),
765 765 ('hg.extern_activate.auto', _('Automatic activation of external account')),
766 766
767 767 ]
768 768
769 769 #definition of system default permissions for DEFAULT user
770 770 DEFAULT_USER_PERMISSIONS = [
771 771 'repository.read',
772 772 'group.read',
773 773 'usergroup.read',
774 774 'hg.create.repository',
775 775 'hg.create.write_on_repogroup.true',
776 776 'hg.fork.repository',
777 777 'hg.register.manual_activate',
778 778 'hg.extern_activate.auto',
779 779 ]
780 780
781 781 # defines which permissions are more important higher the more important
782 782 # Weight defines which permissions are more important.
783 783 # The higher number the more important.
784 784 PERM_WEIGHTS = {
785 785 'repository.none': 0,
786 786 'repository.read': 1,
787 787 'repository.write': 3,
788 788 'repository.admin': 4,
789 789
790 790 'group.none': 0,
791 791 'group.read': 1,
792 792 'group.write': 3,
793 793 'group.admin': 4,
794 794
795 795 'usergroup.none': 0,
796 796 'usergroup.read': 1,
797 797 'usergroup.write': 3,
798 798 'usergroup.admin': 4,
799 799 'hg.repogroup.create.false': 0,
800 800 'hg.repogroup.create.true': 1,
801 801
802 802 'hg.usergroup.create.false': 0,
803 803 'hg.usergroup.create.true': 1,
804 804
805 805 'hg.fork.none': 0,
806 806 'hg.fork.repository': 1,
807 807 'hg.create.none': 0,
808 808 'hg.create.repository': 1
809 809 }
810 810
811 811 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
812 812 permission_name = Column("permission_name", String(255), nullable=True, unique=None, default=None)
813 813 permission_longname = Column("permission_longname", String(255), nullable=True, unique=None, default=None)
814 814
815 815 def __unicode__(self):
816 816 return u"<%s('%s:%s')>" % (
817 817 self.__class__.__name__, self.permission_id, self.permission_name
818 818 )
819 819
820 820 @classmethod
821 821 def get_by_key(cls, key):
822 822 return cls.query().filter(cls.permission_name == key).scalar()
823 823
824 824
825 825 class UserRepoToPerm(Base, BaseModel):
826 826 __tablename__ = 'repo_to_perm'
827 827 __table_args__ = (
828 828 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
829 829 {'extend_existing': True, 'mysql_engine': 'InnoDB',
830 830 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
831 831 )
832 832 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
833 833 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
834 834 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
835 835 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
836 836
837 837 user = relationship('User')
838 838 repository = relationship('Repository')
839 839 permission = relationship('Permission')
840 840
841 841 def __unicode__(self):
842 842 return u'<%s => %s >' % (self.user, self.repository)
843 843
844 844
845 845 class UserUserGroupToPerm(Base, BaseModel):
846 846 __tablename__ = 'user_user_group_to_perm'
847 847 __table_args__ = (
848 848 UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
849 849 {'extend_existing': True, 'mysql_engine': 'InnoDB',
850 850 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
851 851 )
852 852 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
853 853 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
854 854 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
855 855 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
856 856
857 857 user = relationship('User')
858 858 user_group = relationship('UserGroup')
859 859 permission = relationship('Permission')
860 860
861 861 def __unicode__(self):
862 862 return u'<%s => %s >' % (self.user, self.user_group)
863 863
864 864
865 865 class UserToPerm(Base, BaseModel):
866 866 __tablename__ = 'user_to_perm'
867 867 __table_args__ = (
868 868 UniqueConstraint('user_id', 'permission_id'),
869 869 {'extend_existing': True, 'mysql_engine': 'InnoDB',
870 870 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
871 871 )
872 872 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
873 873 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
874 874 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
875 875
876 876 user = relationship('User')
877 877 permission = relationship('Permission', lazy='joined')
878 878
879 879 def __unicode__(self):
880 880 return u'<%s => %s >' % (self.user, self.permission)
881 881
882 882
883 883 class UserGroupRepoToPerm(Base, BaseModel):
884 884 __tablename__ = 'users_group_repo_to_perm'
885 885 __table_args__ = (
886 886 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
887 887 {'extend_existing': True, 'mysql_engine': 'InnoDB',
888 888 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
889 889 )
890 890 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
891 891 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
892 892 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
893 893 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
894 894
895 895 users_group = relationship('UserGroup')
896 896 permission = relationship('Permission')
897 897 repository = relationship('Repository')
898 898
899 899 def __unicode__(self):
900 900 return u'<UserGroupRepoToPerm:%s => %s >' % (self.users_group, self.repository)
901 901
902 902
903 903 class UserGroupUserGroupToPerm(Base, BaseModel):
904 904 __tablename__ = 'user_group_user_group_to_perm'
905 905 __table_args__ = (
906 906 UniqueConstraint('target_user_group_id', 'user_group_id', 'permission_id'),
907 907 CheckConstraint('target_user_group_id != user_group_id'),
908 908 {'extend_existing': True, 'mysql_engine': 'InnoDB',
909 909 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
910 910 )
911 911 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)
912 912 target_user_group_id = Column("target_user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
913 913 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
914 914 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
915 915
916 916 target_user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
917 917 user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
918 918 permission = relationship('Permission')
919 919
920 920 def __unicode__(self):
921 921 return u'<UserGroupUserGroup:%s => %s >' % (self.target_user_group, self.user_group)
922 922
923 923
924 924 class UserGroupToPerm(Base, BaseModel):
925 925 __tablename__ = 'users_group_to_perm'
926 926 __table_args__ = (
927 927 UniqueConstraint('users_group_id', 'permission_id',),
928 928 {'extend_existing': True, 'mysql_engine': 'InnoDB',
929 929 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
930 930 )
931 931 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
932 932 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
933 933 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
934 934
935 935 users_group = relationship('UserGroup')
936 936 permission = relationship('Permission')
937 937
938 938
939 939 class UserRepoGroupToPerm(Base, BaseModel):
940 940 __tablename__ = 'user_repo_group_to_perm'
941 941 __table_args__ = (
942 942 UniqueConstraint('user_id', 'group_id', 'permission_id'),
943 943 {'extend_existing': True, 'mysql_engine': 'InnoDB',
944 944 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
945 945 )
946 946
947 947 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
948 948 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
949 949 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
950 950 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
951 951
952 952 user = relationship('User')
953 953 group = relationship('RepoGroup')
954 954 permission = relationship('Permission')
955 955
956 956
957 957 class UserGroupRepoGroupToPerm(Base, BaseModel):
958 958 __tablename__ = 'users_group_repo_group_to_perm'
959 959 __table_args__ = (
960 960 UniqueConstraint('users_group_id', 'group_id'),
961 961 {'extend_existing': True, 'mysql_engine': 'InnoDB',
962 962 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
963 963 )
964 964
965 965 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)
966 966 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
967 967 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
968 968 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
969 969
970 970 users_group = relationship('UserGroup')
971 971 permission = relationship('Permission')
972 972 group = relationship('RepoGroup')
973 973
974 974
975 975 class Statistics(Base, BaseModel):
976 976 __tablename__ = 'statistics'
977 977 __table_args__ = (
978 978 UniqueConstraint('repository_id'),
979 979 {'extend_existing': True, 'mysql_engine': 'InnoDB',
980 980 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
981 981 )
982 982 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
983 983 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
984 984 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
985 985 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
986 986 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
987 987 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
988 988
989 989 repository = relationship('Repository', single_parent=True)
990 990
991 991
992 992 class UserFollowing(Base, BaseModel):
993 993 __tablename__ = 'user_followings'
994 994 __table_args__ = (
995 995 UniqueConstraint('user_id', 'follows_repository_id'),
996 996 UniqueConstraint('user_id', 'follows_user_id'),
997 997 {'extend_existing': True, 'mysql_engine': 'InnoDB',
998 998 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
999 999 )
1000 1000
1001 1001 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1002 1002 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1003 1003 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1004 1004 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1005 1005 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1006 1006
1007 1007 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1008 1008
1009 1009 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1010 1010 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1011 1011
1012 1012
1013 1013 class CacheInvalidation(Base, BaseModel):
1014 1014 __tablename__ = 'cache_invalidation'
1015 1015 __table_args__ = (
1016 1016 UniqueConstraint('cache_key'),
1017 1017 Index('key_idx', 'cache_key'),
1018 1018 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1019 1019 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1020 1020 )
1021 1021 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1022 1022 cache_key = Column("cache_key", String(255), nullable=True, unique=None, default=None)
1023 1023 cache_args = Column("cache_args", String(255), nullable=True, unique=None, default=None)
1024 1024 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1025 1025
1026 1026 def __init__(self, cache_key, cache_args=''):
1027 1027 self.cache_key = cache_key
1028 1028 self.cache_args = cache_args
1029 1029 self.cache_active = False
1030 1030
1031 1031
1032 1032 class ChangesetComment(Base, BaseModel):
1033 1033 __tablename__ = 'changeset_comments'
1034 1034 __table_args__ = (
1035 1035 Index('cc_revision_idx', 'revision'),
1036 1036 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1037 1037 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1038 1038 )
1039 1039 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1040 1040 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1041 1041 revision = Column('revision', String(40), nullable=True)
1042 1042 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1043 1043 line_no = Column('line_no', Unicode(10), nullable=True)
1044 1044 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1045 1045 f_path = Column('f_path', Unicode(1000), nullable=True)
1046 1046 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1047 1047 text = Column('text', UnicodeText().with_variant(UnicodeText(25000), 'mysql'), nullable=False)
1048 1048 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1049 1049 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1050 1050
1051 1051 author = relationship('User', lazy='joined')
1052 1052 repo = relationship('Repository')
1053 1053 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1054 1054 pull_request = relationship('PullRequest', lazy='joined')
1055 1055
1056 1056
1057 1057 class ChangesetStatus(Base, BaseModel):
1058 1058 __tablename__ = 'changeset_statuses'
1059 1059 __table_args__ = (
1060 1060 Index('cs_revision_idx', 'revision'),
1061 1061 Index('cs_version_idx', 'version'),
1062 1062 UniqueConstraint('repo_id', 'revision', 'version'),
1063 1063 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1064 1064 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1065 1065 )
1066 1066 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1067 1067 STATUS_APPROVED = 'approved'
1068 1068 STATUS_REJECTED = 'rejected'
1069 1069 STATUS_UNDER_REVIEW = 'under_review'
1070 1070
1071 1071 STATUSES = [
1072 1072 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1073 1073 (STATUS_APPROVED, _("Approved")),
1074 1074 (STATUS_REJECTED, _("Rejected")),
1075 1075 (STATUS_UNDER_REVIEW, _("Under Review")),
1076 1076 ]
1077 1077
1078 1078 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1079 1079 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1080 1080 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1081 1081 revision = Column('revision', String(40), nullable=False)
1082 1082 status = Column('status', String(128), nullable=False, default=DEFAULT)
1083 1083 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1084 1084 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1085 1085 version = Column('version', Integer(), nullable=False, default=0)
1086 1086 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1087 1087
1088 1088 author = relationship('User', lazy='joined')
1089 1089 repo = relationship('Repository')
1090 1090 comment = relationship('ChangesetComment', lazy='joined')
1091 1091 pull_request = relationship('PullRequest', lazy='joined')
1092 1092
1093 1093
1094 1094
1095 1095 class PullRequest(Base, BaseModel):
1096 1096 __tablename__ = 'pull_requests'
1097 1097 __table_args__ = (
1098 1098 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1099 1099 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1100 1100 )
1101 1101
1102 1102 STATUS_NEW = u'new'
1103 1103 STATUS_OPEN = u'open'
1104 1104 STATUS_CLOSED = u'closed'
1105 1105
1106 1106 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1107 1107 title = Column('title', Unicode(256), nullable=True)
1108 1108 description = Column('description', UnicodeText().with_variant(UnicodeText(10240), 'mysql'), nullable=True)
1109 1109 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1110 1110 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1111 1111 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1112 1112 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1113 1113 _revisions = Column('revisions', UnicodeText().with_variant(UnicodeText(20500), 'mysql'))
1114 1114 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1115 1115 org_ref = Column('org_ref', Unicode(256), nullable=False)
1116 1116 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1117 1117 other_ref = Column('other_ref', Unicode(256), nullable=False)
1118 1118
1119 1119 author = relationship('User', lazy='joined')
1120 1120 reviewers = relationship('PullRequestReviewers',
1121 1121 cascade="all, delete, delete-orphan")
1122 1122 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1123 1123 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1124 1124 statuses = relationship('ChangesetStatus')
1125 1125 comments = relationship('ChangesetComment',
1126 1126 cascade="all, delete, delete-orphan")
1127 1127
1128 1128
1129 1129 class PullRequestReviewers(Base, BaseModel):
1130 1130 __tablename__ = 'pull_request_reviewers'
1131 1131 __table_args__ = (
1132 1132 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1133 1133 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1134 1134 )
1135 1135
1136 1136 def __init__(self, user=None, pull_request=None):
1137 1137 self.user = user
1138 1138 self.pull_request = pull_request
1139 1139
1140 1140 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1141 1141 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1142 1142 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1143 1143
1144 1144 user = relationship('User')
1145 1145 pull_request = relationship('PullRequest')
1146 1146
1147 1147
1148 1148 class Notification(Base, BaseModel):
1149 1149 __tablename__ = 'notifications'
1150 1150 __table_args__ = (
1151 1151 Index('notification_type_idx', 'type'),
1152 1152 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1153 1153 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1154 1154 )
1155 1155
1156 1156 TYPE_CHANGESET_COMMENT = u'cs_comment'
1157 1157 TYPE_MESSAGE = u'message'
1158 1158 TYPE_MENTION = u'mention'
1159 1159 TYPE_REGISTRATION = u'registration'
1160 1160 TYPE_PULL_REQUEST = u'pull_request'
1161 1161 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1162 1162
1163 1163 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1164 1164 subject = Column('subject', Unicode(512), nullable=True)
1165 1165 body = Column('body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True)
1166 1166 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1167 1167 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1168 1168 type_ = Column('type', Unicode(256))
1169 1169
1170 1170 created_by_user = relationship('User')
1171 1171 notifications_to_users = relationship('UserNotification', lazy='joined',
1172 1172 cascade="all, delete, delete-orphan")
1173 1173
1174 1174
1175 1175 class UserNotification(Base, BaseModel):
1176 1176 __tablename__ = 'user_to_notification'
1177 1177 __table_args__ = (
1178 1178 UniqueConstraint('user_id', 'notification_id'),
1179 1179 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1180 1180 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1181 1181 )
1182 1182 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1183 1183 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1184 1184 read = Column('read', Boolean, default=False)
1185 1185 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1186 1186
1187 1187 user = relationship('User', lazy="joined")
1188 1188 notification = relationship('Notification', lazy="joined",
1189 1189 order_by=lambda: Notification.created_on.desc(),)
1190 1190
1191 1191
1192 1192 class Gist(Base, BaseModel):
1193 1193 __tablename__ = 'gists'
1194 1194 __table_args__ = (
1195 1195 Index('g_gist_access_id_idx', 'gist_access_id'),
1196 1196 Index('g_created_on_idx', 'created_on'),
1197 1197 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1198 1198 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1199 1199 )
1200 1200 GIST_PUBLIC = u'public'
1201 1201 GIST_PRIVATE = u'private'
1202 1202 DEFAULT_FILENAME = u'gistfile1.txt'
1203 1203
1204 1204 gist_id = Column('gist_id', Integer(), primary_key=True)
1205 1205 gist_access_id = Column('gist_access_id', Unicode(250))
1206 1206 gist_description = Column('gist_description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
1207 1207 gist_owner = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=True)
1208 1208 gist_expires = Column('gist_expires', Float(53), nullable=False)
1209 1209 gist_type = Column('gist_type', Unicode(128), nullable=False)
1210 1210 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1211 1211 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1212 1212
1213 1213 owner = relationship('User')
1214 1214
1215 1215
1216 1216 class DbMigrateVersion(Base, BaseModel):
1217 1217 __tablename__ = 'db_migrate_version'
1218 1218 __table_args__ = (
1219 1219 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1220 1220 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1221 1221 )
1222 1222 repository_id = Column('repository_id', String(250), primary_key=True)
1223 1223 repository_path = Column('repository_path', Text)
1224 1224 version = Column('version', Integer)
@@ -1,1230 +1,1230 b''
1 1
2 2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 3 #
4 4 # This program is free software: you can redistribute it and/or modify
5 5 # it under the terms of the GNU Affero General Public License, version 3
6 6 # (only), as published by the Free Software Foundation.
7 7 #
8 8 # This program is distributed in the hope that it will be useful,
9 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 11 # GNU General Public License for more details.
12 12 #
13 13 # You should have received a copy of the GNU Affero General Public License
14 14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 15 #
16 16 # This program is dual-licensed. If you wish to learn more about the
17 17 # RhodeCode Enterprise Edition, including its added features, Support services,
18 18 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 19
20 20 import os
21 21 import time
22 22 import logging
23 23 import datetime
24 24 import traceback
25 25 import hashlib
26 26 import collections
27 27 import functools
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 remove_prefix, time_to_datetime, aslist, Optional, safe_int, \
46 46 get_clone_url
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.items():
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 __tablename__ = 'rhodecode_settings'
154 154 __table_args__ = (
155 155 UniqueConstraint('app_settings_name'),
156 156 {'extend_existing': True, 'mysql_engine': 'InnoDB',
157 157 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
158 158 )
159 159
160 160 SETTINGS_TYPES = {
161 161 'str': safe_str,
162 162 'int': safe_int,
163 163 'unicode': safe_str,
164 164 'bool': str2bool,
165 165 'list': functools.partial(aslist, sep=',')
166 166 }
167 167 DEFAULT_UPDATE_URL = 'https://rhodecode.com/api/v1/info/versions'
168 168
169 169 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
170 170 app_settings_name = Column("app_settings_name", String(255), nullable=True, unique=None, default=None)
171 171 _app_settings_value = Column("app_settings_value", String(4096), nullable=True, unique=None, default=None)
172 172 _app_settings_type = Column("app_settings_type", String(255), nullable=True, unique=None, default=None)
173 173
174 174 def __init__(self, key='', val='', type='unicode'):
175 175 self.app_settings_name = key
176 176 self.app_settings_value = val
177 177 self.app_settings_type = type
178 178
179 179 @validates('_app_settings_value')
180 180 def validate_settings_value(self, key, val):
181 181 assert type(val) == str
182 182 return val
183 183
184 184 @hybrid_property
185 185 def app_settings_value(self):
186 186 v = self._app_settings_value
187 187 _type = self.app_settings_type
188 188 converter = self.SETTINGS_TYPES.get(_type) or self.SETTINGS_TYPES['unicode']
189 189 return converter(v)
190 190
191 191 @app_settings_value.setter
192 192 def app_settings_value(self, val):
193 193 """
194 194 Setter that will always make sure we use unicode in app_settings_value
195 195
196 196 :param val:
197 197 """
198 198 self._app_settings_value = safe_str(val)
199 199
200 200 @hybrid_property
201 201 def app_settings_type(self):
202 202 return self._app_settings_type
203 203
204 204 @app_settings_type.setter
205 205 def app_settings_type(self, val):
206 206 if val not in self.SETTINGS_TYPES:
207 207 raise Exception('type must be one of %s got %s'
208 208 % (self.SETTINGS_TYPES.keys(), val))
209 209 self._app_settings_type = val
210 210
211 211 def __unicode__(self):
212 212 return u"<%s('%s:%s[%s]')>" % (
213 213 self.__class__.__name__,
214 214 self.app_settings_name, self.app_settings_value, self.app_settings_type
215 215 )
216 216
217 217
218 218 class RhodeCodeUi(Base, BaseModel):
219 219 __tablename__ = 'rhodecode_ui'
220 220 __table_args__ = (
221 221 UniqueConstraint('ui_key'),
222 222 {'extend_existing': True, 'mysql_engine': 'InnoDB',
223 223 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
224 224 )
225 225
226 226 HOOK_REPO_SIZE = 'changegroup.repo_size'
227 227 HOOK_PUSH = 'changegroup.push_logger'
228 228 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
229 229 HOOK_PULL = 'outgoing.pull_logger'
230 230 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
231 231
232 232 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
233 233 ui_section = Column("ui_section", String(255), nullable=True, unique=None, default=None)
234 234 ui_key = Column("ui_key", String(255), nullable=True, unique=None, default=None)
235 235 ui_value = Column("ui_value", String(255), nullable=True, unique=None, default=None)
236 236 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
237 237
238 238 def __repr__(self):
239 239 return '<%s[%s]%s=>%s]>' % (self.__class__.__name__, self.ui_section,
240 240 self.ui_key, self.ui_value)
241 241
242 242
243 243 class User(Base, BaseModel):
244 244 __tablename__ = 'users'
245 245 __table_args__ = (
246 246 UniqueConstraint('username'), UniqueConstraint('email'),
247 247 Index('u_username_idx', 'username'),
248 248 Index('u_email_idx', 'email'),
249 249 {'extend_existing': True, 'mysql_engine': 'InnoDB',
250 250 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
251 251 )
252 252 DEFAULT_USER = 'default'
253 253 DEFAULT_GRAVATAR_URL = 'https://secure.gravatar.com/avatar/{md5email}?d=identicon&s={size}'
254 254
255 255 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
256 256 username = Column("username", String(255), nullable=True, unique=None, default=None)
257 257 password = Column("password", String(255), nullable=True, unique=None, default=None)
258 258 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
259 259 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
260 260 name = Column("firstname", String(255), nullable=True, unique=None, default=None)
261 261 lastname = Column("lastname", String(255), nullable=True, unique=None, default=None)
262 262 _email = Column("email", String(255), nullable=True, unique=None, default=None)
263 263 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
264 264 extern_type = Column("extern_type", String(255), nullable=True, unique=None, default=None)
265 265 extern_name = Column("extern_name", String(255), nullable=True, unique=None, default=None)
266 266 api_key = Column("api_key", String(255), nullable=True, unique=None, default=None)
267 267 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
268 268 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
269 269 _user_data = Column("user_data", LargeBinary(), nullable=True) # JSON data
270 270
271 271 user_log = relationship('UserLog')
272 272 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
273 273
274 274 repositories = relationship('Repository')
275 275 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
276 276 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
277 277
278 278 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
279 279 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
280 280
281 281 group_member = relationship('UserGroupMember', cascade='all')
282 282
283 283 notifications = relationship('UserNotification', cascade='all')
284 284 # notifications assigned to this user
285 285 user_created_notifications = relationship('Notification', cascade='all')
286 286 # comments created by this user
287 287 user_comments = relationship('ChangesetComment', cascade='all')
288 288 user_emails = relationship('UserEmailMap', cascade='all')
289 289 user_auth_tokens = relationship('UserApiKeys', cascade='all')
290 290
291 291 @hybrid_property
292 292 def email(self):
293 293 return self._email
294 294
295 295 @email.setter
296 296 def email(self, val):
297 297 self._email = val.lower() if val else None
298 298
299 299 @property
300 300 def firstname(self):
301 301 # alias for future
302 302 return self.name
303 303
304 304 @property
305 305 def username_and_name(self):
306 306 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
307 307
308 308 @property
309 309 def full_name(self):
310 310 return '%s %s' % (self.firstname, self.lastname)
311 311
312 312 @property
313 313 def full_contact(self):
314 314 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
315 315
316 316 @property
317 317 def short_contact(self):
318 318 return '%s %s' % (self.firstname, self.lastname)
319 319
320 320 @property
321 321 def is_admin(self):
322 322 return self.admin
323 323
324 324 @classmethod
325 325 def get_by_username(cls, username, case_insensitive=False, cache=False):
326 326 if case_insensitive:
327 327 q = cls.query().filter(cls.username.ilike(username))
328 328 else:
329 329 q = cls.query().filter(cls.username == username)
330 330
331 331 if cache:
332 332 q = q.options(FromCache(
333 333 "sql_cache_short",
334 334 "get_user_%s" % _hash_key(username)
335 335 )
336 336 )
337 337 return q.scalar()
338 338
339 339 @classmethod
340 340 def get_by_auth_token(cls, auth_token, cache=False, fallback=True):
341 341 q = cls.query().filter(cls.api_key == auth_token)
342 342
343 343 if cache:
344 344 q = q.options(FromCache("sql_cache_short",
345 345 "get_auth_token_%s" % auth_token))
346 346 res = q.scalar()
347 347
348 348 if fallback and not res:
349 349 #fallback to additional keys
350 350 _res = UserApiKeys.query()\
351 351 .filter(UserApiKeys.api_key == auth_token)\
352 352 .filter(or_(UserApiKeys.expires == -1,
353 353 UserApiKeys.expires >= time.time()))\
354 354 .first()
355 355 if _res:
356 356 res = _res.user
357 357 return res
358 358
359 359 @classmethod
360 360 def get_by_email(cls, email, case_insensitive=False, cache=False):
361 361
362 362 if case_insensitive:
363 363 q = cls.query().filter(cls.email.ilike(email))
364 364 else:
365 365 q = cls.query().filter(cls.email == email)
366 366
367 367 if cache:
368 368 q = q.options(FromCache("sql_cache_short",
369 369 "get_email_key_%s" % email))
370 370
371 371 ret = q.scalar()
372 372 if ret is None:
373 373 q = UserEmailMap.query()
374 374 # try fetching in alternate email map
375 375 if case_insensitive:
376 376 q = q.filter(UserEmailMap.email.ilike(email))
377 377 else:
378 378 q = q.filter(UserEmailMap.email == email)
379 379 q = q.options(joinedload(UserEmailMap.user))
380 380 if cache:
381 381 q = q.options(FromCache("sql_cache_short",
382 382 "get_email_map_key_%s" % email))
383 383 ret = getattr(q.scalar(), 'user', None)
384 384
385 385 return ret
386 386
387 387 @classmethod
388 388 def get_first_admin(cls):
389 389 user = User.query().filter(User.admin == True).first()
390 390 if user is None:
391 391 raise Exception('Missing administrative account!')
392 392 return user
393 393
394 394 @classmethod
395 395 def get_default_user(cls, cache=False):
396 396 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
397 397 if user is None:
398 398 raise Exception('Missing default account!')
399 399 return user
400 400
401 401
402 402 class UserApiKeys(Base, BaseModel):
403 403 __tablename__ = 'user_api_keys'
404 404 __table_args__ = (
405 405 Index('uak_api_key_idx', 'api_key'),
406 406 Index('uak_api_key_expires_idx', 'api_key', 'expires'),
407 407 UniqueConstraint('api_key'),
408 408 {'extend_existing': True, 'mysql_engine': 'InnoDB',
409 409 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
410 410 )
411 411
412 412
413 413 user_api_key_id = Column("user_api_key_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
414 414 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
415 415 api_key = Column("api_key", String(255), nullable=False, unique=True)
416 416 description = Column('description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
417 417 expires = Column('expires', Float(53), nullable=False)
418 418 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
419 419
420 420 user = relationship('User', lazy='joined')
421 421
422 422
423 423 class UserEmailMap(Base, BaseModel):
424 424 __tablename__ = 'user_email_map'
425 425 __table_args__ = (
426 426 Index('uem_email_idx', 'email'),
427 427 UniqueConstraint('email'),
428 428 {'extend_existing': True, 'mysql_engine': 'InnoDB',
429 429 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
430 430 )
431 431
432 432
433 433 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
434 434 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
435 435 _email = Column("email", String(255), nullable=True, unique=False, default=None)
436 436 user = relationship('User', lazy='joined')
437 437
438 438 @validates('_email')
439 439 def validate_email(self, key, email):
440 440 # check if this email is not main one
441 441 main_email = Session().query(User).filter(User.email == email).scalar()
442 442 if main_email is not None:
443 443 raise AttributeError('email %s is present is user table' % email)
444 444 return email
445 445
446 446 @hybrid_property
447 447 def email(self):
448 448 return self._email
449 449
450 450 @email.setter
451 451 def email(self, val):
452 452 self._email = val.lower() if val else None
453 453
454 454
455 455 class UserIpMap(Base, BaseModel):
456 456 __tablename__ = 'user_ip_map'
457 457 __table_args__ = (
458 458 UniqueConstraint('user_id', 'ip_addr'),
459 459 {'extend_existing': True, 'mysql_engine': 'InnoDB',
460 460 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
461 461 )
462 462
463 463
464 464 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
465 465 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
466 466 ip_addr = Column("ip_addr", String(255), nullable=True, unique=False, default=None)
467 467 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
468 468 user = relationship('User', lazy='joined')
469 469
470 470 def __unicode__(self):
471 471 return u"<%s('user_id:%s=>%s')>" % (self.__class__.__name__,
472 472 self.user_id, self.ip_addr)
473 473
474 474
475 475 class UserLog(Base, BaseModel):
476 476 __tablename__ = 'user_logs'
477 477 __table_args__ = (
478 478 {'extend_existing': True, 'mysql_engine': 'InnoDB',
479 479 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
480 480 )
481 481 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
482 482 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
483 483 username = Column("username", String(255), nullable=True, unique=None, default=None)
484 484 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
485 485 repository_name = Column("repository_name", String(255), nullable=True, unique=None, default=None)
486 486 user_ip = Column("user_ip", String(255), nullable=True, unique=None, default=None)
487 487 action = Column("action", String(1200000), nullable=True, unique=None, default=None)
488 488 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
489 489
490 490 def __unicode__(self):
491 491 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
492 492 self.repository_name,
493 493 self.action)
494 494
495 495 user = relationship('User')
496 496 repository = relationship('Repository', cascade='')
497 497
498 498
499 499 class UserGroup(Base, BaseModel):
500 500 __tablename__ = 'users_groups'
501 501 __table_args__ = (
502 502 {'extend_existing': True, 'mysql_engine': 'InnoDB',
503 503 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
504 504 )
505 505
506 506 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
507 507 users_group_name = Column("users_group_name", String(255), nullable=False, unique=True, default=None)
508 508 user_group_description = Column("user_group_description", String(10000), nullable=True, unique=None, default=None)
509 509 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
510 510 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
511 511 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
512 512 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
513 513 _group_data = Column("group_data", LargeBinary(), nullable=True) # JSON data
514 514
515 515 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
516 516 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
517 517 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
518 518 users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
519 519 user_user_group_to_perm = relationship('UserUserGroupToPerm', cascade='all')
520 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm ', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
520 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
521 521
522 522 user = relationship('User')
523 523
524 524 def __unicode__(self):
525 525 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
526 526 self.users_group_id,
527 527 self.users_group_name)
528 528
529 529 @classmethod
530 530 def get_by_group_name(cls, group_name, cache=False,
531 531 case_insensitive=False):
532 532 if case_insensitive:
533 533 q = cls.query().filter(cls.users_group_name.ilike(group_name))
534 534 else:
535 535 q = cls.query().filter(cls.users_group_name == group_name)
536 536 if cache:
537 537 q = q.options(FromCache(
538 538 "sql_cache_short",
539 539 "get_user_%s" % _hash_key(group_name)
540 540 )
541 541 )
542 542 return q.scalar()
543 543
544 544 @classmethod
545 545 def get(cls, user_group_id, cache=False):
546 546 user_group = cls.query()
547 547 if cache:
548 548 user_group = user_group.options(FromCache("sql_cache_short",
549 549 "get_users_group_%s" % user_group_id))
550 550 return user_group.get(user_group_id)
551 551
552 552
553 553 class UserGroupMember(Base, BaseModel):
554 554 __tablename__ = 'users_groups_members'
555 555 __table_args__ = (
556 556 {'extend_existing': True, 'mysql_engine': 'InnoDB',
557 557 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
558 558 )
559 559
560 560 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
561 561 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
562 562 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
563 563
564 564 user = relationship('User', lazy='joined')
565 565 users_group = relationship('UserGroup')
566 566
567 567 def __init__(self, gr_id='', u_id=''):
568 568 self.users_group_id = gr_id
569 569 self.user_id = u_id
570 570
571 571
572 572 class RepositoryField(Base, BaseModel):
573 573 __tablename__ = 'repositories_fields'
574 574 __table_args__ = (
575 575 UniqueConstraint('repository_id', 'field_key'), # no-multi field
576 576 {'extend_existing': True, 'mysql_engine': 'InnoDB',
577 577 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
578 578 )
579 579 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
580 580
581 581 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
582 582 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
583 583 field_key = Column("field_key", String(250))
584 584 field_label = Column("field_label", String(1024), nullable=False)
585 585 field_value = Column("field_value", String(10000), nullable=False)
586 586 field_desc = Column("field_desc", String(1024), nullable=False)
587 587 field_type = Column("field_type", String(256), nullable=False, unique=None)
588 588 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
589 589
590 590 repository = relationship('Repository')
591 591
592 592 @classmethod
593 593 def get_by_key_name(cls, key, repo):
594 594 row = cls.query()\
595 595 .filter(cls.repository == repo)\
596 596 .filter(cls.field_key == key).scalar()
597 597 return row
598 598
599 599
600 600 class Repository(Base, BaseModel):
601 601 __tablename__ = 'repositories'
602 602 __table_args__ = (
603 603 UniqueConstraint('repo_name'),
604 604 Index('r_repo_name_idx', 'repo_name'),
605 605 {'extend_existing': True, 'mysql_engine': 'InnoDB',
606 606 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
607 607 )
608 608 DEFAULT_CLONE_URI = '{scheme}://{user}@{netloc}/{repo}'
609 609 DEFAULT_CLONE_URI_ID = '{scheme}://{user}@{netloc}/_{repoid}'
610 610
611 611 STATE_CREATED = 'repo_state_created'
612 612 STATE_PENDING = 'repo_state_pending'
613 613 STATE_ERROR = 'repo_state_error'
614 614
615 615 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
616 616 repo_name = Column("repo_name", String(255), nullable=False, unique=True, default=None)
617 617 repo_state = Column("repo_state", String(255), nullable=True)
618 618
619 619 clone_uri = Column("clone_uri", String(255), nullable=True, unique=False, default=None)
620 620 repo_type = Column("repo_type", String(255), nullable=False, unique=False, default=None)
621 621 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
622 622 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
623 623 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
624 624 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
625 625 description = Column("description", String(10000), nullable=True, unique=None, default=None)
626 626 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
627 627 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
628 628 _landing_revision = Column("landing_revision", String(255), nullable=False, unique=False, default=None)
629 629 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
630 630 _locked = Column("locked", String(255), nullable=True, unique=False, default=None)
631 631 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
632 632
633 633 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
634 634 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
635 635
636 636 user = relationship('User')
637 637 fork = relationship('Repository', remote_side=repo_id)
638 638 group = relationship('RepoGroup')
639 639 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
640 640 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
641 641 stats = relationship('Statistics', cascade='all', uselist=False)
642 642
643 643 followers = relationship('UserFollowing',
644 644 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
645 645 cascade='all')
646 646 extra_fields = relationship('RepositoryField',
647 647 cascade="all, delete, delete-orphan")
648 648
649 649 logs = relationship('UserLog')
650 650 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
651 651
652 652 pull_requests_org = relationship('PullRequest',
653 653 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
654 654 cascade="all, delete, delete-orphan")
655 655
656 656 pull_requests_other = relationship('PullRequest',
657 657 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
658 658 cascade="all, delete, delete-orphan")
659 659
660 660 def __unicode__(self):
661 661 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
662 662 safe_str(self.repo_name))
663 663
664 664 @classmethod
665 665 def get_by_repo_name(cls, repo_name):
666 666 q = Session().query(cls).filter(cls.repo_name == repo_name)
667 667 q = q.options(joinedload(Repository.fork))\
668 668 .options(joinedload(Repository.user))\
669 669 .options(joinedload(Repository.group))
670 670 return q.scalar()
671 671
672 672
673 673 class RepoGroup(Base, BaseModel):
674 674 __tablename__ = 'groups'
675 675 __table_args__ = (
676 676 UniqueConstraint('group_name', 'group_parent_id'),
677 677 {'extend_existing': True, 'mysql_engine': 'InnoDB',
678 678 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
679 679 )
680 680
681 681
682 682 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
683 683 group_name = Column("group_name", String(255), nullable=False, unique=True, default=None)
684 684 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
685 685 group_description = Column("group_description", String(10000), nullable=True, unique=None, default=None)
686 686 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
687 687 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
688 688 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
689 689
690 690 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
691 691 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
692 692 parent_group = relationship('RepoGroup', remote_side=group_id)
693 693 user = relationship('User')
694 694
695 695 def __init__(self, group_name='', parent_group=None):
696 696 self.group_name = group_name
697 697 self.parent_group = parent_group
698 698
699 699 def __unicode__(self):
700 700 return u"<%s('id:%s:%s')>" % (self.__class__.__name__, self.group_id,
701 701 self.group_name)
702 702
703 703 @classmethod
704 704 def url_sep(cls):
705 705 return URL_SEP
706 706
707 707 @classmethod
708 708 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
709 709 if case_insensitive:
710 710 gr = cls.query()\
711 711 .filter(cls.group_name.ilike(group_name))
712 712 else:
713 713 gr = cls.query()\
714 714 .filter(cls.group_name == group_name)
715 715 if cache:
716 716 gr = gr.options(FromCache(
717 717 "sql_cache_short",
718 718 "get_group_%s" % _hash_key(group_name)
719 719 )
720 720 )
721 721 return gr.scalar()
722 722
723 723
724 724 class Permission(Base, BaseModel):
725 725 __tablename__ = 'permissions'
726 726 __table_args__ = (
727 727 Index('p_perm_name_idx', 'permission_name'),
728 728 {'extend_existing': True, 'mysql_engine': 'InnoDB',
729 729 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
730 730 )
731 731 PERMS = [
732 732 ('hg.admin', _('RhodeCode Administrator')),
733 733
734 734 ('repository.none', _('Repository no access')),
735 735 ('repository.read', _('Repository read access')),
736 736 ('repository.write', _('Repository write access')),
737 737 ('repository.admin', _('Repository admin access')),
738 738
739 739 ('group.none', _('Repository group no access')),
740 740 ('group.read', _('Repository group read access')),
741 741 ('group.write', _('Repository group write access')),
742 742 ('group.admin', _('Repository group admin access')),
743 743
744 744 ('usergroup.none', _('User group no access')),
745 745 ('usergroup.read', _('User group read access')),
746 746 ('usergroup.write', _('User group write access')),
747 747 ('usergroup.admin', _('User group admin access')),
748 748
749 749 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
750 750 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
751 751
752 752 ('hg.usergroup.create.false', _('User Group creation disabled')),
753 753 ('hg.usergroup.create.true', _('User Group creation enabled')),
754 754
755 755 ('hg.create.none', _('Repository creation disabled')),
756 756 ('hg.create.repository', _('Repository creation enabled')),
757 757 ('hg.create.write_on_repogroup.true', _('Repository creation enabled with write permission to a repository group')),
758 758 ('hg.create.write_on_repogroup.false', _('Repository creation disabled with write permission to a repository group')),
759 759
760 760 ('hg.fork.none', _('Repository forking disabled')),
761 761 ('hg.fork.repository', _('Repository forking enabled')),
762 762
763 763 ('hg.register.none', _('Registration disabled')),
764 764 ('hg.register.manual_activate', _('User Registration with manual account activation')),
765 765 ('hg.register.auto_activate', _('User Registration with automatic account activation')),
766 766
767 767 ('hg.extern_activate.manual', _('Manual activation of external account')),
768 768 ('hg.extern_activate.auto', _('Automatic activation of external account')),
769 769
770 770 ]
771 771
772 772 #definition of system default permissions for DEFAULT user
773 773 DEFAULT_USER_PERMISSIONS = [
774 774 'repository.read',
775 775 'group.read',
776 776 'usergroup.read',
777 777 'hg.create.repository',
778 778 'hg.create.write_on_repogroup.true',
779 779 'hg.fork.repository',
780 780 'hg.register.manual_activate',
781 781 'hg.extern_activate.auto',
782 782 ]
783 783
784 784 # defines which permissions are more important higher the more important
785 785 # Weight defines which permissions are more important.
786 786 # The higher number the more important.
787 787 PERM_WEIGHTS = {
788 788 'repository.none': 0,
789 789 'repository.read': 1,
790 790 'repository.write': 3,
791 791 'repository.admin': 4,
792 792
793 793 'group.none': 0,
794 794 'group.read': 1,
795 795 'group.write': 3,
796 796 'group.admin': 4,
797 797
798 798 'usergroup.none': 0,
799 799 'usergroup.read': 1,
800 800 'usergroup.write': 3,
801 801 'usergroup.admin': 4,
802 802 'hg.repogroup.create.false': 0,
803 803 'hg.repogroup.create.true': 1,
804 804
805 805 'hg.usergroup.create.false': 0,
806 806 'hg.usergroup.create.true': 1,
807 807
808 808 'hg.fork.none': 0,
809 809 'hg.fork.repository': 1,
810 810 'hg.create.none': 0,
811 811 'hg.create.repository': 1
812 812 }
813 813
814 814 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
815 815 permission_name = Column("permission_name", String(255), nullable=True, unique=None, default=None)
816 816 permission_longname = Column("permission_longname", String(255), nullable=True, unique=None, default=None)
817 817
818 818 def __unicode__(self):
819 819 return u"<%s('%s:%s')>" % (
820 820 self.__class__.__name__, self.permission_id, self.permission_name
821 821 )
822 822
823 823 @classmethod
824 824 def get_by_key(cls, key):
825 825 return cls.query().filter(cls.permission_name == key).scalar()
826 826
827 827
828 828 class UserRepoToPerm(Base, BaseModel):
829 829 __tablename__ = 'repo_to_perm'
830 830 __table_args__ = (
831 831 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
832 832 {'extend_existing': True, 'mysql_engine': 'InnoDB',
833 833 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
834 834 )
835 835 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
836 836 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
837 837 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
838 838 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
839 839
840 840 user = relationship('User')
841 841 repository = relationship('Repository')
842 842 permission = relationship('Permission')
843 843
844 844 def __unicode__(self):
845 845 return u'<%s => %s >' % (self.user, self.repository)
846 846
847 847
848 848 class UserUserGroupToPerm(Base, BaseModel):
849 849 __tablename__ = 'user_user_group_to_perm'
850 850 __table_args__ = (
851 851 UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
852 852 {'extend_existing': True, 'mysql_engine': 'InnoDB',
853 853 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
854 854 )
855 855 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
856 856 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
857 857 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
858 858 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
859 859
860 860 user = relationship('User')
861 861 user_group = relationship('UserGroup')
862 862 permission = relationship('Permission')
863 863
864 864 def __unicode__(self):
865 865 return u'<%s => %s >' % (self.user, self.user_group)
866 866
867 867
868 868 class UserToPerm(Base, BaseModel):
869 869 __tablename__ = 'user_to_perm'
870 870 __table_args__ = (
871 871 UniqueConstraint('user_id', 'permission_id'),
872 872 {'extend_existing': True, 'mysql_engine': 'InnoDB',
873 873 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
874 874 )
875 875 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
876 876 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
877 877 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
878 878
879 879 user = relationship('User')
880 880 permission = relationship('Permission', lazy='joined')
881 881
882 882 def __unicode__(self):
883 883 return u'<%s => %s >' % (self.user, self.permission)
884 884
885 885
886 886 class UserGroupRepoToPerm(Base, BaseModel):
887 887 __tablename__ = 'users_group_repo_to_perm'
888 888 __table_args__ = (
889 889 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
890 890 {'extend_existing': True, 'mysql_engine': 'InnoDB',
891 891 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
892 892 )
893 893 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
894 894 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
895 895 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
896 896 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
897 897
898 898 users_group = relationship('UserGroup')
899 899 permission = relationship('Permission')
900 900 repository = relationship('Repository')
901 901
902 902 def __unicode__(self):
903 903 return u'<UserGroupRepoToPerm:%s => %s >' % (self.users_group, self.repository)
904 904
905 905
906 906 class UserGroupUserGroupToPerm(Base, BaseModel):
907 907 __tablename__ = 'user_group_user_group_to_perm'
908 908 __table_args__ = (
909 909 UniqueConstraint('target_user_group_id', 'user_group_id', 'permission_id'),
910 910 CheckConstraint('target_user_group_id != user_group_id'),
911 911 {'extend_existing': True, 'mysql_engine': 'InnoDB',
912 912 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
913 913 )
914 914 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)
915 915 target_user_group_id = Column("target_user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
916 916 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
917 917 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
918 918
919 919 target_user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
920 920 user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
921 921 permission = relationship('Permission')
922 922
923 923 def __unicode__(self):
924 924 return u'<UserGroupUserGroup:%s => %s >' % (self.target_user_group, self.user_group)
925 925
926 926
927 927 class UserGroupToPerm(Base, BaseModel):
928 928 __tablename__ = 'users_group_to_perm'
929 929 __table_args__ = (
930 930 UniqueConstraint('users_group_id', 'permission_id',),
931 931 {'extend_existing': True, 'mysql_engine': 'InnoDB',
932 932 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
933 933 )
934 934 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
935 935 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
936 936 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
937 937
938 938 users_group = relationship('UserGroup')
939 939 permission = relationship('Permission')
940 940
941 941
942 942 class UserRepoGroupToPerm(Base, BaseModel):
943 943 __tablename__ = 'user_repo_group_to_perm'
944 944 __table_args__ = (
945 945 UniqueConstraint('user_id', 'group_id', 'permission_id'),
946 946 {'extend_existing': True, 'mysql_engine': 'InnoDB',
947 947 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
948 948 )
949 949
950 950 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
951 951 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
952 952 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
953 953 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
954 954
955 955 user = relationship('User')
956 956 group = relationship('RepoGroup')
957 957 permission = relationship('Permission')
958 958
959 959
960 960 class UserGroupRepoGroupToPerm(Base, BaseModel):
961 961 __tablename__ = 'users_group_repo_group_to_perm'
962 962 __table_args__ = (
963 963 UniqueConstraint('users_group_id', 'group_id'),
964 964 {'extend_existing': True, 'mysql_engine': 'InnoDB',
965 965 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
966 966 )
967 967
968 968 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)
969 969 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
970 970 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
971 971 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
972 972
973 973 users_group = relationship('UserGroup')
974 974 permission = relationship('Permission')
975 975 group = relationship('RepoGroup')
976 976
977 977
978 978 class Statistics(Base, BaseModel):
979 979 __tablename__ = 'statistics'
980 980 __table_args__ = (
981 981 UniqueConstraint('repository_id'),
982 982 {'extend_existing': True, 'mysql_engine': 'InnoDB',
983 983 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
984 984 )
985 985 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
986 986 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
987 987 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
988 988 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
989 989 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
990 990 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
991 991
992 992 repository = relationship('Repository', single_parent=True)
993 993
994 994
995 995 class UserFollowing(Base, BaseModel):
996 996 __tablename__ = 'user_followings'
997 997 __table_args__ = (
998 998 UniqueConstraint('user_id', 'follows_repository_id'),
999 999 UniqueConstraint('user_id', 'follows_user_id'),
1000 1000 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1001 1001 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1002 1002 )
1003 1003
1004 1004 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1005 1005 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1006 1006 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1007 1007 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1008 1008 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1009 1009
1010 1010 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1011 1011
1012 1012 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1013 1013 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1014 1014
1015 1015
1016 1016 class CacheInvalidation(Base, BaseModel):
1017 1017 __tablename__ = 'cache_invalidation'
1018 1018 __table_args__ = (
1019 1019 UniqueConstraint('cache_key'),
1020 1020 Index('key_idx', 'cache_key'),
1021 1021 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1022 1022 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1023 1023 )
1024 1024 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1025 1025 cache_key = Column("cache_key", String(255), nullable=True, unique=None, default=None)
1026 1026 cache_args = Column("cache_args", String(255), nullable=True, unique=None, default=None)
1027 1027 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1028 1028
1029 1029 def __init__(self, cache_key, cache_args=''):
1030 1030 self.cache_key = cache_key
1031 1031 self.cache_args = cache_args
1032 1032 self.cache_active = False
1033 1033
1034 1034
1035 1035 class ChangesetComment(Base, BaseModel):
1036 1036 __tablename__ = 'changeset_comments'
1037 1037 __table_args__ = (
1038 1038 Index('cc_revision_idx', 'revision'),
1039 1039 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1040 1040 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1041 1041 )
1042 1042 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1043 1043 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1044 1044 revision = Column('revision', String(40), nullable=True)
1045 1045 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1046 1046 line_no = Column('line_no', Unicode(10), nullable=True)
1047 1047 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1048 1048 f_path = Column('f_path', Unicode(1000), nullable=True)
1049 1049 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1050 1050 text = Column('text', UnicodeText().with_variant(UnicodeText(25000), 'mysql'), nullable=False)
1051 1051 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1052 1052 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1053 1053
1054 1054 author = relationship('User', lazy='joined')
1055 1055 repo = relationship('Repository')
1056 1056 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1057 1057 pull_request = relationship('PullRequest', lazy='joined')
1058 1058
1059 1059
1060 1060 class ChangesetStatus(Base, BaseModel):
1061 1061 __tablename__ = 'changeset_statuses'
1062 1062 __table_args__ = (
1063 1063 Index('cs_revision_idx', 'revision'),
1064 1064 Index('cs_version_idx', 'version'),
1065 1065 UniqueConstraint('repo_id', 'revision', 'version'),
1066 1066 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1067 1067 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1068 1068 )
1069 1069 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1070 1070 STATUS_APPROVED = 'approved'
1071 1071 STATUS_REJECTED = 'rejected'
1072 1072 STATUS_UNDER_REVIEW = 'under_review'
1073 1073
1074 1074 STATUSES = [
1075 1075 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1076 1076 (STATUS_APPROVED, _("Approved")),
1077 1077 (STATUS_REJECTED, _("Rejected")),
1078 1078 (STATUS_UNDER_REVIEW, _("Under Review")),
1079 1079 ]
1080 1080
1081 1081 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1082 1082 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1083 1083 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1084 1084 revision = Column('revision', String(40), nullable=False)
1085 1085 status = Column('status', String(128), nullable=False, default=DEFAULT)
1086 1086 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1087 1087 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1088 1088 version = Column('version', Integer(), nullable=False, default=0)
1089 1089 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1090 1090
1091 1091 author = relationship('User', lazy='joined')
1092 1092 repo = relationship('Repository')
1093 1093 comment = relationship('ChangesetComment', lazy='joined')
1094 1094 pull_request = relationship('PullRequest', lazy='joined')
1095 1095
1096 1096
1097 1097
1098 1098 class PullRequest(Base, BaseModel):
1099 1099 __tablename__ = 'pull_requests'
1100 1100 __table_args__ = (
1101 1101 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1102 1102 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1103 1103 )
1104 1104
1105 1105 STATUS_NEW = u'new'
1106 1106 STATUS_OPEN = u'open'
1107 1107 STATUS_CLOSED = u'closed'
1108 1108
1109 1109 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1110 1110 title = Column('title', Unicode(256), nullable=True)
1111 1111 description = Column('description', UnicodeText().with_variant(UnicodeText(10240), 'mysql'), nullable=True)
1112 1112 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1113 1113 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1114 1114 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1115 1115 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1116 1116 _revisions = Column('revisions', UnicodeText().with_variant(UnicodeText(20500), 'mysql'))
1117 1117 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1118 1118 org_ref = Column('org_ref', Unicode(256), nullable=False)
1119 1119 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1120 1120 other_ref = Column('other_ref', Unicode(256), nullable=False)
1121 1121
1122 1122 author = relationship('User', lazy='joined')
1123 1123 reviewers = relationship('PullRequestReviewers',
1124 1124 cascade="all, delete, delete-orphan")
1125 1125 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1126 1126 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1127 1127 statuses = relationship('ChangesetStatus')
1128 1128 comments = relationship('ChangesetComment',
1129 1129 cascade="all, delete, delete-orphan")
1130 1130
1131 1131
1132 1132 class PullRequestReviewers(Base, BaseModel):
1133 1133 __tablename__ = 'pull_request_reviewers'
1134 1134 __table_args__ = (
1135 1135 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1136 1136 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1137 1137 )
1138 1138
1139 1139 def __init__(self, user=None, pull_request=None):
1140 1140 self.user = user
1141 1141 self.pull_request = pull_request
1142 1142
1143 1143 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1144 1144 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1145 1145 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1146 1146
1147 1147 user = relationship('User')
1148 1148 pull_request = relationship('PullRequest')
1149 1149
1150 1150
1151 1151 class Notification(Base, BaseModel):
1152 1152 __tablename__ = 'notifications'
1153 1153 __table_args__ = (
1154 1154 Index('notification_type_idx', 'type'),
1155 1155 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1156 1156 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1157 1157 )
1158 1158
1159 1159 TYPE_CHANGESET_COMMENT = u'cs_comment'
1160 1160 TYPE_MESSAGE = u'message'
1161 1161 TYPE_MENTION = u'mention'
1162 1162 TYPE_REGISTRATION = u'registration'
1163 1163 TYPE_PULL_REQUEST = u'pull_request'
1164 1164 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1165 1165
1166 1166 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1167 1167 subject = Column('subject', Unicode(512), nullable=True)
1168 1168 body = Column('body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True)
1169 1169 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1170 1170 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1171 1171 type_ = Column('type', Unicode(256))
1172 1172
1173 1173 created_by_user = relationship('User')
1174 1174 notifications_to_users = relationship('UserNotification', lazy='joined',
1175 1175 cascade="all, delete, delete-orphan")
1176 1176
1177 1177
1178 1178 class UserNotification(Base, BaseModel):
1179 1179 __tablename__ = 'user_to_notification'
1180 1180 __table_args__ = (
1181 1181 UniqueConstraint('user_id', 'notification_id'),
1182 1182 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1183 1183 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1184 1184 )
1185 1185 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1186 1186 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1187 1187 read = Column('read', Boolean, default=False)
1188 1188 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1189 1189
1190 1190 user = relationship('User', lazy="joined")
1191 1191 notification = relationship('Notification', lazy="joined",
1192 1192 order_by=lambda: Notification.created_on.desc(),)
1193 1193
1194 1194
1195 1195 class Gist(Base, BaseModel):
1196 1196 __tablename__ = 'gists'
1197 1197 __table_args__ = (
1198 1198 Index('g_gist_access_id_idx', 'gist_access_id'),
1199 1199 Index('g_created_on_idx', 'created_on'),
1200 1200 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1201 1201 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1202 1202 )
1203 1203 GIST_PUBLIC = u'public'
1204 1204 GIST_PRIVATE = u'private'
1205 1205 DEFAULT_FILENAME = u'gistfile1.txt'
1206 1206
1207 1207 gist_id = Column('gist_id', Integer(), primary_key=True)
1208 1208 gist_access_id = Column('gist_access_id', Unicode(250))
1209 1209 gist_description = Column('gist_description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
1210 1210 gist_owner = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=True)
1211 1211 gist_expires = Column('gist_expires', Float(53), nullable=False)
1212 1212 gist_type = Column('gist_type', Unicode(128), nullable=False)
1213 1213 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1214 1214 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1215 1215
1216 1216 owner = relationship('User')
1217 1217
1218 1218 def __repr__(self):
1219 1219 return '<Gist:[%s]%s>' % (self.gist_type, self.gist_access_id)
1220 1220
1221 1221
1222 1222 class DbMigrateVersion(Base, BaseModel):
1223 1223 __tablename__ = 'db_migrate_version'
1224 1224 __table_args__ = (
1225 1225 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1226 1226 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1227 1227 )
1228 1228 repository_id = Column('repository_id', String(250), primary_key=True)
1229 1229 repository_path = Column('repository_path', Text)
1230 1230 version = Column('version', Integer)
@@ -1,1241 +1,1241 b''
1 1
2 2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 3 #
4 4 # This program is free software: you can redistribute it and/or modify
5 5 # it under the terms of the GNU Affero General Public License, version 3
6 6 # (only), as published by the Free Software Foundation.
7 7 #
8 8 # This program is distributed in the hope that it will be useful,
9 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 11 # GNU General Public License for more details.
12 12 #
13 13 # You should have received a copy of the GNU Affero General Public License
14 14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 15 #
16 16 # This program is dual-licensed. If you wish to learn more about the
17 17 # RhodeCode Enterprise Edition, including its added features, Support services,
18 18 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 19
20 20 import os
21 21 import time
22 22 import logging
23 23 import datetime
24 24 import traceback
25 25 import hashlib
26 26 import collections
27 27 import functools
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 remove_prefix, time_to_datetime, aslist, Optional, safe_int, \
46 46 get_clone_url
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.items():
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 __tablename__ = 'rhodecode_settings'
154 154 __table_args__ = (
155 155 UniqueConstraint('app_settings_name'),
156 156 {'extend_existing': True, 'mysql_engine': 'InnoDB',
157 157 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
158 158 )
159 159
160 160 SETTINGS_TYPES = {
161 161 'str': safe_str,
162 162 'int': safe_int,
163 163 'unicode': safe_str,
164 164 'bool': str2bool,
165 165 'list': functools.partial(aslist, sep=',')
166 166 }
167 167 DEFAULT_UPDATE_URL = 'https://rhodecode.com/api/v1/info/versions'
168 168
169 169 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
170 170 app_settings_name = Column("app_settings_name", String(255), nullable=True, unique=None, default=None)
171 171 _app_settings_value = Column("app_settings_value", String(4096), nullable=True, unique=None, default=None)
172 172 _app_settings_type = Column("app_settings_type", String(255), nullable=True, unique=None, default=None)
173 173
174 174 def __init__(self, key='', val='', type='unicode'):
175 175 self.app_settings_name = key
176 176 self.app_settings_value = val
177 177 self.app_settings_type = type
178 178
179 179 @validates('_app_settings_value')
180 180 def validate_settings_value(self, key, val):
181 181 assert type(val) == str
182 182 return val
183 183
184 184 @hybrid_property
185 185 def app_settings_value(self):
186 186 v = self._app_settings_value
187 187 _type = self.app_settings_type
188 188 converter = self.SETTINGS_TYPES.get(_type) or self.SETTINGS_TYPES['unicode']
189 189 return converter(v)
190 190
191 191 @app_settings_value.setter
192 192 def app_settings_value(self, val):
193 193 """
194 194 Setter that will always make sure we use unicode in app_settings_value
195 195
196 196 :param val:
197 197 """
198 198 self._app_settings_value = safe_str(val)
199 199
200 200 @hybrid_property
201 201 def app_settings_type(self):
202 202 return self._app_settings_type
203 203
204 204 @app_settings_type.setter
205 205 def app_settings_type(self, val):
206 206 if val not in self.SETTINGS_TYPES:
207 207 raise Exception('type must be one of %s got %s'
208 208 % (self.SETTINGS_TYPES.keys(), val))
209 209 self._app_settings_type = val
210 210
211 211 def __unicode__(self):
212 212 return u"<%s('%s:%s[%s]')>" % (
213 213 self.__class__.__name__,
214 214 self.app_settings_name, self.app_settings_value, self.app_settings_type
215 215 )
216 216
217 217
218 218 class RhodeCodeUi(Base, BaseModel):
219 219 __tablename__ = 'rhodecode_ui'
220 220 __table_args__ = (
221 221 UniqueConstraint('ui_key'),
222 222 {'extend_existing': True, 'mysql_engine': 'InnoDB',
223 223 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
224 224 )
225 225
226 226 HOOK_REPO_SIZE = 'changegroup.repo_size'
227 227 HOOK_PUSH = 'changegroup.push_logger'
228 228 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
229 229 HOOK_PULL = 'outgoing.pull_logger'
230 230 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
231 231
232 232 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
233 233 ui_section = Column("ui_section", String(255), nullable=True, unique=None, default=None)
234 234 ui_key = Column("ui_key", String(255), nullable=True, unique=None, default=None)
235 235 ui_value = Column("ui_value", String(255), nullable=True, unique=None, default=None)
236 236 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
237 237
238 238 def __repr__(self):
239 239 return '<%s[%s]%s=>%s]>' % (self.__class__.__name__, self.ui_section,
240 240 self.ui_key, self.ui_value)
241 241
242 242
243 243 class User(Base, BaseModel):
244 244 __tablename__ = 'users'
245 245 __table_args__ = (
246 246 UniqueConstraint('username'), UniqueConstraint('email'),
247 247 Index('u_username_idx', 'username'),
248 248 Index('u_email_idx', 'email'),
249 249 {'extend_existing': True, 'mysql_engine': 'InnoDB',
250 250 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
251 251 )
252 252 DEFAULT_USER = 'default'
253 253 DEFAULT_GRAVATAR_URL = 'https://secure.gravatar.com/avatar/{md5email}?d=identicon&s={size}'
254 254
255 255 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
256 256 username = Column("username", String(255), nullable=True, unique=None, default=None)
257 257 password = Column("password", String(255), nullable=True, unique=None, default=None)
258 258 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
259 259 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
260 260 name = Column("firstname", String(255), nullable=True, unique=None, default=None)
261 261 lastname = Column("lastname", String(255), nullable=True, unique=None, default=None)
262 262 _email = Column("email", String(255), nullable=True, unique=None, default=None)
263 263 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
264 264 extern_type = Column("extern_type", String(255), nullable=True, unique=None, default=None)
265 265 extern_name = Column("extern_name", String(255), nullable=True, unique=None, default=None)
266 266 api_key = Column("api_key", String(255), nullable=True, unique=None, default=None)
267 267 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
268 268 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
269 269 _user_data = Column("user_data", LargeBinary(), nullable=True) # JSON data
270 270
271 271 user_log = relationship('UserLog')
272 272 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
273 273
274 274 repositories = relationship('Repository')
275 275 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
276 276 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
277 277
278 278 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
279 279 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
280 280
281 281 group_member = relationship('UserGroupMember', cascade='all')
282 282
283 283 notifications = relationship('UserNotification', cascade='all')
284 284 # notifications assigned to this user
285 285 user_created_notifications = relationship('Notification', cascade='all')
286 286 # comments created by this user
287 287 user_comments = relationship('ChangesetComment', cascade='all')
288 288 user_emails = relationship('UserEmailMap', cascade='all')
289 289 user_auth_tokens = relationship('UserApiKeys', cascade='all')
290 290
291 291 @hybrid_property
292 292 def email(self):
293 293 return self._email
294 294
295 295 @email.setter
296 296 def email(self, val):
297 297 self._email = val.lower() if val else None
298 298
299 299 @property
300 300 def firstname(self):
301 301 # alias for future
302 302 return self.name
303 303
304 304 @property
305 305 def username_and_name(self):
306 306 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
307 307
308 308 @property
309 309 def full_name(self):
310 310 return '%s %s' % (self.firstname, self.lastname)
311 311
312 312 @property
313 313 def full_contact(self):
314 314 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
315 315
316 316 @property
317 317 def short_contact(self):
318 318 return '%s %s' % (self.firstname, self.lastname)
319 319
320 320 @property
321 321 def is_admin(self):
322 322 return self.admin
323 323
324 324 @classmethod
325 325 def get_by_username(cls, username, case_insensitive=False, cache=False):
326 326 if case_insensitive:
327 327 q = cls.query().filter(cls.username.ilike(username))
328 328 else:
329 329 q = cls.query().filter(cls.username == username)
330 330
331 331 if cache:
332 332 q = q.options(FromCache(
333 333 "sql_cache_short",
334 334 "get_user_%s" % _hash_key(username)))
335 335 return q.scalar()
336 336
337 337 @classmethod
338 338 def get_by_auth_token(cls, auth_token, cache=False, fallback=True):
339 339 q = cls.query().filter(cls.api_key == auth_token)
340 340
341 341 if cache:
342 342 q = q.options(FromCache("sql_cache_short",
343 343 "get_auth_token_%s" % auth_token))
344 344 res = q.scalar()
345 345
346 346 if fallback and not res:
347 347 #fallback to additional keys
348 348 _res = UserApiKeys.query()\
349 349 .filter(UserApiKeys.api_key == auth_token)\
350 350 .filter(or_(UserApiKeys.expires == -1,
351 351 UserApiKeys.expires >= time.time()))\
352 352 .first()
353 353 if _res:
354 354 res = _res.user
355 355 return res
356 356
357 357 @classmethod
358 358 def get_by_email(cls, email, case_insensitive=False, cache=False):
359 359
360 360 if case_insensitive:
361 361 q = cls.query().filter(cls.email.ilike(email))
362 362 else:
363 363 q = cls.query().filter(cls.email == email)
364 364
365 365 if cache:
366 366 q = q.options(FromCache("sql_cache_short",
367 367 "get_email_key_%s" % email))
368 368
369 369 ret = q.scalar()
370 370 if ret is None:
371 371 q = UserEmailMap.query()
372 372 # try fetching in alternate email map
373 373 if case_insensitive:
374 374 q = q.filter(UserEmailMap.email.ilike(email))
375 375 else:
376 376 q = q.filter(UserEmailMap.email == email)
377 377 q = q.options(joinedload(UserEmailMap.user))
378 378 if cache:
379 379 q = q.options(FromCache("sql_cache_short",
380 380 "get_email_map_key_%s" % email))
381 381 ret = getattr(q.scalar(), 'user', None)
382 382
383 383 return ret
384 384
385 385 @classmethod
386 386 def get_first_admin(cls):
387 387 user = User.query().filter(User.admin == True).first()
388 388 if user is None:
389 389 raise Exception('Missing administrative account!')
390 390 return user
391 391
392 392 @classmethod
393 393 def get_default_user(cls, cache=False):
394 394 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
395 395 if user is None:
396 396 raise Exception('Missing default account!')
397 397 return user
398 398
399 399
400 400 class UserApiKeys(Base, BaseModel):
401 401 __tablename__ = 'user_api_keys'
402 402 __table_args__ = (
403 403 Index('uak_api_key_idx', 'api_key'),
404 404 Index('uak_api_key_expires_idx', 'api_key', 'expires'),
405 405 UniqueConstraint('api_key'),
406 406 {'extend_existing': True, 'mysql_engine': 'InnoDB',
407 407 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
408 408 )
409 409
410 410
411 411 # ApiKey role
412 412 ROLE_ALL = 'token_role_all'
413 413 ROLE_HTTP = 'token_role_http'
414 414 ROLE_VCS = 'token_role_vcs'
415 415 ROLE_API = 'token_role_api'
416 416 ROLES = [ROLE_ALL, ROLE_HTTP, ROLE_VCS, ROLE_API]
417 417
418 418 user_api_key_id = Column("user_api_key_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
419 419 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
420 420 api_key = Column("api_key", String(255), nullable=False, unique=True)
421 421 description = Column('description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
422 422 expires = Column('expires', Float(53), nullable=False)
423 423 role = Column('role', String(255), nullable=True)
424 424 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
425 425
426 426 user = relationship('User', lazy='joined')
427 427
428 428
429 429 class UserEmailMap(Base, BaseModel):
430 430 __tablename__ = 'user_email_map'
431 431 __table_args__ = (
432 432 Index('uem_email_idx', 'email'),
433 433 UniqueConstraint('email'),
434 434 {'extend_existing': True, 'mysql_engine': 'InnoDB',
435 435 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
436 436 )
437 437
438 438
439 439 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
440 440 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
441 441 _email = Column("email", String(255), nullable=True, unique=False, default=None)
442 442 user = relationship('User', lazy='joined')
443 443
444 444 @validates('_email')
445 445 def validate_email(self, key, email):
446 446 # check if this email is not main one
447 447 main_email = Session().query(User).filter(User.email == email).scalar()
448 448 if main_email is not None:
449 449 raise AttributeError('email %s is present is user table' % email)
450 450 return email
451 451
452 452 @hybrid_property
453 453 def email(self):
454 454 return self._email
455 455
456 456 @email.setter
457 457 def email(self, val):
458 458 self._email = val.lower() if val else None
459 459
460 460
461 461 class UserIpMap(Base, BaseModel):
462 462 __tablename__ = 'user_ip_map'
463 463 __table_args__ = (
464 464 UniqueConstraint('user_id', 'ip_addr'),
465 465 {'extend_existing': True, 'mysql_engine': 'InnoDB',
466 466 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
467 467 )
468 468
469 469
470 470 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
471 471 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
472 472 ip_addr = Column("ip_addr", String(255), nullable=True, unique=False, default=None)
473 473 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
474 474 user = relationship('User', lazy='joined')
475 475
476 476 def __unicode__(self):
477 477 return u"<%s('user_id:%s=>%s')>" % (self.__class__.__name__,
478 478 self.user_id, self.ip_addr)
479 479
480 480
481 481 class UserLog(Base, BaseModel):
482 482 __tablename__ = 'user_logs'
483 483 __table_args__ = (
484 484 {'extend_existing': True, 'mysql_engine': 'InnoDB',
485 485 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
486 486 )
487 487 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
488 488 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
489 489 username = Column("username", String(255), nullable=True, unique=None, default=None)
490 490 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
491 491 repository_name = Column("repository_name", String(255), nullable=True, unique=None, default=None)
492 492 user_ip = Column("user_ip", String(255), nullable=True, unique=None, default=None)
493 493 action = Column("action", String(1200000), nullable=True, unique=None, default=None)
494 494 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
495 495
496 496 def __unicode__(self):
497 497 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
498 498 self.repository_name,
499 499 self.action)
500 500
501 501 user = relationship('User')
502 502 repository = relationship('Repository', cascade='')
503 503
504 504
505 505 class UserGroup(Base, BaseModel):
506 506 __tablename__ = 'users_groups'
507 507 __table_args__ = (
508 508 {'extend_existing': True, 'mysql_engine': 'InnoDB',
509 509 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
510 510 )
511 511
512 512 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
513 513 users_group_name = Column("users_group_name", String(255), nullable=False, unique=True, default=None)
514 514 user_group_description = Column("user_group_description", String(10000), nullable=True, unique=None, default=None)
515 515 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
516 516 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
517 517 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
518 518 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
519 519 _group_data = Column("group_data", LargeBinary(), nullable=True) # JSON data
520 520
521 521 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
522 522 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
523 523 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
524 524 users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
525 525 user_user_group_to_perm = relationship('UserUserGroupToPerm', cascade='all')
526 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm ', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
526 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
527 527
528 528 user = relationship('User')
529 529
530 530 def __unicode__(self):
531 531 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
532 532 self.users_group_id,
533 533 self.users_group_name)
534 534
535 535 @classmethod
536 536 def get_by_group_name(cls, group_name, cache=False,
537 537 case_insensitive=False):
538 538 if case_insensitive:
539 539 q = cls.query().filter(cls.users_group_name.ilike(group_name))
540 540 else:
541 541 q = cls.query().filter(cls.users_group_name == group_name)
542 542 if cache:
543 543 q = q.options(FromCache(
544 544 "sql_cache_short",
545 545 "get_user_%s" % _hash_key(group_name)))
546 546 return q.scalar()
547 547
548 548 @classmethod
549 549 def get(cls, user_group_id, cache=False):
550 550 user_group = cls.query()
551 551 if cache:
552 552 user_group = user_group.options(FromCache("sql_cache_short",
553 553 "get_users_group_%s" % user_group_id))
554 554 return user_group.get(user_group_id)
555 555
556 556
557 557 class UserGroupMember(Base, BaseModel):
558 558 __tablename__ = 'users_groups_members'
559 559 __table_args__ = (
560 560 {'extend_existing': True, 'mysql_engine': 'InnoDB',
561 561 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
562 562 )
563 563
564 564 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
565 565 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
566 566 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
567 567
568 568 user = relationship('User', lazy='joined')
569 569 users_group = relationship('UserGroup')
570 570
571 571 def __init__(self, gr_id='', u_id=''):
572 572 self.users_group_id = gr_id
573 573 self.user_id = u_id
574 574
575 575
576 576 class RepositoryField(Base, BaseModel):
577 577 __tablename__ = 'repositories_fields'
578 578 __table_args__ = (
579 579 UniqueConstraint('repository_id', 'field_key'), # no-multi field
580 580 {'extend_existing': True, 'mysql_engine': 'InnoDB',
581 581 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
582 582 )
583 583 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
584 584
585 585 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
586 586 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
587 587 field_key = Column("field_key", String(250))
588 588 field_label = Column("field_label", String(1024), nullable=False)
589 589 field_value = Column("field_value", String(10000), nullable=False)
590 590 field_desc = Column("field_desc", String(1024), nullable=False)
591 591 field_type = Column("field_type", String(256), nullable=False, unique=None)
592 592 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
593 593
594 594 repository = relationship('Repository')
595 595
596 596 @classmethod
597 597 def get_by_key_name(cls, key, repo):
598 598 row = cls.query()\
599 599 .filter(cls.repository == repo)\
600 600 .filter(cls.field_key == key).scalar()
601 601 return row
602 602
603 603
604 604 class Repository(Base, BaseModel):
605 605 __tablename__ = 'repositories'
606 606 __table_args__ = (
607 607 UniqueConstraint('repo_name'),
608 608 Index('r_repo_name_idx', 'repo_name'),
609 609 {'extend_existing': True, 'mysql_engine': 'InnoDB',
610 610 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
611 611 )
612 612 DEFAULT_CLONE_URI = '{scheme}://{user}@{netloc}/{repo}'
613 613 DEFAULT_CLONE_URI_ID = '{scheme}://{user}@{netloc}/_{repoid}'
614 614
615 615 STATE_CREATED = 'repo_state_created'
616 616 STATE_PENDING = 'repo_state_pending'
617 617 STATE_ERROR = 'repo_state_error'
618 618
619 619 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
620 620 repo_name = Column("repo_name", String(255), nullable=False, unique=True, default=None)
621 621 repo_state = Column("repo_state", String(255), nullable=True)
622 622
623 623 clone_uri = Column("clone_uri", String(255), nullable=True, unique=False, default=None)
624 624 repo_type = Column("repo_type", String(255), nullable=False, unique=False, default=None)
625 625 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
626 626 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
627 627 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
628 628 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
629 629 description = Column("description", String(10000), nullable=True, unique=None, default=None)
630 630 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
631 631 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
632 632 _landing_revision = Column("landing_revision", String(255), nullable=False, unique=False, default=None)
633 633 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
634 634 _locked = Column("locked", String(255), nullable=True, unique=False, default=None)
635 635 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
636 636
637 637 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
638 638 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
639 639
640 640 user = relationship('User')
641 641 fork = relationship('Repository', remote_side=repo_id)
642 642 group = relationship('RepoGroup')
643 643 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
644 644 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
645 645 stats = relationship('Statistics', cascade='all', uselist=False)
646 646
647 647 followers = relationship('UserFollowing',
648 648 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
649 649 cascade='all')
650 650 extra_fields = relationship('RepositoryField',
651 651 cascade="all, delete, delete-orphan")
652 652
653 653 logs = relationship('UserLog')
654 654 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
655 655
656 656 pull_requests_org = relationship('PullRequest',
657 657 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
658 658 cascade="all, delete, delete-orphan")
659 659
660 660 pull_requests_other = relationship('PullRequest',
661 661 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
662 662 cascade="all, delete, delete-orphan")
663 663
664 664 def __unicode__(self):
665 665 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
666 666 safe_str(self.repo_name))
667 667
668 668 @classmethod
669 669 def get_by_repo_name(cls, repo_name):
670 670 q = Session().query(cls).filter(cls.repo_name == repo_name)
671 671 q = q.options(joinedload(Repository.fork))\
672 672 .options(joinedload(Repository.user))\
673 673 .options(joinedload(Repository.group))
674 674 return q.scalar()
675 675
676 676
677 677 class RepoGroup(Base, BaseModel):
678 678 __tablename__ = 'groups'
679 679 __table_args__ = (
680 680 UniqueConstraint('group_name', 'group_parent_id'),
681 681 {'extend_existing': True, 'mysql_engine': 'InnoDB',
682 682 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
683 683 )
684 684
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 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
691 691 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
692 692 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
693 693
694 694 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
695 695 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
696 696 parent_group = relationship('RepoGroup', remote_side=group_id)
697 697 user = relationship('User')
698 698
699 699 def __init__(self, group_name='', parent_group=None):
700 700 self.group_name = group_name
701 701 self.parent_group = parent_group
702 702
703 703 def __unicode__(self):
704 704 return u"<%s('id:%s:%s')>" % (self.__class__.__name__, self.group_id,
705 705 self.group_name)
706 706
707 707 @classmethod
708 708 def url_sep(cls):
709 709 return URL_SEP
710 710
711 711 @classmethod
712 712 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
713 713 if case_insensitive:
714 714 gr = cls.query()\
715 715 .filter(cls.group_name.ilike(group_name))
716 716 else:
717 717 gr = cls.query()\
718 718 .filter(cls.group_name == group_name)
719 719 if cache:
720 720 gr = gr.options(FromCache(
721 721 "sql_cache_short",
722 722 "get_group_%s" % _hash_key(group_name)))
723 723 return gr.scalar()
724 724
725 725
726 726 class Permission(Base, BaseModel):
727 727 __tablename__ = 'permissions'
728 728 __table_args__ = (
729 729 Index('p_perm_name_idx', 'permission_name'),
730 730 {'extend_existing': True, 'mysql_engine': 'InnoDB',
731 731 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
732 732 )
733 733 PERMS = [
734 734 ('hg.admin', _('RhodeCode Administrator')),
735 735
736 736 ('repository.none', _('Repository no access')),
737 737 ('repository.read', _('Repository read access')),
738 738 ('repository.write', _('Repository write access')),
739 739 ('repository.admin', _('Repository admin access')),
740 740
741 741 ('group.none', _('Repository group no access')),
742 742 ('group.read', _('Repository group read access')),
743 743 ('group.write', _('Repository group write access')),
744 744 ('group.admin', _('Repository group admin access')),
745 745
746 746 ('usergroup.none', _('User group no access')),
747 747 ('usergroup.read', _('User group read access')),
748 748 ('usergroup.write', _('User group write access')),
749 749 ('usergroup.admin', _('User group admin access')),
750 750
751 751 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
752 752 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
753 753
754 754 ('hg.usergroup.create.false', _('User Group creation disabled')),
755 755 ('hg.usergroup.create.true', _('User Group creation enabled')),
756 756
757 757 ('hg.create.none', _('Repository creation disabled')),
758 758 ('hg.create.repository', _('Repository creation enabled')),
759 759 ('hg.create.write_on_repogroup.true', _('Repository creation enabled with write permission to a repository group')),
760 760 ('hg.create.write_on_repogroup.false', _('Repository creation disabled with write permission to a repository group')),
761 761
762 762 ('hg.fork.none', _('Repository forking disabled')),
763 763 ('hg.fork.repository', _('Repository forking enabled')),
764 764
765 765 ('hg.register.none', _('Registration disabled')),
766 766 ('hg.register.manual_activate', _('User Registration with manual account activation')),
767 767 ('hg.register.auto_activate', _('User Registration with automatic account activation')),
768 768
769 769 ('hg.extern_activate.manual', _('Manual activation of external account')),
770 770 ('hg.extern_activate.auto', _('Automatic activation of external account')),
771 771
772 772 ]
773 773
774 774 #definition of system default permissions for DEFAULT user
775 775 DEFAULT_USER_PERMISSIONS = [
776 776 'repository.read',
777 777 'group.read',
778 778 'usergroup.read',
779 779 'hg.create.repository',
780 780 'hg.create.write_on_repogroup.true',
781 781 'hg.fork.repository',
782 782 'hg.register.manual_activate',
783 783 'hg.extern_activate.auto',
784 784 ]
785 785
786 786 # defines which permissions are more important higher the more important
787 787 # Weight defines which permissions are more important.
788 788 # The higher number the more important.
789 789 PERM_WEIGHTS = {
790 790 'repository.none': 0,
791 791 'repository.read': 1,
792 792 'repository.write': 3,
793 793 'repository.admin': 4,
794 794
795 795 'group.none': 0,
796 796 'group.read': 1,
797 797 'group.write': 3,
798 798 'group.admin': 4,
799 799
800 800 'usergroup.none': 0,
801 801 'usergroup.read': 1,
802 802 'usergroup.write': 3,
803 803 'usergroup.admin': 4,
804 804 'hg.repogroup.create.false': 0,
805 805 'hg.repogroup.create.true': 1,
806 806
807 807 'hg.usergroup.create.false': 0,
808 808 'hg.usergroup.create.true': 1,
809 809
810 810 'hg.fork.none': 0,
811 811 'hg.fork.repository': 1,
812 812 'hg.create.none': 0,
813 813 'hg.create.repository': 1
814 814 }
815 815
816 816 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
817 817 permission_name = Column("permission_name", String(255), nullable=True, unique=None, default=None)
818 818 permission_longname = Column("permission_longname", String(255), nullable=True, unique=None, default=None)
819 819
820 820 def __unicode__(self):
821 821 return u"<%s('%s:%s')>" % (
822 822 self.__class__.__name__, self.permission_id, self.permission_name
823 823 )
824 824
825 825 @classmethod
826 826 def get_by_key(cls, key):
827 827 return cls.query().filter(cls.permission_name == key).scalar()
828 828
829 829
830 830 class UserRepoToPerm(Base, BaseModel):
831 831 __tablename__ = 'repo_to_perm'
832 832 __table_args__ = (
833 833 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
834 834 {'extend_existing': True, 'mysql_engine': 'InnoDB',
835 835 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
836 836 )
837 837 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
838 838 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
839 839 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
840 840 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
841 841
842 842 user = relationship('User')
843 843 repository = relationship('Repository')
844 844 permission = relationship('Permission')
845 845
846 846 def __unicode__(self):
847 847 return u'<%s => %s >' % (self.user, self.repository)
848 848
849 849
850 850 class UserUserGroupToPerm(Base, BaseModel):
851 851 __tablename__ = 'user_user_group_to_perm'
852 852 __table_args__ = (
853 853 UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
854 854 {'extend_existing': True, 'mysql_engine': 'InnoDB',
855 855 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
856 856 )
857 857 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
858 858 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
859 859 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
860 860 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
861 861
862 862 user = relationship('User')
863 863 user_group = relationship('UserGroup')
864 864 permission = relationship('Permission')
865 865
866 866 def __unicode__(self):
867 867 return u'<%s => %s >' % (self.user, self.user_group)
868 868
869 869
870 870 class UserToPerm(Base, BaseModel):
871 871 __tablename__ = 'user_to_perm'
872 872 __table_args__ = (
873 873 UniqueConstraint('user_id', 'permission_id'),
874 874 {'extend_existing': True, 'mysql_engine': 'InnoDB',
875 875 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
876 876 )
877 877 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
878 878 user_id = Column("user_id", Integer(), ForeignKey('users.user_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 user = relationship('User')
882 882 permission = relationship('Permission', lazy='joined')
883 883
884 884 def __unicode__(self):
885 885 return u'<%s => %s >' % (self.user, self.permission)
886 886
887 887
888 888 class UserGroupRepoToPerm(Base, BaseModel):
889 889 __tablename__ = 'users_group_repo_to_perm'
890 890 __table_args__ = (
891 891 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
892 892 {'extend_existing': True, 'mysql_engine': 'InnoDB',
893 893 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
894 894 )
895 895 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
896 896 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
897 897 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
898 898 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
899 899
900 900 users_group = relationship('UserGroup')
901 901 permission = relationship('Permission')
902 902 repository = relationship('Repository')
903 903
904 904 def __unicode__(self):
905 905 return u'<UserGroupRepoToPerm:%s => %s >' % (self.users_group, self.repository)
906 906
907 907
908 908 class UserGroupUserGroupToPerm(Base, BaseModel):
909 909 __tablename__ = 'user_group_user_group_to_perm'
910 910 __table_args__ = (
911 911 UniqueConstraint('target_user_group_id', 'user_group_id', 'permission_id'),
912 912 CheckConstraint('target_user_group_id != user_group_id'),
913 913 {'extend_existing': True, 'mysql_engine': 'InnoDB',
914 914 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
915 915 )
916 916 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)
917 917 target_user_group_id = Column("target_user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
918 918 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
919 919 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
920 920
921 921 target_user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
922 922 user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
923 923 permission = relationship('Permission')
924 924
925 925 def __unicode__(self):
926 926 return u'<UserGroupUserGroup:%s => %s >' % (self.target_user_group, self.user_group)
927 927
928 928
929 929 class UserGroupToPerm(Base, BaseModel):
930 930 __tablename__ = 'users_group_to_perm'
931 931 __table_args__ = (
932 932 UniqueConstraint('users_group_id', 'permission_id',),
933 933 {'extend_existing': True, 'mysql_engine': 'InnoDB',
934 934 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
935 935 )
936 936 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
937 937 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
938 938 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
939 939
940 940 users_group = relationship('UserGroup')
941 941 permission = relationship('Permission')
942 942
943 943
944 944 class UserRepoGroupToPerm(Base, BaseModel):
945 945 __tablename__ = 'user_repo_group_to_perm'
946 946 __table_args__ = (
947 947 UniqueConstraint('user_id', 'group_id', 'permission_id'),
948 948 {'extend_existing': True, 'mysql_engine': 'InnoDB',
949 949 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
950 950 )
951 951
952 952 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
953 953 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
954 954 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
955 955 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
956 956
957 957 user = relationship('User')
958 958 group = relationship('RepoGroup')
959 959 permission = relationship('Permission')
960 960
961 961
962 962 class UserGroupRepoGroupToPerm(Base, BaseModel):
963 963 __tablename__ = 'users_group_repo_group_to_perm'
964 964 __table_args__ = (
965 965 UniqueConstraint('users_group_id', 'group_id'),
966 966 {'extend_existing': True, 'mysql_engine': 'InnoDB',
967 967 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
968 968 )
969 969
970 970 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)
971 971 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
972 972 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
973 973 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
974 974
975 975 users_group = relationship('UserGroup')
976 976 permission = relationship('Permission')
977 977 group = relationship('RepoGroup')
978 978
979 979 @classmethod
980 980 def create(cls, user_group, repository_group, permission):
981 981 n = cls()
982 982 n.users_group = user_group
983 983 n.group = repository_group
984 984 n.permission = permission
985 985 Session().add(n)
986 986 return n
987 987
988 988
989 989 class Statistics(Base, BaseModel):
990 990 __tablename__ = 'statistics'
991 991 __table_args__ = (
992 992 UniqueConstraint('repository_id'),
993 993 {'extend_existing': True, 'mysql_engine': 'InnoDB',
994 994 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
995 995 )
996 996 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
997 997 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
998 998 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
999 999 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1000 1000 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1001 1001 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1002 1002
1003 1003 repository = relationship('Repository', single_parent=True)
1004 1004
1005 1005
1006 1006 class UserFollowing(Base, BaseModel):
1007 1007 __tablename__ = 'user_followings'
1008 1008 __table_args__ = (
1009 1009 UniqueConstraint('user_id', 'follows_repository_id'),
1010 1010 UniqueConstraint('user_id', 'follows_user_id'),
1011 1011 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1012 1012 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1013 1013 )
1014 1014
1015 1015 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1016 1016 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1017 1017 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1018 1018 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1019 1019 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1020 1020
1021 1021 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1022 1022
1023 1023 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1024 1024 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1025 1025
1026 1026
1027 1027 class CacheInvalidation(Base, BaseModel):
1028 1028 __tablename__ = 'cache_invalidation'
1029 1029 __table_args__ = (
1030 1030 UniqueConstraint('cache_key'),
1031 1031 Index('key_idx', 'cache_key'),
1032 1032 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1033 1033 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1034 1034 )
1035 1035 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1036 1036 cache_key = Column("cache_key", String(255), nullable=True, unique=None, default=None)
1037 1037 cache_args = Column("cache_args", String(255), nullable=True, unique=None, default=None)
1038 1038 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1039 1039
1040 1040 def __init__(self, cache_key, cache_args=''):
1041 1041 self.cache_key = cache_key
1042 1042 self.cache_args = cache_args
1043 1043 self.cache_active = False
1044 1044
1045 1045
1046 1046 class ChangesetComment(Base, BaseModel):
1047 1047 __tablename__ = 'changeset_comments'
1048 1048 __table_args__ = (
1049 1049 Index('cc_revision_idx', 'revision'),
1050 1050 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1051 1051 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1052 1052 )
1053 1053 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1054 1054 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1055 1055 revision = Column('revision', String(40), nullable=True)
1056 1056 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1057 1057 line_no = Column('line_no', Unicode(10), nullable=True)
1058 1058 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1059 1059 f_path = Column('f_path', Unicode(1000), nullable=True)
1060 1060 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1061 1061 text = Column('text', UnicodeText().with_variant(UnicodeText(25000), 'mysql'), nullable=False)
1062 1062 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1063 1063 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1064 1064
1065 1065 author = relationship('User', lazy='joined')
1066 1066 repo = relationship('Repository')
1067 1067 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1068 1068 pull_request = relationship('PullRequest', lazy='joined')
1069 1069
1070 1070
1071 1071 class ChangesetStatus(Base, BaseModel):
1072 1072 __tablename__ = 'changeset_statuses'
1073 1073 __table_args__ = (
1074 1074 Index('cs_revision_idx', 'revision'),
1075 1075 Index('cs_version_idx', 'version'),
1076 1076 UniqueConstraint('repo_id', 'revision', 'version'),
1077 1077 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1078 1078 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1079 1079 )
1080 1080 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1081 1081 STATUS_APPROVED = 'approved'
1082 1082 STATUS_REJECTED = 'rejected'
1083 1083 STATUS_UNDER_REVIEW = 'under_review'
1084 1084
1085 1085 STATUSES = [
1086 1086 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1087 1087 (STATUS_APPROVED, _("Approved")),
1088 1088 (STATUS_REJECTED, _("Rejected")),
1089 1089 (STATUS_UNDER_REVIEW, _("Under Review")),
1090 1090 ]
1091 1091
1092 1092 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1093 1093 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1094 1094 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1095 1095 revision = Column('revision', String(40), nullable=False)
1096 1096 status = Column('status', String(128), nullable=False, default=DEFAULT)
1097 1097 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1098 1098 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1099 1099 version = Column('version', Integer(), nullable=False, default=0)
1100 1100 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1101 1101
1102 1102 author = relationship('User', lazy='joined')
1103 1103 repo = relationship('Repository')
1104 1104 comment = relationship('ChangesetComment', lazy='joined')
1105 1105 pull_request = relationship('PullRequest', lazy='joined')
1106 1106
1107 1107
1108 1108
1109 1109 class PullRequest(Base, BaseModel):
1110 1110 __tablename__ = 'pull_requests'
1111 1111 __table_args__ = (
1112 1112 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1113 1113 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1114 1114 )
1115 1115
1116 1116 STATUS_NEW = u'new'
1117 1117 STATUS_OPEN = u'open'
1118 1118 STATUS_CLOSED = u'closed'
1119 1119
1120 1120 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1121 1121 title = Column('title', Unicode(256), nullable=True)
1122 1122 description = Column('description', UnicodeText().with_variant(UnicodeText(10240), 'mysql'), nullable=True)
1123 1123 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1124 1124 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1125 1125 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1126 1126 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1127 1127 _revisions = Column('revisions', UnicodeText().with_variant(UnicodeText(20500), 'mysql'))
1128 1128 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1129 1129 org_ref = Column('org_ref', Unicode(256), nullable=False)
1130 1130 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1131 1131 other_ref = Column('other_ref', Unicode(256), nullable=False)
1132 1132
1133 1133 author = relationship('User', lazy='joined')
1134 1134 reviewers = relationship('PullRequestReviewers',
1135 1135 cascade="all, delete, delete-orphan")
1136 1136 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1137 1137 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1138 1138 statuses = relationship('ChangesetStatus')
1139 1139 comments = relationship('ChangesetComment',
1140 1140 cascade="all, delete, delete-orphan")
1141 1141
1142 1142
1143 1143 class PullRequestReviewers(Base, BaseModel):
1144 1144 __tablename__ = 'pull_request_reviewers'
1145 1145 __table_args__ = (
1146 1146 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1147 1147 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1148 1148 )
1149 1149
1150 1150 def __init__(self, user=None, pull_request=None):
1151 1151 self.user = user
1152 1152 self.pull_request = pull_request
1153 1153
1154 1154 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1155 1155 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1156 1156 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1157 1157
1158 1158 user = relationship('User')
1159 1159 pull_request = relationship('PullRequest')
1160 1160
1161 1161
1162 1162 class Notification(Base, BaseModel):
1163 1163 __tablename__ = 'notifications'
1164 1164 __table_args__ = (
1165 1165 Index('notification_type_idx', 'type'),
1166 1166 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1167 1167 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1168 1168 )
1169 1169
1170 1170 TYPE_CHANGESET_COMMENT = u'cs_comment'
1171 1171 TYPE_MESSAGE = u'message'
1172 1172 TYPE_MENTION = u'mention'
1173 1173 TYPE_REGISTRATION = u'registration'
1174 1174 TYPE_PULL_REQUEST = u'pull_request'
1175 1175 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1176 1176
1177 1177 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1178 1178 subject = Column('subject', Unicode(512), nullable=True)
1179 1179 body = Column('body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True)
1180 1180 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1181 1181 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1182 1182 type_ = Column('type', Unicode(256))
1183 1183
1184 1184 created_by_user = relationship('User')
1185 1185 notifications_to_users = relationship('UserNotification', lazy='joined',
1186 1186 cascade="all, delete, delete-orphan")
1187 1187
1188 1188
1189 1189 class UserNotification(Base, BaseModel):
1190 1190 __tablename__ = 'user_to_notification'
1191 1191 __table_args__ = (
1192 1192 UniqueConstraint('user_id', 'notification_id'),
1193 1193 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1194 1194 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1195 1195 )
1196 1196 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1197 1197 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1198 1198 read = Column('read', Boolean, default=False)
1199 1199 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1200 1200
1201 1201 user = relationship('User', lazy="joined")
1202 1202 notification = relationship('Notification', lazy="joined",
1203 1203 order_by=lambda: Notification.created_on.desc(),)
1204 1204
1205 1205
1206 1206 class Gist(Base, BaseModel):
1207 1207 __tablename__ = 'gists'
1208 1208 __table_args__ = (
1209 1209 Index('g_gist_access_id_idx', 'gist_access_id'),
1210 1210 Index('g_created_on_idx', 'created_on'),
1211 1211 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1212 1212 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1213 1213 )
1214 1214 GIST_PUBLIC = u'public'
1215 1215 GIST_PRIVATE = u'private'
1216 1216 DEFAULT_FILENAME = u'gistfile1.txt'
1217 1217
1218 1218 gist_id = Column('gist_id', Integer(), primary_key=True)
1219 1219 gist_access_id = Column('gist_access_id', Unicode(250))
1220 1220 gist_description = Column('gist_description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
1221 1221 gist_owner = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=True)
1222 1222 gist_expires = Column('gist_expires', Float(53), nullable=False)
1223 1223 gist_type = Column('gist_type', Unicode(128), nullable=False)
1224 1224 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1225 1225 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1226 1226
1227 1227 owner = relationship('User')
1228 1228
1229 1229 def __repr__(self):
1230 1230 return '<Gist:[%s]%s>' % (self.gist_type, self.gist_access_id)
1231 1231
1232 1232
1233 1233 class DbMigrateVersion(Base, BaseModel):
1234 1234 __tablename__ = 'db_migrate_version'
1235 1235 __table_args__ = (
1236 1236 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1237 1237 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1238 1238 )
1239 1239 repository_id = Column('repository_id', String(250), primary_key=True)
1240 1240 repository_path = Column('repository_path', Text)
1241 1241 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
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