##// END OF EJS Templates
migration: remove usage of pylons inside migration schemas and code.
marcink -
r2096:5c18693f default
parent child Browse files
Show More

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

@@ -1,19 +1,27 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21
22 def url(*args, **kwargs):
23 """
24 Dummy url generator to be used inside the old db migration schemas that rely on it.
25 It would protect from errors after removal of pylons.
26 """
27 return '/'
@@ -1,973 +1,972 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21
22 22 import os
23 23 import time
24 24 import logging
25 25 import datetime
26 26 import traceback
27 27 import hashlib
28 28 import collections
29 29
30 30 from sqlalchemy import *
31 31 from sqlalchemy.ext.hybrid import hybrid_property
32 32 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
33 33 from sqlalchemy.exc import DatabaseError
34 34 from beaker.cache import cache_region, region_invalidate
35 35 from webob.exc import HTTPNotFound
36 36
37 from pylons.i18n.translation import lazy_ugettext as _
38
37 from rhodecode.translation import _
39 38 from rhodecode.lib.vcs import get_backend
40 39 from rhodecode.lib.vcs.utils.helpers import get_scm
41 40 from rhodecode.lib.vcs.exceptions import VCSError
42 41 from zope.cachedescriptors.property import Lazy as LazyProperty
43 42
44 43 from rhodecode.lib.utils2 import str2bool, safe_str, get_commit_safe, \
45 44 safe_unicode, remove_suffix
46 45 from rhodecode.lib.ext_json import json
47 46 from rhodecode.lib.caching_query import FromCache
48 47
49 48 from rhodecode.model.meta import Base, Session
50 49
51 50 URL_SEP = '/'
52 51 log = logging.getLogger(__name__)
53 52
54 53 #==============================================================================
55 54 # BASE CLASSES
56 55 #==============================================================================
57 56
58 57 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
59 58
60 59
61 60 class BaseModel(object):
62 61 """
63 62 Base Model for all classes
64 63 """
65 64
66 65 @classmethod
67 66 def _get_keys(cls):
68 67 """return column names for this model """
69 68 return class_mapper(cls).c.keys()
70 69
71 70 def get_dict(self):
72 71 """
73 72 return dict with keys and values corresponding
74 73 to this model data """
75 74
76 75 d = {}
77 76 for k in self._get_keys():
78 77 d[k] = getattr(self, k)
79 78
80 79 # also use __json__() if present to get additional fields
81 80 _json_attr = getattr(self, '__json__', None)
82 81 if _json_attr:
83 82 # update with attributes from __json__
84 83 if callable(_json_attr):
85 84 _json_attr = _json_attr()
86 85 for k, val in _json_attr.iteritems():
87 86 d[k] = val
88 87 return d
89 88
90 89 def get_appstruct(self):
91 90 """return list with keys and values tupples corresponding
92 91 to this model data """
93 92
94 93 l = []
95 94 for k in self._get_keys():
96 95 l.append((k, getattr(self, k),))
97 96 return l
98 97
99 98 def populate_obj(self, populate_dict):
100 99 """populate model with data from given populate_dict"""
101 100
102 101 for k in self._get_keys():
103 102 if k in populate_dict:
104 103 setattr(self, k, populate_dict[k])
105 104
106 105 @classmethod
107 106 def query(cls):
108 107 return Session().query(cls)
109 108
110 109 @classmethod
111 110 def get(cls, id_):
112 111 if id_:
113 112 return cls.query().get(id_)
114 113
115 114 @classmethod
116 115 def get_or_404(cls, id_):
117 116 try:
118 117 id_ = int(id_)
119 118 except (TypeError, ValueError):
120 119 raise HTTPNotFound
121 120
122 121 res = cls.query().get(id_)
123 122 if not res:
124 123 raise HTTPNotFound
125 124 return res
126 125
127 126 @classmethod
128 127 def getAll(cls):
129 128 return cls.query().all()
130 129
131 130 @classmethod
132 131 def delete(cls, id_):
133 132 obj = cls.query().get(id_)
134 133 Session().delete(obj)
135 134
136 135 def __repr__(self):
137 136 if hasattr(self, '__unicode__'):
138 137 # python repr needs to return str
139 138 return safe_str(self.__unicode__())
140 139 return '<DB:%s>' % (self.__class__.__name__)
141 140
142 141
143 142 class RhodeCodeSetting(Base, BaseModel):
144 143 __tablename__ = 'rhodecode_settings'
145 144 __table_args__ = (
146 145 UniqueConstraint('app_settings_name'),
147 146 {'extend_existing': True, 'mysql_engine': 'InnoDB',
148 147 'mysql_charset': 'utf8'}
149 148 )
150 149 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
151 150 app_settings_name = Column("app_settings_name", String(255), nullable=True, unique=None, default=None)
152 151 _app_settings_value = Column("app_settings_value", String(255), nullable=True, unique=None, default=None)
153 152
154 153 def __init__(self, k='', v=''):
155 154 self.app_settings_name = k
156 155 self.app_settings_value = v
157 156
158 157 @validates('_app_settings_value')
159 158 def validate_settings_value(self, key, val):
160 159 assert type(val) == unicode
161 160 return val
162 161
163 162 @hybrid_property
164 163 def app_settings_value(self):
165 164 v = self._app_settings_value
166 165 if self.app_settings_name == 'ldap_active':
167 166 v = str2bool(v)
168 167 return v
169 168
170 169 @app_settings_value.setter
171 170 def app_settings_value(self, val):
172 171 """
173 172 Setter that will always make sure we use unicode in app_settings_value
174 173
175 174 :param val:
176 175 """
177 176 self._app_settings_value = safe_unicode(val)
178 177
179 178 def __unicode__(self):
180 179 return u"<%s('%s:%s')>" % (
181 180 self.__class__.__name__,
182 181 self.app_settings_name, self.app_settings_value
183 182 )
184 183
185 184
186 185 class RhodeCodeUi(Base, BaseModel):
187 186 __tablename__ = 'rhodecode_ui'
188 187 __table_args__ = (
189 188 UniqueConstraint('ui_key'),
190 189 {'extend_existing': True, 'mysql_engine': 'InnoDB',
191 190 'mysql_charset': 'utf8'}
192 191 )
193 192
194 193 HOOK_REPO_SIZE = 'changegroup.repo_size'
195 194 HOOK_PUSH = 'changegroup.push_logger'
196 195 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
197 196 HOOK_PULL = 'outgoing.pull_logger'
198 197 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
199 198
200 199 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
201 200 ui_section = Column("ui_section", String(255), nullable=True, unique=None, default=None)
202 201 ui_key = Column("ui_key", String(255), nullable=True, unique=None, default=None)
203 202 ui_value = Column("ui_value", String(255), nullable=True, unique=None, default=None)
204 203 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
205 204
206 205
207 206
208 207 class User(Base, BaseModel):
209 208 __tablename__ = 'users'
210 209 __table_args__ = (
211 210 UniqueConstraint('username'), UniqueConstraint('email'),
212 211 Index('u_username_idx', 'username'),
213 212 Index('u_email_idx', 'email'),
214 213 {'extend_existing': True, 'mysql_engine': 'InnoDB',
215 214 'mysql_charset': 'utf8'}
216 215 )
217 216 DEFAULT_USER = 'default'
218 217 DEFAULT_PERMISSIONS = [
219 218 'hg.register.manual_activate', 'hg.create.repository',
220 219 'hg.fork.repository', 'repository.read', 'group.read'
221 220 ]
222 221 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
223 222 username = Column("username", String(255), nullable=True, unique=None, default=None)
224 223 password = Column("password", String(255), nullable=True, unique=None, default=None)
225 224 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
226 225 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
227 226 name = Column("firstname", String(255), nullable=True, unique=None, default=None)
228 227 lastname = Column("lastname", String(255), nullable=True, unique=None, default=None)
229 228 _email = Column("email", String(255), nullable=True, unique=None, default=None)
230 229 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
231 230 ldap_dn = Column("ldap_dn", String(255), nullable=True, unique=None, default=None)
232 231 api_key = Column("api_key", String(255), nullable=True, unique=None, default=None)
233 232 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
234 233
235 234 user_log = relationship('UserLog', cascade='all')
236 235 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
237 236
238 237 repositories = relationship('Repository')
239 238 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
240 239 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
241 240 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
242 241
243 242 group_member = relationship('UserGroupMember', cascade='all')
244 243
245 244 notifications = relationship('UserNotification', cascade='all')
246 245 # notifications assigned to this user
247 246 user_created_notifications = relationship('Notification', cascade='all')
248 247 # comments created by this user
249 248 user_comments = relationship('ChangesetComment', cascade='all')
250 249 user_emails = relationship('UserEmailMap', cascade='all')
251 250
252 251 @hybrid_property
253 252 def email(self):
254 253 return self._email
255 254
256 255 @email.setter
257 256 def email(self, val):
258 257 self._email = val.lower() if val else None
259 258
260 259 @property
261 260 def firstname(self):
262 261 # alias for future
263 262 return self.name
264 263
265 264 @property
266 265 def username_and_name(self):
267 266 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
268 267
269 268 @property
270 269 def full_name(self):
271 270 return '%s %s' % (self.firstname, self.lastname)
272 271
273 272 @property
274 273 def full_contact(self):
275 274 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
276 275
277 276 @property
278 277 def short_contact(self):
279 278 return '%s %s' % (self.firstname, self.lastname)
280 279
281 280 @property
282 281 def is_admin(self):
283 282 return self.admin
284 283
285 284 @classmethod
286 285 def get_by_username(cls, username, case_insensitive=False, cache=False):
287 286 if case_insensitive:
288 287 q = cls.query().filter(cls.username.ilike(username))
289 288 else:
290 289 q = cls.query().filter(cls.username == username)
291 290
292 291 if cache:
293 292 q = q.options(FromCache(
294 293 "sql_cache_short",
295 294 "get_user_%s" % _hash_key(username)
296 295 )
297 296 )
298 297 return q.scalar()
299 298
300 299 @classmethod
301 300 def get_by_auth_token(cls, auth_token, cache=False):
302 301 q = cls.query().filter(cls.api_key == auth_token)
303 302
304 303 if cache:
305 304 q = q.options(FromCache("sql_cache_short",
306 305 "get_auth_token_%s" % auth_token))
307 306 return q.scalar()
308 307
309 308 @classmethod
310 309 def get_by_email(cls, email, case_insensitive=False, cache=False):
311 310 if case_insensitive:
312 311 q = cls.query().filter(cls.email.ilike(email))
313 312 else:
314 313 q = cls.query().filter(cls.email == email)
315 314
316 315 if cache:
317 316 q = q.options(FromCache("sql_cache_short",
318 317 "get_email_key_%s" % email))
319 318
320 319 ret = q.scalar()
321 320 if ret is None:
322 321 q = UserEmailMap.query()
323 322 # try fetching in alternate email map
324 323 if case_insensitive:
325 324 q = q.filter(UserEmailMap.email.ilike(email))
326 325 else:
327 326 q = q.filter(UserEmailMap.email == email)
328 327 q = q.options(joinedload(UserEmailMap.user))
329 328 if cache:
330 329 q = q.options(FromCache("sql_cache_short",
331 330 "get_email_map_key_%s" % email))
332 331 ret = getattr(q.scalar(), 'user', None)
333 332
334 333 return ret
335 334
336 335
337 336 class UserEmailMap(Base, BaseModel):
338 337 __tablename__ = 'user_email_map'
339 338 __table_args__ = (
340 339 Index('uem_email_idx', 'email'),
341 340 UniqueConstraint('email'),
342 341 {'extend_existing': True, 'mysql_engine': 'InnoDB',
343 342 'mysql_charset': 'utf8'}
344 343 )
345 344 __mapper_args__ = {}
346 345
347 346 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
348 347 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
349 348 _email = Column("email", String(255), nullable=True, unique=False, default=None)
350 349 user = relationship('User', lazy='joined')
351 350
352 351 @validates('_email')
353 352 def validate_email(self, key, email):
354 353 # check if this email is not main one
355 354 main_email = Session().query(User).filter(User.email == email).scalar()
356 355 if main_email is not None:
357 356 raise AttributeError('email %s is present is user table' % email)
358 357 return email
359 358
360 359 @hybrid_property
361 360 def email(self):
362 361 return self._email
363 362
364 363 @email.setter
365 364 def email(self, val):
366 365 self._email = val.lower() if val else None
367 366
368 367
369 368 class UserLog(Base, BaseModel):
370 369 __tablename__ = 'user_logs'
371 370 __table_args__ = (
372 371 {'extend_existing': True, 'mysql_engine': 'InnoDB',
373 372 'mysql_charset': 'utf8'},
374 373 )
375 374 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
376 375 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
377 376 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
378 377 repository_name = Column("repository_name", String(255), nullable=True, unique=None, default=None)
379 378 user_ip = Column("user_ip", String(255), nullable=True, unique=None, default=None)
380 379 action = Column("action", String(1200000), nullable=True, unique=None, default=None)
381 380 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
382 381
383 382
384 383 user = relationship('User')
385 384 repository = relationship('Repository', cascade='')
386 385
387 386
388 387 class UserGroup(Base, BaseModel):
389 388 __tablename__ = 'users_groups'
390 389 __table_args__ = (
391 390 {'extend_existing': True, 'mysql_engine': 'InnoDB',
392 391 'mysql_charset': 'utf8'},
393 392 )
394 393
395 394 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
396 395 users_group_name = Column("users_group_name", String(255), nullable=False, unique=True, default=None)
397 396 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
398 397 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
399 398
400 399 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
401 400 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
402 401 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
403 402
404 403 def __unicode__(self):
405 404 return u'<userGroup(%s)>' % (self.users_group_name)
406 405
407 406 @classmethod
408 407 def get_by_group_name(cls, group_name, cache=False,
409 408 case_insensitive=False):
410 409 if case_insensitive:
411 410 q = cls.query().filter(cls.users_group_name.ilike(group_name))
412 411 else:
413 412 q = cls.query().filter(cls.users_group_name == group_name)
414 413 if cache:
415 414 q = q.options(FromCache(
416 415 "sql_cache_short",
417 416 "get_user_%s" % _hash_key(group_name)
418 417 )
419 418 )
420 419 return q.scalar()
421 420
422 421 @classmethod
423 422 def get(cls, users_group_id, cache=False):
424 423 user_group = cls.query()
425 424 if cache:
426 425 user_group = user_group.options(FromCache("sql_cache_short",
427 426 "get_users_group_%s" % users_group_id))
428 427 return user_group.get(users_group_id)
429 428
430 429
431 430 class UserGroupMember(Base, BaseModel):
432 431 __tablename__ = 'users_groups_members'
433 432 __table_args__ = (
434 433 {'extend_existing': True, 'mysql_engine': 'InnoDB',
435 434 'mysql_charset': 'utf8'},
436 435 )
437 436
438 437 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
439 438 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
440 439 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
441 440
442 441 user = relationship('User', lazy='joined')
443 442 users_group = relationship('UserGroup')
444 443
445 444 def __init__(self, gr_id='', u_id=''):
446 445 self.users_group_id = gr_id
447 446 self.user_id = u_id
448 447
449 448
450 449 class Repository(Base, BaseModel):
451 450 __tablename__ = 'repositories'
452 451 __table_args__ = (
453 452 UniqueConstraint('repo_name'),
454 453 Index('r_repo_name_idx', 'repo_name'),
455 454 {'extend_existing': True, 'mysql_engine': 'InnoDB',
456 455 'mysql_charset': 'utf8'},
457 456 )
458 457
459 458 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
460 459 repo_name = Column("repo_name", String(255), nullable=False, unique=True, default=None)
461 460 clone_uri = Column("clone_uri", String(255), nullable=True, unique=False, default=None)
462 461 repo_type = Column("repo_type", String(255), nullable=False, unique=False, default=None)
463 462 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
464 463 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
465 464 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
466 465 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
467 466 description = Column("description", String(10000), nullable=True, unique=None, default=None)
468 467 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
469 468 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
470 469 landing_rev = Column("landing_revision", String(255), nullable=False, unique=False, default=None)
471 470 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
472 471 _locked = Column("locked", String(255), nullable=True, unique=False, default=None)
473 472
474 473 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
475 474 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
476 475
477 476 user = relationship('User')
478 477 fork = relationship('Repository', remote_side=repo_id)
479 478 group = relationship('RepoGroup')
480 479 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
481 480 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
482 481 stats = relationship('Statistics', cascade='all', uselist=False)
483 482
484 483 followers = relationship('UserFollowing',
485 484 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
486 485 cascade='all')
487 486
488 487 logs = relationship('UserLog')
489 488 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
490 489
491 490 pull_requests_org = relationship('PullRequest',
492 491 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
493 492 cascade="all, delete, delete-orphan")
494 493
495 494 pull_requests_other = relationship('PullRequest',
496 495 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
497 496 cascade="all, delete, delete-orphan")
498 497
499 498 def __unicode__(self):
500 499 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
501 500 self.repo_name)
502 501
503 502
504 503 @classmethod
505 504 def get_by_repo_name(cls, repo_name):
506 505 q = Session().query(cls).filter(cls.repo_name == repo_name)
507 506 q = q.options(joinedload(Repository.fork))\
508 507 .options(joinedload(Repository.user))\
509 508 .options(joinedload(Repository.group))
510 509 return q.scalar()
511 510
512 511
513 512 class RepoGroup(Base, BaseModel):
514 513 __tablename__ = 'groups'
515 514 __table_args__ = (
516 515 UniqueConstraint('group_name', 'group_parent_id'),
517 516 CheckConstraint('group_id != group_parent_id'),
518 517 {'extend_existing': True, 'mysql_engine': 'InnoDB',
519 518 'mysql_charset': 'utf8'},
520 519 )
521 520 __mapper_args__ = {'order_by': 'group_name'}
522 521
523 522 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
524 523 group_name = Column("group_name", String(255), nullable=False, unique=True, default=None)
525 524 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
526 525 group_description = Column("group_description", String(10000), nullable=True, unique=None, default=None)
527 526 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
528 527
529 528 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
530 529 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
531 530 parent_group = relationship('RepoGroup', remote_side=group_id)
532 531
533 532 def __init__(self, group_name='', parent_group=None):
534 533 self.group_name = group_name
535 534 self.parent_group = parent_group
536 535
537 536 def __unicode__(self):
538 537 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
539 538 self.group_name)
540 539
541 540 @classmethod
542 541 def url_sep(cls):
543 542 return URL_SEP
544 543
545 544 @classmethod
546 545 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
547 546 if case_insensitive:
548 547 gr = cls.query()\
549 548 .filter(cls.group_name.ilike(group_name))
550 549 else:
551 550 gr = cls.query()\
552 551 .filter(cls.group_name == group_name)
553 552 if cache:
554 553 gr = gr.options(FromCache(
555 554 "sql_cache_short",
556 555 "get_group_%s" % _hash_key(group_name)
557 556 )
558 557 )
559 558 return gr.scalar()
560 559
561 560
562 561 class Permission(Base, BaseModel):
563 562 __tablename__ = 'permissions'
564 563 __table_args__ = (
565 564 Index('p_perm_name_idx', 'permission_name'),
566 565 {'extend_existing': True, 'mysql_engine': 'InnoDB',
567 566 'mysql_charset': 'utf8'},
568 567 )
569 568 PERMS = [
570 569 ('repository.none', _('Repository no access')),
571 570 ('repository.read', _('Repository read access')),
572 571 ('repository.write', _('Repository write access')),
573 572 ('repository.admin', _('Repository admin access')),
574 573
575 574 ('group.none', _('Repositories Group no access')),
576 575 ('group.read', _('Repositories Group read access')),
577 576 ('group.write', _('Repositories Group write access')),
578 577 ('group.admin', _('Repositories Group admin access')),
579 578
580 579 ('hg.admin', _('RhodeCode Administrator')),
581 580 ('hg.create.none', _('Repository creation disabled')),
582 581 ('hg.create.repository', _('Repository creation enabled')),
583 582 ('hg.fork.none', _('Repository forking disabled')),
584 583 ('hg.fork.repository', _('Repository forking enabled')),
585 584 ('hg.register.none', _('Register disabled')),
586 585 ('hg.register.manual_activate', _('Register new user with RhodeCode '
587 586 'with manual activation')),
588 587
589 588 ('hg.register.auto_activate', _('Register new user with RhodeCode '
590 589 'with auto activation')),
591 590 ]
592 591
593 592 # defines which permissions are more important higher the more important
594 593 PERM_WEIGHTS = {
595 594 'repository.none': 0,
596 595 'repository.read': 1,
597 596 'repository.write': 3,
598 597 'repository.admin': 4,
599 598
600 599 'group.none': 0,
601 600 'group.read': 1,
602 601 'group.write': 3,
603 602 'group.admin': 4,
604 603
605 604 'hg.fork.none': 0,
606 605 'hg.fork.repository': 1,
607 606 'hg.create.none': 0,
608 607 'hg.create.repository':1
609 608 }
610 609
611 610 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
612 611 permission_name = Column("permission_name", String(255), nullable=True, unique=None, default=None)
613 612 permission_longname = Column("permission_longname", String(255), nullable=True, unique=None, default=None)
614 613
615 614 def __unicode__(self):
616 615 return u"<%s('%s:%s')>" % (
617 616 self.__class__.__name__, self.permission_id, self.permission_name
618 617 )
619 618
620 619 @classmethod
621 620 def get_by_key(cls, key):
622 621 return cls.query().filter(cls.permission_name == key).scalar()
623 622
624 623
625 624 class UserRepoToPerm(Base, BaseModel):
626 625 __tablename__ = 'repo_to_perm'
627 626 __table_args__ = (
628 627 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
629 628 {'extend_existing': True, 'mysql_engine': 'InnoDB',
630 629 'mysql_charset': 'utf8'}
631 630 )
632 631 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
633 632 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
634 633 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
635 634 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
636 635
637 636 user = relationship('User')
638 637 repository = relationship('Repository')
639 638 permission = relationship('Permission')
640 639
641 640 def __unicode__(self):
642 641 return u'<user:%s => %s >' % (self.user, self.repository)
643 642
644 643
645 644 class UserToPerm(Base, BaseModel):
646 645 __tablename__ = 'user_to_perm'
647 646 __table_args__ = (
648 647 UniqueConstraint('user_id', 'permission_id'),
649 648 {'extend_existing': True, 'mysql_engine': 'InnoDB',
650 649 'mysql_charset': 'utf8'}
651 650 )
652 651 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
653 652 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
654 653 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
655 654
656 655 user = relationship('User')
657 656 permission = relationship('Permission', lazy='joined')
658 657
659 658
660 659 class UserGroupRepoToPerm(Base, BaseModel):
661 660 __tablename__ = 'users_group_repo_to_perm'
662 661 __table_args__ = (
663 662 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
664 663 {'extend_existing': True, 'mysql_engine': 'InnoDB',
665 664 'mysql_charset': 'utf8'}
666 665 )
667 666 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
668 667 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
669 668 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
670 669 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
671 670
672 671 users_group = relationship('UserGroup')
673 672 permission = relationship('Permission')
674 673 repository = relationship('Repository')
675 674
676 675 def __unicode__(self):
677 676 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
678 677
679 678
680 679 class UserGroupToPerm(Base, BaseModel):
681 680 __tablename__ = 'users_group_to_perm'
682 681 __table_args__ = (
683 682 UniqueConstraint('users_group_id', 'permission_id',),
684 683 {'extend_existing': True, 'mysql_engine': 'InnoDB',
685 684 'mysql_charset': 'utf8'}
686 685 )
687 686 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
688 687 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
689 688 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
690 689
691 690 users_group = relationship('UserGroup')
692 691 permission = relationship('Permission')
693 692
694 693
695 694 class UserRepoGroupToPerm(Base, BaseModel):
696 695 __tablename__ = 'user_repo_group_to_perm'
697 696 __table_args__ = (
698 697 UniqueConstraint('user_id', 'group_id', 'permission_id'),
699 698 {'extend_existing': True, 'mysql_engine': 'InnoDB',
700 699 'mysql_charset': 'utf8'}
701 700 )
702 701
703 702 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
704 703 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
705 704 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
706 705 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
707 706
708 707 user = relationship('User')
709 708 group = relationship('RepoGroup')
710 709 permission = relationship('Permission')
711 710
712 711
713 712 class UserGroupRepoGroupToPerm(Base, BaseModel):
714 713 __tablename__ = 'users_group_repo_group_to_perm'
715 714 __table_args__ = (
716 715 UniqueConstraint('users_group_id', 'group_id'),
717 716 {'extend_existing': True, 'mysql_engine': 'InnoDB',
718 717 'mysql_charset': 'utf8'}
719 718 )
720 719
721 720 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)
722 721 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
723 722 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
724 723 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
725 724
726 725 users_group = relationship('UserGroup')
727 726 permission = relationship('Permission')
728 727 group = relationship('RepoGroup')
729 728
730 729
731 730 class Statistics(Base, BaseModel):
732 731 __tablename__ = 'statistics'
733 732 __table_args__ = (
734 733 UniqueConstraint('repository_id'),
735 734 {'extend_existing': True, 'mysql_engine': 'InnoDB',
736 735 'mysql_charset': 'utf8'}
737 736 )
738 737 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
739 738 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
740 739 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
741 740 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
742 741 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
743 742 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
744 743
745 744 repository = relationship('Repository', single_parent=True)
746 745
747 746
748 747 class UserFollowing(Base, BaseModel):
749 748 __tablename__ = 'user_followings'
750 749 __table_args__ = (
751 750 UniqueConstraint('user_id', 'follows_repository_id'),
752 751 UniqueConstraint('user_id', 'follows_user_id'),
753 752 {'extend_existing': True, 'mysql_engine': 'InnoDB',
754 753 'mysql_charset': 'utf8'}
755 754 )
756 755
757 756 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
758 757 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
759 758 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
760 759 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
761 760 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
762 761
763 762 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
764 763
765 764 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
766 765 follows_repository = relationship('Repository', order_by='Repository.repo_name')
767 766
768 767
769 768 class CacheInvalidation(Base, BaseModel):
770 769 __tablename__ = 'cache_invalidation'
771 770 __table_args__ = (
772 771 UniqueConstraint('cache_key'),
773 772 Index('key_idx', 'cache_key'),
774 773 {'extend_existing': True, 'mysql_engine': 'InnoDB',
775 774 'mysql_charset': 'utf8'},
776 775 )
777 776 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
778 777 cache_key = Column("cache_key", String(255), nullable=True, unique=None, default=None)
779 778 cache_args = Column("cache_args", String(255), nullable=True, unique=None, default=None)
780 779 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
781 780
782 781 def __init__(self, cache_key, cache_args=''):
783 782 self.cache_key = cache_key
784 783 self.cache_args = cache_args
785 784 self.cache_active = False
786 785
787 786
788 787 class ChangesetComment(Base, BaseModel):
789 788 __tablename__ = 'changeset_comments'
790 789 __table_args__ = (
791 790 Index('cc_revision_idx', 'revision'),
792 791 {'extend_existing': True, 'mysql_engine': 'InnoDB',
793 792 'mysql_charset': 'utf8'},
794 793 )
795 794 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
796 795 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
797 796 revision = Column('revision', String(40), nullable=True)
798 797 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
799 798 line_no = Column('line_no', Unicode(10), nullable=True)
800 799 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
801 800 f_path = Column('f_path', Unicode(1000), nullable=True)
802 801 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
803 802 text = Column('text', UnicodeText().with_variant(UnicodeText(25000), 'mysql'), nullable=False)
804 803 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
805 804 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
806 805
807 806 author = relationship('User', lazy='joined')
808 807 repo = relationship('Repository')
809 808 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
810 809 pull_request = relationship('PullRequest', lazy='joined')
811 810
812 811 @classmethod
813 812 def get_users(cls, revision=None, pull_request_id=None):
814 813 """
815 814 Returns user associated with this ChangesetComment. ie those
816 815 who actually commented
817 816
818 817 :param cls:
819 818 :param revision:
820 819 """
821 820 q = Session().query(User)\
822 821 .join(ChangesetComment.author)
823 822 if revision:
824 823 q = q.filter(cls.revision == revision)
825 824 elif pull_request_id:
826 825 q = q.filter(cls.pull_request_id == pull_request_id)
827 826 return q.all()
828 827
829 828
830 829 class ChangesetStatus(Base, BaseModel):
831 830 __tablename__ = 'changeset_statuses'
832 831 __table_args__ = (
833 832 Index('cs_revision_idx', 'revision'),
834 833 Index('cs_version_idx', 'version'),
835 834 UniqueConstraint('repo_id', 'revision', 'version'),
836 835 {'extend_existing': True, 'mysql_engine': 'InnoDB',
837 836 'mysql_charset': 'utf8'}
838 837 )
839 838 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
840 839 STATUS_APPROVED = 'approved'
841 840 STATUS_REJECTED = 'rejected'
842 841 STATUS_UNDER_REVIEW = 'under_review'
843 842
844 843 STATUSES = [
845 844 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
846 845 (STATUS_APPROVED, _("Approved")),
847 846 (STATUS_REJECTED, _("Rejected")),
848 847 (STATUS_UNDER_REVIEW, _("Under Review")),
849 848 ]
850 849
851 850 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
852 851 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
853 852 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
854 853 revision = Column('revision', String(40), nullable=False)
855 854 status = Column('status', String(128), nullable=False, default=DEFAULT)
856 855 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
857 856 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
858 857 version = Column('version', Integer(), nullable=False, default=0)
859 858 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
860 859
861 860 author = relationship('User', lazy='joined')
862 861 repo = relationship('Repository')
863 862 comment = relationship('ChangesetComment', lazy='joined')
864 863 pull_request = relationship('PullRequest', lazy='joined')
865 864
866 865
867 866
868 867 class PullRequest(Base, BaseModel):
869 868 __tablename__ = 'pull_requests'
870 869 __table_args__ = (
871 870 {'extend_existing': True, 'mysql_engine': 'InnoDB',
872 871 'mysql_charset': 'utf8'},
873 872 )
874 873
875 874 STATUS_NEW = u'new'
876 875 STATUS_OPEN = u'open'
877 876 STATUS_CLOSED = u'closed'
878 877
879 878 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
880 879 title = Column('title', Unicode(256), nullable=True)
881 880 description = Column('description', UnicodeText().with_variant(UnicodeText(10240), 'mysql'), nullable=True)
882 881 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
883 882 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
884 883 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
885 884 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
886 885 _revisions = Column('revisions', UnicodeText().with_variant(UnicodeText(20500), 'mysql'))
887 886 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
888 887 org_ref = Column('org_ref', Unicode(256), nullable=False)
889 888 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
890 889 other_ref = Column('other_ref', Unicode(256), nullable=False)
891 890
892 891 author = relationship('User', lazy='joined')
893 892 reviewers = relationship('PullRequestReviewers',
894 893 cascade="all, delete, delete-orphan")
895 894 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
896 895 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
897 896 statuses = relationship('ChangesetStatus')
898 897 comments = relationship('ChangesetComment',
899 898 cascade="all, delete, delete-orphan")
900 899
901 900
902 901 class PullRequestReviewers(Base, BaseModel):
903 902 __tablename__ = 'pull_request_reviewers'
904 903 __table_args__ = (
905 904 {'extend_existing': True, 'mysql_engine': 'InnoDB',
906 905 'mysql_charset': 'utf8'},
907 906 )
908 907
909 908 def __init__(self, user=None, pull_request=None):
910 909 self.user = user
911 910 self.pull_request = pull_request
912 911
913 912 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
914 913 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
915 914 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
916 915
917 916 user = relationship('User')
918 917 pull_request = relationship('PullRequest')
919 918
920 919
921 920 class Notification(Base, BaseModel):
922 921 __tablename__ = 'notifications'
923 922 __table_args__ = (
924 923 Index('notification_type_idx', 'type'),
925 924 {'extend_existing': True, 'mysql_engine': 'InnoDB',
926 925 'mysql_charset': 'utf8'},
927 926 )
928 927
929 928 TYPE_CHANGESET_COMMENT = u'cs_comment'
930 929 TYPE_MESSAGE = u'message'
931 930 TYPE_MENTION = u'mention'
932 931 TYPE_REGISTRATION = u'registration'
933 932 TYPE_PULL_REQUEST = u'pull_request'
934 933 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
935 934
936 935 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
937 936 subject = Column('subject', Unicode(512), nullable=True)
938 937 body = Column('body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True)
939 938 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
940 939 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
941 940 type_ = Column('type', Unicode(256))
942 941
943 942 created_by_user = relationship('User')
944 943 notifications_to_users = relationship('UserNotification', lazy='joined',
945 944 cascade="all, delete, delete-orphan")
946 945
947 946
948 947 class UserNotification(Base, BaseModel):
949 948 __tablename__ = 'user_to_notification'
950 949 __table_args__ = (
951 950 UniqueConstraint('user_id', 'notification_id'),
952 951 {'extend_existing': True, 'mysql_engine': 'InnoDB',
953 952 'mysql_charset': 'utf8'}
954 953 )
955 954 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
956 955 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
957 956 read = Column('read', Boolean, default=False)
958 957 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
959 958
960 959 user = relationship('User', lazy="joined")
961 960 notification = relationship('Notification', lazy="joined",
962 961 order_by=lambda: Notification.created_on.desc(),)
963 962
964 963
965 964 class DbMigrateVersion(Base, BaseModel):
966 965 __tablename__ = 'db_migrate_version'
967 966 __table_args__ = (
968 967 {'extend_existing': True, 'mysql_engine': 'InnoDB',
969 968 'mysql_charset': 'utf8'},
970 969 )
971 970 repository_id = Column('repository_id', String(250), primary_key=True)
972 971 repository_path = Column('repository_path', Text)
973 972 version = Column('version', Integer)
@@ -1,993 +1,993 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21
22 22 import os
23 23 import time
24 24 import logging
25 25 import datetime
26 26 import traceback
27 27 import hashlib
28 28 import collections
29 29
30 30 from sqlalchemy import *
31 31 from sqlalchemy.ext.hybrid import hybrid_property
32 32 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
33 33 from sqlalchemy.exc import DatabaseError
34 34 from beaker.cache import cache_region, region_invalidate
35 35 from webob.exc import HTTPNotFound
36 36
37 from pylons.i18n.translation import lazy_ugettext as _
37 from rhodecode.translation import _
38 38
39 39 from rhodecode.lib.vcs import get_backend
40 40 from rhodecode.lib.vcs.utils.helpers import get_scm
41 41 from rhodecode.lib.vcs.exceptions import VCSError
42 42 from zope.cachedescriptors.property import Lazy as LazyProperty
43 43
44 44 from rhodecode.lib.utils2 import str2bool, safe_str, get_commit_safe, \
45 45 safe_unicode, remove_suffix, remove_prefix
46 46 from rhodecode.lib.ext_json import json
47 47 from rhodecode.lib.caching_query import FromCache
48 48
49 49 from rhodecode.model.meta import Base, Session
50 50
51 51 URL_SEP = '/'
52 52 log = logging.getLogger(__name__)
53 53
54 54 #==============================================================================
55 55 # BASE CLASSES
56 56 #==============================================================================
57 57
58 58 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
59 59
60 60
61 61 class BaseModel(object):
62 62 """
63 63 Base Model for all classes
64 64 """
65 65
66 66 @classmethod
67 67 def _get_keys(cls):
68 68 """return column names for this model """
69 69 return class_mapper(cls).c.keys()
70 70
71 71 def get_dict(self):
72 72 """
73 73 return dict with keys and values corresponding
74 74 to this model data """
75 75
76 76 d = {}
77 77 for k in self._get_keys():
78 78 d[k] = getattr(self, k)
79 79
80 80 # also use __json__() if present to get additional fields
81 81 _json_attr = getattr(self, '__json__', None)
82 82 if _json_attr:
83 83 # update with attributes from __json__
84 84 if callable(_json_attr):
85 85 _json_attr = _json_attr()
86 86 for k, val in _json_attr.iteritems():
87 87 d[k] = val
88 88 return d
89 89
90 90 def get_appstruct(self):
91 91 """return list with keys and values tupples corresponding
92 92 to this model data """
93 93
94 94 l = []
95 95 for k in self._get_keys():
96 96 l.append((k, getattr(self, k),))
97 97 return l
98 98
99 99 def populate_obj(self, populate_dict):
100 100 """populate model with data from given populate_dict"""
101 101
102 102 for k in self._get_keys():
103 103 if k in populate_dict:
104 104 setattr(self, k, populate_dict[k])
105 105
106 106 @classmethod
107 107 def query(cls):
108 108 return Session().query(cls)
109 109
110 110 @classmethod
111 111 def get(cls, id_):
112 112 if id_:
113 113 return cls.query().get(id_)
114 114
115 115 @classmethod
116 116 def get_or_404(cls, id_):
117 117 try:
118 118 id_ = int(id_)
119 119 except (TypeError, ValueError):
120 120 raise HTTPNotFound
121 121
122 122 res = cls.query().get(id_)
123 123 if not res:
124 124 raise HTTPNotFound
125 125 return res
126 126
127 127 @classmethod
128 128 def getAll(cls):
129 129 # deprecated and left for backward compatibility
130 130 return cls.get_all()
131 131
132 132 @classmethod
133 133 def get_all(cls):
134 134 return cls.query().all()
135 135
136 136 @classmethod
137 137 def delete(cls, id_):
138 138 obj = cls.query().get(id_)
139 139 Session().delete(obj)
140 140
141 141 def __repr__(self):
142 142 if hasattr(self, '__unicode__'):
143 143 # python repr needs to return str
144 144 return safe_str(self.__unicode__())
145 145 return '<DB:%s>' % (self.__class__.__name__)
146 146
147 147
148 148 class RhodeCodeSetting(Base, BaseModel):
149 149 __tablename__ = 'rhodecode_settings'
150 150 __table_args__ = (
151 151 UniqueConstraint('app_settings_name'),
152 152 {'extend_existing': True, 'mysql_engine': 'InnoDB',
153 153 'mysql_charset': 'utf8'}
154 154 )
155 155 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
156 156 app_settings_name = Column("app_settings_name", String(255), nullable=True, unique=None, default=None)
157 157 _app_settings_value = Column("app_settings_value", String(255), nullable=True, unique=None, default=None)
158 158
159 159 def __init__(self, k='', v=''):
160 160 self.app_settings_name = k
161 161 self.app_settings_value = v
162 162
163 163 @validates('_app_settings_value')
164 164 def validate_settings_value(self, key, val):
165 165 assert type(val) == unicode
166 166 return val
167 167
168 168 @hybrid_property
169 169 def app_settings_value(self):
170 170 v = self._app_settings_value
171 171 if self.app_settings_name in ["ldap_active",
172 172 "default_repo_enable_statistics",
173 173 "default_repo_enable_locking",
174 174 "default_repo_private",
175 175 "default_repo_enable_downloads"]:
176 176 v = str2bool(v)
177 177 return v
178 178
179 179 @app_settings_value.setter
180 180 def app_settings_value(self, val):
181 181 """
182 182 Setter that will always make sure we use unicode in app_settings_value
183 183
184 184 :param val:
185 185 """
186 186 self._app_settings_value = safe_unicode(val)
187 187
188 188 def __unicode__(self):
189 189 return u"<%s('%s:%s')>" % (
190 190 self.__class__.__name__,
191 191 self.app_settings_name, self.app_settings_value
192 192 )
193 193
194 194
195 195 class RhodeCodeUi(Base, BaseModel):
196 196 __tablename__ = 'rhodecode_ui'
197 197 __table_args__ = (
198 198 UniqueConstraint('ui_key'),
199 199 {'extend_existing': True, 'mysql_engine': 'InnoDB',
200 200 'mysql_charset': 'utf8'}
201 201 )
202 202
203 203 HOOK_REPO_SIZE = 'changegroup.repo_size'
204 204 HOOK_PUSH = 'changegroup.push_logger'
205 205 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
206 206 HOOK_PULL = 'outgoing.pull_logger'
207 207 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
208 208
209 209 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
210 210 ui_section = Column("ui_section", String(255), nullable=True, unique=None, default=None)
211 211 ui_key = Column("ui_key", String(255), nullable=True, unique=None, default=None)
212 212 ui_value = Column("ui_value", String(255), nullable=True, unique=None, default=None)
213 213 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
214 214
215 215
216 216
217 217 class User(Base, BaseModel):
218 218 __tablename__ = 'users'
219 219 __table_args__ = (
220 220 UniqueConstraint('username'), UniqueConstraint('email'),
221 221 Index('u_username_idx', 'username'),
222 222 Index('u_email_idx', 'email'),
223 223 {'extend_existing': True, 'mysql_engine': 'InnoDB',
224 224 'mysql_charset': 'utf8'}
225 225 )
226 226 DEFAULT_USER = 'default'
227 227 DEFAULT_PERMISSIONS = [
228 228 'hg.register.manual_activate', 'hg.create.repository',
229 229 'hg.fork.repository', 'repository.read', 'group.read'
230 230 ]
231 231 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
232 232 username = Column("username", String(255), nullable=True, unique=None, default=None)
233 233 password = Column("password", String(255), nullable=True, unique=None, default=None)
234 234 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
235 235 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
236 236 name = Column("firstname", String(255), nullable=True, unique=None, default=None)
237 237 lastname = Column("lastname", String(255), nullable=True, unique=None, default=None)
238 238 _email = Column("email", String(255), nullable=True, unique=None, default=None)
239 239 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
240 240 ldap_dn = Column("ldap_dn", String(255), nullable=True, unique=None, default=None)
241 241 api_key = Column("api_key", String(255), nullable=True, unique=None, default=None)
242 242 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
243 243
244 244 user_log = relationship('UserLog')
245 245 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
246 246
247 247 repositories = relationship('Repository')
248 248 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
249 249 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
250 250
251 251 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
252 252 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
253 253
254 254 group_member = relationship('UserGroupMember', cascade='all')
255 255
256 256 notifications = relationship('UserNotification', cascade='all')
257 257 # notifications assigned to this user
258 258 user_created_notifications = relationship('Notification', cascade='all')
259 259 # comments created by this user
260 260 user_comments = relationship('ChangesetComment', cascade='all')
261 261 user_emails = relationship('UserEmailMap', cascade='all')
262 262
263 263 @hybrid_property
264 264 def email(self):
265 265 return self._email
266 266
267 267 @email.setter
268 268 def email(self, val):
269 269 self._email = val.lower() if val else None
270 270
271 271 @property
272 272 def firstname(self):
273 273 # alias for future
274 274 return self.name
275 275
276 276 @property
277 277 def username_and_name(self):
278 278 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
279 279
280 280 @property
281 281 def full_name(self):
282 282 return '%s %s' % (self.firstname, self.lastname)
283 283
284 284 @property
285 285 def full_contact(self):
286 286 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
287 287
288 288 @property
289 289 def short_contact(self):
290 290 return '%s %s' % (self.firstname, self.lastname)
291 291
292 292 @property
293 293 def is_admin(self):
294 294 return self.admin
295 295
296 296 @classmethod
297 297 def get_by_username(cls, username, case_insensitive=False, cache=False):
298 298 if case_insensitive:
299 299 q = cls.query().filter(cls.username.ilike(username))
300 300 else:
301 301 q = cls.query().filter(cls.username == username)
302 302
303 303 if cache:
304 304 q = q.options(FromCache(
305 305 "sql_cache_short",
306 306 "get_user_%s" % _hash_key(username)
307 307 )
308 308 )
309 309 return q.scalar()
310 310
311 311 @classmethod
312 312 def get_by_auth_token(cls, auth_token, cache=False):
313 313 q = cls.query().filter(cls.api_key == auth_token)
314 314
315 315 if cache:
316 316 q = q.options(FromCache("sql_cache_short",
317 317 "get_auth_token_%s" % auth_token))
318 318 return q.scalar()
319 319
320 320 @classmethod
321 321 def get_by_email(cls, email, case_insensitive=False, cache=False):
322 322 if case_insensitive:
323 323 q = cls.query().filter(cls.email.ilike(email))
324 324 else:
325 325 q = cls.query().filter(cls.email == email)
326 326
327 327 if cache:
328 328 q = q.options(FromCache("sql_cache_short",
329 329 "get_email_key_%s" % email))
330 330
331 331 ret = q.scalar()
332 332 if ret is None:
333 333 q = UserEmailMap.query()
334 334 # try fetching in alternate email map
335 335 if case_insensitive:
336 336 q = q.filter(UserEmailMap.email.ilike(email))
337 337 else:
338 338 q = q.filter(UserEmailMap.email == email)
339 339 q = q.options(joinedload(UserEmailMap.user))
340 340 if cache:
341 341 q = q.options(FromCache("sql_cache_short",
342 342 "get_email_map_key_%s" % email))
343 343 ret = getattr(q.scalar(), 'user', None)
344 344
345 345 return ret
346 346
347 347
348 348 class UserEmailMap(Base, BaseModel):
349 349 __tablename__ = 'user_email_map'
350 350 __table_args__ = (
351 351 Index('uem_email_idx', 'email'),
352 352 UniqueConstraint('email'),
353 353 {'extend_existing': True, 'mysql_engine': 'InnoDB',
354 354 'mysql_charset': 'utf8'}
355 355 )
356 356 __mapper_args__ = {}
357 357
358 358 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
359 359 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
360 360 _email = Column("email", String(255), nullable=True, unique=False, default=None)
361 361 user = relationship('User', lazy='joined')
362 362
363 363 @validates('_email')
364 364 def validate_email(self, key, email):
365 365 # check if this email is not main one
366 366 main_email = Session().query(User).filter(User.email == email).scalar()
367 367 if main_email is not None:
368 368 raise AttributeError('email %s is present is user table' % email)
369 369 return email
370 370
371 371 @hybrid_property
372 372 def email(self):
373 373 return self._email
374 374
375 375 @email.setter
376 376 def email(self, val):
377 377 self._email = val.lower() if val else None
378 378
379 379
380 380 class UserLog(Base, BaseModel):
381 381 __tablename__ = 'user_logs'
382 382 __table_args__ = (
383 383 {'extend_existing': True, 'mysql_engine': 'InnoDB',
384 384 'mysql_charset': 'utf8'},
385 385 )
386 386 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
387 387 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
388 388 username = Column("username", String(255), nullable=True, unique=None, default=None)
389 389 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
390 390 repository_name = Column("repository_name", String(255), nullable=True, unique=None, default=None)
391 391 user_ip = Column("user_ip", String(255), nullable=True, unique=None, default=None)
392 392 action = Column("action", String(1200000), nullable=True, unique=None, default=None)
393 393 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
394 394
395 395
396 396 user = relationship('User')
397 397 repository = relationship('Repository', cascade='')
398 398
399 399
400 400 class UserGroup(Base, BaseModel):
401 401 __tablename__ = 'users_groups'
402 402 __table_args__ = (
403 403 {'extend_existing': True, 'mysql_engine': 'InnoDB',
404 404 'mysql_charset': 'utf8'},
405 405 )
406 406
407 407 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
408 408 users_group_name = Column("users_group_name", String(255), nullable=False, unique=True, default=None)
409 409 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
410 410 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
411 411
412 412 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
413 413 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
414 414 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
415 415
416 416 def __unicode__(self):
417 417 return u'<userGroup(%s)>' % (self.users_group_name)
418 418
419 419 @classmethod
420 420 def get_by_group_name(cls, group_name, cache=False,
421 421 case_insensitive=False):
422 422 if case_insensitive:
423 423 q = cls.query().filter(cls.users_group_name.ilike(group_name))
424 424 else:
425 425 q = cls.query().filter(cls.users_group_name == group_name)
426 426 if cache:
427 427 q = q.options(FromCache(
428 428 "sql_cache_short",
429 429 "get_user_%s" % _hash_key(group_name)
430 430 )
431 431 )
432 432 return q.scalar()
433 433
434 434 @classmethod
435 435 def get(cls, users_group_id, cache=False):
436 436 user_group = cls.query()
437 437 if cache:
438 438 user_group = user_group.options(FromCache("sql_cache_short",
439 439 "get_users_group_%s" % users_group_id))
440 440 return user_group.get(users_group_id)
441 441
442 442
443 443 class UserGroupMember(Base, BaseModel):
444 444 __tablename__ = 'users_groups_members'
445 445 __table_args__ = (
446 446 {'extend_existing': True, 'mysql_engine': 'InnoDB',
447 447 'mysql_charset': 'utf8'},
448 448 )
449 449
450 450 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
451 451 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
452 452 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
453 453
454 454 user = relationship('User', lazy='joined')
455 455 users_group = relationship('UserGroup')
456 456
457 457 def __init__(self, gr_id='', u_id=''):
458 458 self.users_group_id = gr_id
459 459 self.user_id = u_id
460 460
461 461
462 462 class Repository(Base, BaseModel):
463 463 __tablename__ = 'repositories'
464 464 __table_args__ = (
465 465 UniqueConstraint('repo_name'),
466 466 Index('r_repo_name_idx', 'repo_name'),
467 467 {'extend_existing': True, 'mysql_engine': 'InnoDB',
468 468 'mysql_charset': 'utf8'},
469 469 )
470 470
471 471 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
472 472 repo_name = Column("repo_name", String(255), nullable=False, unique=True, default=None)
473 473 clone_uri = Column("clone_uri", String(255), nullable=True, unique=False, default=None)
474 474 repo_type = Column("repo_type", String(255), nullable=False, unique=False, default=None)
475 475 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
476 476 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
477 477 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
478 478 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
479 479 description = Column("description", String(10000), nullable=True, unique=None, default=None)
480 480 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
481 481 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
482 482 landing_rev = Column("landing_revision", String(255), nullable=False, unique=False, default=None)
483 483 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
484 484 _locked = Column("locked", String(255), nullable=True, unique=False, default=None)
485 485
486 486 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
487 487 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
488 488
489 489 user = relationship('User')
490 490 fork = relationship('Repository', remote_side=repo_id)
491 491 group = relationship('RepoGroup')
492 492 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
493 493 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
494 494 stats = relationship('Statistics', cascade='all', uselist=False)
495 495
496 496 followers = relationship('UserFollowing',
497 497 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
498 498 cascade='all')
499 499
500 500 logs = relationship('UserLog')
501 501 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
502 502
503 503 pull_requests_org = relationship('PullRequest',
504 504 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
505 505 cascade="all, delete, delete-orphan")
506 506
507 507 pull_requests_other = relationship('PullRequest',
508 508 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
509 509 cascade="all, delete, delete-orphan")
510 510
511 511 def __unicode__(self):
512 512 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
513 513 self.repo_name)
514 514
515 515
516 516 @classmethod
517 517 def get_by_repo_name(cls, repo_name):
518 518 q = Session().query(cls).filter(cls.repo_name == repo_name)
519 519 q = q.options(joinedload(Repository.fork))\
520 520 .options(joinedload(Repository.user))\
521 521 .options(joinedload(Repository.group))
522 522 return q.scalar()
523 523
524 524
525 525 class RepoGroup(Base, BaseModel):
526 526 __tablename__ = 'groups'
527 527 __table_args__ = (
528 528 UniqueConstraint('group_name', 'group_parent_id'),
529 529 CheckConstraint('group_id != group_parent_id'),
530 530 {'extend_existing': True, 'mysql_engine': 'InnoDB',
531 531 'mysql_charset': 'utf8'},
532 532 )
533 533 __mapper_args__ = {'order_by': 'group_name'}
534 534
535 535 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
536 536 group_name = Column("group_name", String(255), nullable=False, unique=True, default=None)
537 537 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
538 538 group_description = Column("group_description", String(10000), nullable=True, unique=None, default=None)
539 539 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
540 540
541 541 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
542 542 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
543 543 parent_group = relationship('RepoGroup', remote_side=group_id)
544 544
545 545 def __init__(self, group_name='', parent_group=None):
546 546 self.group_name = group_name
547 547 self.parent_group = parent_group
548 548
549 549 def __unicode__(self):
550 550 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
551 551 self.group_name)
552 552
553 553 @classmethod
554 554 def url_sep(cls):
555 555 return URL_SEP
556 556
557 557 @classmethod
558 558 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
559 559 if case_insensitive:
560 560 gr = cls.query()\
561 561 .filter(cls.group_name.ilike(group_name))
562 562 else:
563 563 gr = cls.query()\
564 564 .filter(cls.group_name == group_name)
565 565 if cache:
566 566 gr = gr.options(FromCache(
567 567 "sql_cache_short",
568 568 "get_group_%s" % _hash_key(group_name)
569 569 )
570 570 )
571 571 return gr.scalar()
572 572
573 573
574 574 class Permission(Base, BaseModel):
575 575 __tablename__ = 'permissions'
576 576 __table_args__ = (
577 577 Index('p_perm_name_idx', 'permission_name'),
578 578 {'extend_existing': True, 'mysql_engine': 'InnoDB',
579 579 'mysql_charset': 'utf8'},
580 580 )
581 581 PERMS = [
582 582 ('repository.none', _('Repository no access')),
583 583 ('repository.read', _('Repository read access')),
584 584 ('repository.write', _('Repository write access')),
585 585 ('repository.admin', _('Repository admin access')),
586 586
587 587 ('group.none', _('Repositories Group no access')),
588 588 ('group.read', _('Repositories Group read access')),
589 589 ('group.write', _('Repositories Group write access')),
590 590 ('group.admin', _('Repositories Group admin access')),
591 591
592 592 ('hg.admin', _('RhodeCode Administrator')),
593 593 ('hg.create.none', _('Repository creation disabled')),
594 594 ('hg.create.repository', _('Repository creation enabled')),
595 595 ('hg.fork.none', _('Repository forking disabled')),
596 596 ('hg.fork.repository', _('Repository forking enabled')),
597 597 ('hg.register.none', _('Register disabled')),
598 598 ('hg.register.manual_activate', _('Register new user with RhodeCode '
599 599 'with manual activation')),
600 600
601 601 ('hg.register.auto_activate', _('Register new user with RhodeCode '
602 602 'with auto activation')),
603 603 ]
604 604
605 605 # defines which permissions are more important higher the more important
606 606 PERM_WEIGHTS = {
607 607 'repository.none': 0,
608 608 'repository.read': 1,
609 609 'repository.write': 3,
610 610 'repository.admin': 4,
611 611
612 612 'group.none': 0,
613 613 'group.read': 1,
614 614 'group.write': 3,
615 615 'group.admin': 4,
616 616
617 617 'hg.fork.none': 0,
618 618 'hg.fork.repository': 1,
619 619 'hg.create.none': 0,
620 620 'hg.create.repository':1
621 621 }
622 622
623 623 DEFAULT_USER_PERMISSIONS = [
624 624 'repository.read',
625 625 'group.read',
626 626 'hg.create.repository',
627 627 'hg.fork.repository',
628 628 'hg.register.manual_activate',
629 629 ]
630 630
631 631 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
632 632 permission_name = Column("permission_name", String(255), nullable=True, unique=None, default=None)
633 633 permission_longname = Column("permission_longname", String(255), nullable=True, unique=None, default=None)
634 634
635 635 def __unicode__(self):
636 636 return u"<%s('%s:%s')>" % (
637 637 self.__class__.__name__, self.permission_id, self.permission_name
638 638 )
639 639
640 640 @classmethod
641 641 def get_by_key(cls, key):
642 642 return cls.query().filter(cls.permission_name == key).scalar()
643 643
644 644
645 645 class UserRepoToPerm(Base, BaseModel):
646 646 __tablename__ = 'repo_to_perm'
647 647 __table_args__ = (
648 648 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
649 649 {'extend_existing': True, 'mysql_engine': 'InnoDB',
650 650 'mysql_charset': 'utf8'}
651 651 )
652 652 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
653 653 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
654 654 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
655 655 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
656 656
657 657 user = relationship('User')
658 658 repository = relationship('Repository')
659 659 permission = relationship('Permission')
660 660
661 661 def __unicode__(self):
662 662 return u'<user:%s => %s >' % (self.user, self.repository)
663 663
664 664
665 665 class UserToPerm(Base, BaseModel):
666 666 __tablename__ = 'user_to_perm'
667 667 __table_args__ = (
668 668 UniqueConstraint('user_id', 'permission_id'),
669 669 {'extend_existing': True, 'mysql_engine': 'InnoDB',
670 670 'mysql_charset': 'utf8'}
671 671 )
672 672 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
673 673 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
674 674 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
675 675
676 676 user = relationship('User')
677 677 permission = relationship('Permission', lazy='joined')
678 678
679 679
680 680 class UserGroupRepoToPerm(Base, BaseModel):
681 681 __tablename__ = 'users_group_repo_to_perm'
682 682 __table_args__ = (
683 683 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
684 684 {'extend_existing': True, 'mysql_engine': 'InnoDB',
685 685 'mysql_charset': 'utf8'}
686 686 )
687 687 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
688 688 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
689 689 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
690 690 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
691 691
692 692 users_group = relationship('UserGroup')
693 693 permission = relationship('Permission')
694 694 repository = relationship('Repository')
695 695
696 696 def __unicode__(self):
697 697 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
698 698
699 699
700 700 class UserGroupToPerm(Base, BaseModel):
701 701 __tablename__ = 'users_group_to_perm'
702 702 __table_args__ = (
703 703 UniqueConstraint('users_group_id', 'permission_id',),
704 704 {'extend_existing': True, 'mysql_engine': 'InnoDB',
705 705 'mysql_charset': 'utf8'}
706 706 )
707 707 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
708 708 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
709 709 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
710 710
711 711 users_group = relationship('UserGroup')
712 712 permission = relationship('Permission')
713 713
714 714
715 715 class UserRepoGroupToPerm(Base, BaseModel):
716 716 __tablename__ = 'user_repo_group_to_perm'
717 717 __table_args__ = (
718 718 UniqueConstraint('user_id', 'group_id', 'permission_id'),
719 719 {'extend_existing': True, 'mysql_engine': 'InnoDB',
720 720 'mysql_charset': 'utf8'}
721 721 )
722 722
723 723 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
724 724 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
725 725 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
726 726 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
727 727
728 728 user = relationship('User')
729 729 group = relationship('RepoGroup')
730 730 permission = relationship('Permission')
731 731
732 732
733 733 class UserGroupRepoGroupToPerm(Base, BaseModel):
734 734 __tablename__ = 'users_group_repo_group_to_perm'
735 735 __table_args__ = (
736 736 UniqueConstraint('users_group_id', 'group_id'),
737 737 {'extend_existing': True, 'mysql_engine': 'InnoDB',
738 738 'mysql_charset': 'utf8'}
739 739 )
740 740
741 741 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
742 742 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
743 743 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
744 744 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
745 745
746 746 users_group = relationship('UserGroup')
747 747 permission = relationship('Permission')
748 748 group = relationship('RepoGroup')
749 749
750 750
751 751 class Statistics(Base, BaseModel):
752 752 __tablename__ = 'statistics'
753 753 __table_args__ = (
754 754 UniqueConstraint('repository_id'),
755 755 {'extend_existing': True, 'mysql_engine': 'InnoDB',
756 756 'mysql_charset': 'utf8'}
757 757 )
758 758 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
759 759 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
760 760 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
761 761 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
762 762 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
763 763 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
764 764
765 765 repository = relationship('Repository', single_parent=True)
766 766
767 767
768 768 class UserFollowing(Base, BaseModel):
769 769 __tablename__ = 'user_followings'
770 770 __table_args__ = (
771 771 UniqueConstraint('user_id', 'follows_repository_id'),
772 772 UniqueConstraint('user_id', 'follows_user_id'),
773 773 {'extend_existing': True, 'mysql_engine': 'InnoDB',
774 774 'mysql_charset': 'utf8'}
775 775 )
776 776
777 777 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
778 778 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
779 779 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
780 780 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
781 781 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
782 782
783 783 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
784 784
785 785 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
786 786 follows_repository = relationship('Repository', order_by='Repository.repo_name')
787 787
788 788
789 789 class CacheInvalidation(Base, BaseModel):
790 790 __tablename__ = 'cache_invalidation'
791 791 __table_args__ = (
792 792 UniqueConstraint('cache_key'),
793 793 Index('key_idx', 'cache_key'),
794 794 {'extend_existing': True, 'mysql_engine': 'InnoDB',
795 795 'mysql_charset': 'utf8'},
796 796 )
797 797 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
798 798 cache_key = Column("cache_key", String(255), nullable=True, unique=None, default=None)
799 799 cache_args = Column("cache_args", String(255), nullable=True, unique=None, default=None)
800 800 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
801 801
802 802 def __init__(self, cache_key, cache_args=''):
803 803 self.cache_key = cache_key
804 804 self.cache_args = cache_args
805 805 self.cache_active = False
806 806
807 807
808 808 class ChangesetComment(Base, BaseModel):
809 809 __tablename__ = 'changeset_comments'
810 810 __table_args__ = (
811 811 Index('cc_revision_idx', 'revision'),
812 812 {'extend_existing': True, 'mysql_engine': 'InnoDB',
813 813 'mysql_charset': 'utf8'},
814 814 )
815 815 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
816 816 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
817 817 revision = Column('revision', String(40), nullable=True)
818 818 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
819 819 line_no = Column('line_no', Unicode(10), nullable=True)
820 820 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
821 821 f_path = Column('f_path', Unicode(1000), nullable=True)
822 822 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
823 823 text = Column('text', UnicodeText().with_variant(UnicodeText(25000), 'mysql'), nullable=False)
824 824 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
825 825 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
826 826
827 827 author = relationship('User', lazy='joined')
828 828 repo = relationship('Repository')
829 829 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
830 830 pull_request = relationship('PullRequest', lazy='joined')
831 831
832 832 @classmethod
833 833 def get_users(cls, revision=None, pull_request_id=None):
834 834 """
835 835 Returns user associated with this ChangesetComment. ie those
836 836 who actually commented
837 837
838 838 :param cls:
839 839 :param revision:
840 840 """
841 841 q = Session().query(User)\
842 842 .join(ChangesetComment.author)
843 843 if revision:
844 844 q = q.filter(cls.revision == revision)
845 845 elif pull_request_id:
846 846 q = q.filter(cls.pull_request_id == pull_request_id)
847 847 return q.all()
848 848
849 849
850 850 class ChangesetStatus(Base, BaseModel):
851 851 __tablename__ = 'changeset_statuses'
852 852 __table_args__ = (
853 853 Index('cs_revision_idx', 'revision'),
854 854 Index('cs_version_idx', 'version'),
855 855 UniqueConstraint('repo_id', 'revision', 'version'),
856 856 {'extend_existing': True, 'mysql_engine': 'InnoDB',
857 857 'mysql_charset': 'utf8'}
858 858 )
859 859 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
860 860 STATUS_APPROVED = 'approved'
861 861 STATUS_REJECTED = 'rejected'
862 862 STATUS_UNDER_REVIEW = 'under_review'
863 863
864 864 STATUSES = [
865 865 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
866 866 (STATUS_APPROVED, _("Approved")),
867 867 (STATUS_REJECTED, _("Rejected")),
868 868 (STATUS_UNDER_REVIEW, _("Under Review")),
869 869 ]
870 870
871 871 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
872 872 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
873 873 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
874 874 revision = Column('revision', String(40), nullable=False)
875 875 status = Column('status', String(128), nullable=False, default=DEFAULT)
876 876 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
877 877 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
878 878 version = Column('version', Integer(), nullable=False, default=0)
879 879 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
880 880
881 881 author = relationship('User', lazy='joined')
882 882 repo = relationship('Repository')
883 883 comment = relationship('ChangesetComment', lazy='joined')
884 884 pull_request = relationship('PullRequest', lazy='joined')
885 885
886 886
887 887
888 888 class PullRequest(Base, BaseModel):
889 889 __tablename__ = 'pull_requests'
890 890 __table_args__ = (
891 891 {'extend_existing': True, 'mysql_engine': 'InnoDB',
892 892 'mysql_charset': 'utf8'},
893 893 )
894 894
895 895 STATUS_NEW = u'new'
896 896 STATUS_OPEN = u'open'
897 897 STATUS_CLOSED = u'closed'
898 898
899 899 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
900 900 title = Column('title', Unicode(256), nullable=True)
901 901 description = Column('description', UnicodeText().with_variant(UnicodeText(10240), 'mysql'), nullable=True)
902 902 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
903 903 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
904 904 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
905 905 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
906 906 _revisions = Column('revisions', UnicodeText().with_variant(UnicodeText(20500), 'mysql'))
907 907 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
908 908 org_ref = Column('org_ref', Unicode(256), nullable=False)
909 909 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
910 910 other_ref = Column('other_ref', Unicode(256), nullable=False)
911 911
912 912 author = relationship('User', lazy='joined')
913 913 reviewers = relationship('PullRequestReviewers',
914 914 cascade="all, delete, delete-orphan")
915 915 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
916 916 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
917 917 statuses = relationship('ChangesetStatus')
918 918 comments = relationship('ChangesetComment',
919 919 cascade="all, delete, delete-orphan")
920 920
921 921
922 922 class PullRequestReviewers(Base, BaseModel):
923 923 __tablename__ = 'pull_request_reviewers'
924 924 __table_args__ = (
925 925 {'extend_existing': True, 'mysql_engine': 'InnoDB',
926 926 'mysql_charset': 'utf8'},
927 927 )
928 928
929 929 def __init__(self, user=None, pull_request=None):
930 930 self.user = user
931 931 self.pull_request = pull_request
932 932
933 933 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
934 934 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
935 935 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
936 936
937 937 user = relationship('User')
938 938 pull_request = relationship('PullRequest')
939 939
940 940
941 941 class Notification(Base, BaseModel):
942 942 __tablename__ = 'notifications'
943 943 __table_args__ = (
944 944 Index('notification_type_idx', 'type'),
945 945 {'extend_existing': True, 'mysql_engine': 'InnoDB',
946 946 'mysql_charset': 'utf8'},
947 947 )
948 948
949 949 TYPE_CHANGESET_COMMENT = u'cs_comment'
950 950 TYPE_MESSAGE = u'message'
951 951 TYPE_MENTION = u'mention'
952 952 TYPE_REGISTRATION = u'registration'
953 953 TYPE_PULL_REQUEST = u'pull_request'
954 954 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
955 955
956 956 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
957 957 subject = Column('subject', Unicode(512), nullable=True)
958 958 body = Column('body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True)
959 959 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
960 960 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
961 961 type_ = Column('type', Unicode(256))
962 962
963 963 created_by_user = relationship('User')
964 964 notifications_to_users = relationship('UserNotification', lazy='joined',
965 965 cascade="all, delete, delete-orphan")
966 966
967 967
968 968 class UserNotification(Base, BaseModel):
969 969 __tablename__ = 'user_to_notification'
970 970 __table_args__ = (
971 971 UniqueConstraint('user_id', 'notification_id'),
972 972 {'extend_existing': True, 'mysql_engine': 'InnoDB',
973 973 'mysql_charset': 'utf8'}
974 974 )
975 975 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
976 976 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
977 977 read = Column('read', Boolean, default=False)
978 978 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
979 979
980 980 user = relationship('User', lazy="joined")
981 981 notification = relationship('Notification', lazy="joined",
982 982 order_by=lambda: Notification.created_on.desc(),)
983 983
984 984
985 985 class DbMigrateVersion(Base, BaseModel):
986 986 __tablename__ = 'db_migrate_version'
987 987 __table_args__ = (
988 988 {'extend_existing': True, 'mysql_engine': 'InnoDB',
989 989 'mysql_charset': 'utf8'},
990 990 )
991 991 repository_id = Column('repository_id', String(250), primary_key=True)
992 992 repository_path = Column('repository_path', Text)
993 993 version = Column('version', Integer)
@@ -1,1002 +1,1002 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import os
22 22 import time
23 23 import logging
24 24 import datetime
25 25 import traceback
26 26 import hashlib
27 27 import collections
28 28
29 29 from sqlalchemy import *
30 30 from sqlalchemy.ext.hybrid import hybrid_property
31 31 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
32 32 from sqlalchemy.exc import DatabaseError
33 33 from beaker.cache import cache_region, region_invalidate
34 34 from webob.exc import HTTPNotFound
35 35
36 from pylons.i18n.translation import lazy_ugettext as _
36 from rhodecode.translation import _
37 37
38 38 from rhodecode.lib.vcs import get_backend
39 39 from rhodecode.lib.vcs.utils.helpers import get_scm
40 40 from rhodecode.lib.vcs.exceptions import VCSError
41 41 from zope.cachedescriptors.property import Lazy as LazyProperty
42 42 from rhodecode.lib.vcs.backends.base import EmptyCommit
43 43
44 44 from rhodecode.lib.utils2 import str2bool, safe_str, get_commit_safe, \
45 45 safe_unicode, remove_suffix, remove_prefix
46 46 from rhodecode.lib.ext_json import json
47 47 from rhodecode.lib.caching_query import FromCache
48 48
49 49 from rhodecode.model.meta import Base, Session
50 50
51 51 URL_SEP = '/'
52 52 log = logging.getLogger(__name__)
53 53
54 54 #==============================================================================
55 55 # BASE CLASSES
56 56 #==============================================================================
57 57
58 58 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
59 59
60 60
61 61 class BaseModel(object):
62 62 """
63 63 Base Model for all classes
64 64 """
65 65
66 66 @classmethod
67 67 def _get_keys(cls):
68 68 """return column names for this model """
69 69 return class_mapper(cls).c.keys()
70 70
71 71 def get_dict(self):
72 72 """
73 73 return dict with keys and values corresponding
74 74 to this model data """
75 75
76 76 d = {}
77 77 for k in self._get_keys():
78 78 d[k] = getattr(self, k)
79 79
80 80 # also use __json__() if present to get additional fields
81 81 _json_attr = getattr(self, '__json__', None)
82 82 if _json_attr:
83 83 # update with attributes from __json__
84 84 if callable(_json_attr):
85 85 _json_attr = _json_attr()
86 86 for k, val in _json_attr.iteritems():
87 87 d[k] = val
88 88 return d
89 89
90 90 def get_appstruct(self):
91 91 """return list with keys and values tupples corresponding
92 92 to this model data """
93 93
94 94 l = []
95 95 for k in self._get_keys():
96 96 l.append((k, getattr(self, k),))
97 97 return l
98 98
99 99 def populate_obj(self, populate_dict):
100 100 """populate model with data from given populate_dict"""
101 101
102 102 for k in self._get_keys():
103 103 if k in populate_dict:
104 104 setattr(self, k, populate_dict[k])
105 105
106 106 @classmethod
107 107 def query(cls):
108 108 return Session().query(cls)
109 109
110 110 @classmethod
111 111 def get(cls, id_):
112 112 if id_:
113 113 return cls.query().get(id_)
114 114
115 115 @classmethod
116 116 def get_or_404(cls, id_):
117 117 try:
118 118 id_ = int(id_)
119 119 except (TypeError, ValueError):
120 120 raise HTTPNotFound
121 121
122 122 res = cls.query().get(id_)
123 123 if not res:
124 124 raise HTTPNotFound
125 125 return res
126 126
127 127 @classmethod
128 128 def getAll(cls):
129 129 # deprecated and left for backward compatibility
130 130 return cls.get_all()
131 131
132 132 @classmethod
133 133 def get_all(cls):
134 134 return cls.query().all()
135 135
136 136 @classmethod
137 137 def delete(cls, id_):
138 138 obj = cls.query().get(id_)
139 139 Session().delete(obj)
140 140
141 141 def __repr__(self):
142 142 if hasattr(self, '__unicode__'):
143 143 # python repr needs to return str
144 144 return safe_str(self.__unicode__())
145 145 return '<DB:%s>' % (self.__class__.__name__)
146 146
147 147
148 148 class RhodeCodeSetting(Base, BaseModel):
149 149 __tablename__ = 'rhodecode_settings'
150 150 __table_args__ = (
151 151 UniqueConstraint('app_settings_name'),
152 152 {'extend_existing': True, 'mysql_engine': 'InnoDB',
153 153 'mysql_charset': 'utf8'}
154 154 )
155 155 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
156 156 app_settings_name = Column("app_settings_name", String(255), nullable=True, unique=None, default=None)
157 157 _app_settings_value = Column("app_settings_value", String(255), nullable=True, unique=None, default=None)
158 158
159 159 def __init__(self, k='', v=''):
160 160 self.app_settings_name = k
161 161 self.app_settings_value = v
162 162
163 163 @validates('_app_settings_value')
164 164 def validate_settings_value(self, key, val):
165 165 assert type(val) == unicode
166 166 return val
167 167
168 168 @hybrid_property
169 169 def app_settings_value(self):
170 170 v = self._app_settings_value
171 171 if self.app_settings_name in ["ldap_active",
172 172 "default_repo_enable_statistics",
173 173 "default_repo_enable_locking",
174 174 "default_repo_private",
175 175 "default_repo_enable_downloads"]:
176 176 v = str2bool(v)
177 177 return v
178 178
179 179 @app_settings_value.setter
180 180 def app_settings_value(self, val):
181 181 """
182 182 Setter that will always make sure we use unicode in app_settings_value
183 183
184 184 :param val:
185 185 """
186 186 self._app_settings_value = safe_unicode(val)
187 187
188 188 def __unicode__(self):
189 189 return u"<%s('%s:%s')>" % (
190 190 self.__class__.__name__,
191 191 self.app_settings_name, self.app_settings_value
192 192 )
193 193
194 194
195 195 class RhodeCodeUi(Base, BaseModel):
196 196 __tablename__ = 'rhodecode_ui'
197 197 __table_args__ = (
198 198 UniqueConstraint('ui_key'),
199 199 {'extend_existing': True, 'mysql_engine': 'InnoDB',
200 200 'mysql_charset': 'utf8'}
201 201 )
202 202
203 203 HOOK_REPO_SIZE = 'changegroup.repo_size'
204 204 HOOK_PUSH = 'changegroup.push_logger'
205 205 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
206 206 HOOK_PULL = 'outgoing.pull_logger'
207 207 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
208 208
209 209 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
210 210 ui_section = Column("ui_section", String(255), nullable=True, unique=None, default=None)
211 211 ui_key = Column("ui_key", String(255), nullable=True, unique=None, default=None)
212 212 ui_value = Column("ui_value", String(255), nullable=True, unique=None, default=None)
213 213 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
214 214
215 215
216 216
217 217 class User(Base, BaseModel):
218 218 __tablename__ = 'users'
219 219 __table_args__ = (
220 220 UniqueConstraint('username'), UniqueConstraint('email'),
221 221 Index('u_username_idx', 'username'),
222 222 Index('u_email_idx', 'email'),
223 223 {'extend_existing': True, 'mysql_engine': 'InnoDB',
224 224 'mysql_charset': 'utf8'}
225 225 )
226 226 DEFAULT_USER = 'default'
227 227 DEFAULT_PERMISSIONS = [
228 228 'hg.register.manual_activate', 'hg.create.repository',
229 229 'hg.fork.repository', 'repository.read', 'group.read'
230 230 ]
231 231 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
232 232 username = Column("username", String(255), nullable=True, unique=None, default=None)
233 233 password = Column("password", String(255), nullable=True, unique=None, default=None)
234 234 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
235 235 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
236 236 name = Column("firstname", String(255), nullable=True, unique=None, default=None)
237 237 lastname = Column("lastname", String(255), nullable=True, unique=None, default=None)
238 238 _email = Column("email", String(255), nullable=True, unique=None, default=None)
239 239 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
240 240 ldap_dn = Column("ldap_dn", String(255), nullable=True, unique=None, default=None)
241 241 api_key = Column("api_key", String(255), nullable=True, unique=None, default=None)
242 242 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
243 243
244 244 user_log = relationship('UserLog')
245 245 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
246 246
247 247 repositories = relationship('Repository')
248 248 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
249 249 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
250 250
251 251 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
252 252 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
253 253
254 254 group_member = relationship('UserGroupMember', cascade='all')
255 255
256 256 notifications = relationship('UserNotification', cascade='all')
257 257 # notifications assigned to this user
258 258 user_created_notifications = relationship('Notification', cascade='all')
259 259 # comments created by this user
260 260 user_comments = relationship('ChangesetComment', cascade='all')
261 261 user_emails = relationship('UserEmailMap', cascade='all')
262 262
263 263 @hybrid_property
264 264 def email(self):
265 265 return self._email
266 266
267 267 @email.setter
268 268 def email(self, val):
269 269 self._email = val.lower() if val else None
270 270
271 271 @property
272 272 def firstname(self):
273 273 # alias for future
274 274 return self.name
275 275
276 276 @property
277 277 def username_and_name(self):
278 278 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
279 279
280 280 @property
281 281 def full_name(self):
282 282 return '%s %s' % (self.firstname, self.lastname)
283 283
284 284 @property
285 285 def full_contact(self):
286 286 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
287 287
288 288 @property
289 289 def short_contact(self):
290 290 return '%s %s' % (self.firstname, self.lastname)
291 291
292 292 @property
293 293 def is_admin(self):
294 294 return self.admin
295 295
296 296 @classmethod
297 297 def get_by_username(cls, username, case_insensitive=False, cache=False):
298 298 if case_insensitive:
299 299 q = cls.query().filter(cls.username.ilike(username))
300 300 else:
301 301 q = cls.query().filter(cls.username == username)
302 302
303 303 if cache:
304 304 q = q.options(FromCache(
305 305 "sql_cache_short",
306 306 "get_user_%s" % _hash_key(username)
307 307 )
308 308 )
309 309 return q.scalar()
310 310
311 311 @classmethod
312 312 def get_by_auth_token(cls, auth_token, cache=False):
313 313 q = cls.query().filter(cls.api_key == auth_token)
314 314
315 315 if cache:
316 316 q = q.options(FromCache("sql_cache_short",
317 317 "get_auth_token_%s" % auth_token))
318 318 return q.scalar()
319 319
320 320 @classmethod
321 321 def get_by_email(cls, email, case_insensitive=False, cache=False):
322 322 if case_insensitive:
323 323 q = cls.query().filter(cls.email.ilike(email))
324 324 else:
325 325 q = cls.query().filter(cls.email == email)
326 326
327 327 if cache:
328 328 q = q.options(FromCache("sql_cache_short",
329 329 "get_email_key_%s" % email))
330 330
331 331 ret = q.scalar()
332 332 if ret is None:
333 333 q = UserEmailMap.query()
334 334 # try fetching in alternate email map
335 335 if case_insensitive:
336 336 q = q.filter(UserEmailMap.email.ilike(email))
337 337 else:
338 338 q = q.filter(UserEmailMap.email == email)
339 339 q = q.options(joinedload(UserEmailMap.user))
340 340 if cache:
341 341 q = q.options(FromCache("sql_cache_short",
342 342 "get_email_map_key_%s" % email))
343 343 ret = getattr(q.scalar(), 'user', None)
344 344
345 345 return ret
346 346
347 347
348 348 class UserEmailMap(Base, BaseModel):
349 349 __tablename__ = 'user_email_map'
350 350 __table_args__ = (
351 351 Index('uem_email_idx', 'email'),
352 352 UniqueConstraint('email'),
353 353 {'extend_existing': True, 'mysql_engine': 'InnoDB',
354 354 'mysql_charset': 'utf8'}
355 355 )
356 356 __mapper_args__ = {}
357 357
358 358 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
359 359 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
360 360 _email = Column("email", String(255), nullable=True, unique=False, default=None)
361 361 user = relationship('User', lazy='joined')
362 362
363 363 @validates('_email')
364 364 def validate_email(self, key, email):
365 365 # check if this email is not main one
366 366 main_email = Session().query(User).filter(User.email == email).scalar()
367 367 if main_email is not None:
368 368 raise AttributeError('email %s is present is user table' % email)
369 369 return email
370 370
371 371 @hybrid_property
372 372 def email(self):
373 373 return self._email
374 374
375 375 @email.setter
376 376 def email(self, val):
377 377 self._email = val.lower() if val else None
378 378
379 379
380 380 class UserIpMap(Base, BaseModel):
381 381 __tablename__ = 'user_ip_map'
382 382 __table_args__ = (
383 383 UniqueConstraint('user_id', 'ip_addr'),
384 384 {'extend_existing': True, 'mysql_engine': 'InnoDB',
385 385 'mysql_charset': 'utf8'}
386 386 )
387 387 __mapper_args__ = {}
388 388
389 389 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
390 390 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
391 391 ip_addr = Column("ip_addr", String(255), nullable=True, unique=False, default=None)
392 392 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
393 393 user = relationship('User', lazy='joined')
394 394
395 395
396 396 class UserLog(Base, BaseModel):
397 397 __tablename__ = 'user_logs'
398 398 __table_args__ = (
399 399 {'extend_existing': True, 'mysql_engine': 'InnoDB',
400 400 'mysql_charset': 'utf8'},
401 401 )
402 402 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
403 403 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
404 404 username = Column("username", String(255), nullable=True, unique=None, default=None)
405 405 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
406 406 repository_name = Column("repository_name", String(255), nullable=True, unique=None, default=None)
407 407 user_ip = Column("user_ip", String(255), nullable=True, unique=None, default=None)
408 408 action = Column("action", String(1200000), nullable=True, unique=None, default=None)
409 409 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
410 410
411 411
412 412 user = relationship('User')
413 413 repository = relationship('Repository', cascade='')
414 414
415 415
416 416 class UserGroup(Base, BaseModel):
417 417 __tablename__ = 'users_groups'
418 418 __table_args__ = (
419 419 {'extend_existing': True, 'mysql_engine': 'InnoDB',
420 420 'mysql_charset': 'utf8'},
421 421 )
422 422
423 423 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
424 424 users_group_name = Column("users_group_name", String(255), nullable=False, unique=True, default=None)
425 425 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
426 426 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
427 427
428 428 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
429 429 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
430 430 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
431 431
432 432 def __unicode__(self):
433 433 return u'<userGroup(%s)>' % (self.users_group_name)
434 434
435 435 @classmethod
436 436 def get_by_group_name(cls, group_name, cache=False,
437 437 case_insensitive=False):
438 438 if case_insensitive:
439 439 q = cls.query().filter(cls.users_group_name.ilike(group_name))
440 440 else:
441 441 q = cls.query().filter(cls.users_group_name == group_name)
442 442 if cache:
443 443 q = q.options(FromCache(
444 444 "sql_cache_short",
445 445 "get_user_%s" % _hash_key(group_name)
446 446 )
447 447 )
448 448 return q.scalar()
449 449
450 450 @classmethod
451 451 def get(cls, users_group_id, cache=False):
452 452 user_group = cls.query()
453 453 if cache:
454 454 user_group = user_group.options(FromCache("sql_cache_short",
455 455 "get_users_group_%s" % users_group_id))
456 456 return user_group.get(users_group_id)
457 457
458 458
459 459 class UserGroupMember(Base, BaseModel):
460 460 __tablename__ = 'users_groups_members'
461 461 __table_args__ = (
462 462 {'extend_existing': True, 'mysql_engine': 'InnoDB',
463 463 'mysql_charset': 'utf8'},
464 464 )
465 465
466 466 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
467 467 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
468 468 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
469 469
470 470 user = relationship('User', lazy='joined')
471 471 users_group = relationship('UserGroup')
472 472
473 473 def __init__(self, gr_id='', u_id=''):
474 474 self.users_group_id = gr_id
475 475 self.user_id = u_id
476 476
477 477
478 478 class Repository(Base, BaseModel):
479 479 __tablename__ = 'repositories'
480 480 __table_args__ = (
481 481 UniqueConstraint('repo_name'),
482 482 Index('r_repo_name_idx', 'repo_name'),
483 483 {'extend_existing': True, 'mysql_engine': 'InnoDB',
484 484 'mysql_charset': 'utf8'},
485 485 )
486 486
487 487 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
488 488 repo_name = Column("repo_name", String(255), nullable=False, unique=True, default=None)
489 489 clone_uri = Column("clone_uri", String(255), nullable=True, unique=False, default=None)
490 490 repo_type = Column("repo_type", String(255), nullable=False, unique=False, default=None)
491 491 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
492 492 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
493 493 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
494 494 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
495 495 description = Column("description", String(10000), nullable=True, unique=None, default=None)
496 496 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
497 497 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
498 498 landing_rev = Column("landing_revision", String(255), nullable=False, unique=False, default=None)
499 499 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
500 500 _locked = Column("locked", String(255), nullable=True, unique=False, default=None)
501 501 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
502 502
503 503 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
504 504 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
505 505
506 506 user = relationship('User')
507 507 fork = relationship('Repository', remote_side=repo_id)
508 508 group = relationship('RepoGroup')
509 509 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
510 510 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
511 511 stats = relationship('Statistics', cascade='all', uselist=False)
512 512
513 513 followers = relationship('UserFollowing',
514 514 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
515 515 cascade='all')
516 516
517 517 logs = relationship('UserLog')
518 518 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
519 519
520 520 pull_requests_org = relationship('PullRequest',
521 521 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
522 522 cascade="all, delete, delete-orphan")
523 523
524 524 pull_requests_other = relationship('PullRequest',
525 525 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
526 526 cascade="all, delete, delete-orphan")
527 527
528 528 def __unicode__(self):
529 529 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
530 530 safe_unicode(self.repo_name))
531 531
532 532
533 533 @classmethod
534 534 def get_by_repo_name(cls, repo_name):
535 535 q = Session().query(cls).filter(cls.repo_name == repo_name)
536 536 q = q.options(joinedload(Repository.fork))\
537 537 .options(joinedload(Repository.user))\
538 538 .options(joinedload(Repository.group))
539 539 return q.scalar()
540 540
541 541
542 542 class RepoGroup(Base, BaseModel):
543 543 __tablename__ = 'groups'
544 544 __table_args__ = (
545 545 UniqueConstraint('group_name', 'group_parent_id'),
546 546 CheckConstraint('group_id != group_parent_id'),
547 547 {'extend_existing': True, 'mysql_engine': 'InnoDB',
548 548 'mysql_charset': 'utf8'},
549 549 )
550 550 __mapper_args__ = {'order_by': 'group_name'}
551 551
552 552 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
553 553 group_name = Column("group_name", String(255), nullable=False, unique=True, default=None)
554 554 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
555 555 group_description = Column("group_description", String(10000), nullable=True, unique=None, default=None)
556 556 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
557 557
558 558 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
559 559 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
560 560 parent_group = relationship('RepoGroup', remote_side=group_id)
561 561
562 562 def __init__(self, group_name='', parent_group=None):
563 563 self.group_name = group_name
564 564 self.parent_group = parent_group
565 565
566 566 def __unicode__(self):
567 567 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
568 568 self.group_name)
569 569
570 570 @classmethod
571 571 def url_sep(cls):
572 572 return URL_SEP
573 573
574 574 @classmethod
575 575 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
576 576 if case_insensitive:
577 577 gr = cls.query()\
578 578 .filter(cls.group_name.ilike(group_name))
579 579 else:
580 580 gr = cls.query()\
581 581 .filter(cls.group_name == group_name)
582 582 if cache:
583 583 gr = gr.options(FromCache(
584 584 "sql_cache_short",
585 585 "get_group_%s" % _hash_key(group_name)
586 586 )
587 587 )
588 588 return gr.scalar()
589 589
590 590
591 591 class Permission(Base, BaseModel):
592 592 __tablename__ = 'permissions'
593 593 __table_args__ = (
594 594 Index('p_perm_name_idx', 'permission_name'),
595 595 {'extend_existing': True, 'mysql_engine': 'InnoDB',
596 596 'mysql_charset': 'utf8'},
597 597 )
598 598 PERMS = [
599 599 ('repository.none', _('Repository no access')),
600 600 ('repository.read', _('Repository read access')),
601 601 ('repository.write', _('Repository write access')),
602 602 ('repository.admin', _('Repository admin access')),
603 603
604 604 ('group.none', _('Repositories Group no access')),
605 605 ('group.read', _('Repositories Group read access')),
606 606 ('group.write', _('Repositories Group write access')),
607 607 ('group.admin', _('Repositories Group admin access')),
608 608
609 609 ('hg.admin', _('RhodeCode Administrator')),
610 610 ('hg.create.none', _('Repository creation disabled')),
611 611 ('hg.create.repository', _('Repository creation enabled')),
612 612 ('hg.fork.none', _('Repository forking disabled')),
613 613 ('hg.fork.repository', _('Repository forking enabled')),
614 614 ('hg.register.none', _('Register disabled')),
615 615 ('hg.register.manual_activate', _('Register new user with RhodeCode '
616 616 'with manual activation')),
617 617
618 618 ('hg.register.auto_activate', _('Register new user with RhodeCode '
619 619 'with auto activation')),
620 620 ]
621 621
622 622 # defines which permissions are more important higher the more important
623 623 PERM_WEIGHTS = {
624 624 'repository.none': 0,
625 625 'repository.read': 1,
626 626 'repository.write': 3,
627 627 'repository.admin': 4,
628 628
629 629 'group.none': 0,
630 630 'group.read': 1,
631 631 'group.write': 3,
632 632 'group.admin': 4,
633 633
634 634 'hg.fork.none': 0,
635 635 'hg.fork.repository': 1,
636 636 'hg.create.none': 0,
637 637 'hg.create.repository':1
638 638 }
639 639
640 640 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
641 641 permission_name = Column("permission_name", String(255), nullable=True, unique=None, default=None)
642 642 permission_longname = Column("permission_longname", String(255), nullable=True, unique=None, default=None)
643 643
644 644 def __unicode__(self):
645 645 return u"<%s('%s:%s')>" % (
646 646 self.__class__.__name__, self.permission_id, self.permission_name
647 647 )
648 648
649 649 @classmethod
650 650 def get_by_key(cls, key):
651 651 return cls.query().filter(cls.permission_name == key).scalar()
652 652
653 653
654 654 class UserRepoToPerm(Base, BaseModel):
655 655 __tablename__ = 'repo_to_perm'
656 656 __table_args__ = (
657 657 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
658 658 {'extend_existing': True, 'mysql_engine': 'InnoDB',
659 659 'mysql_charset': 'utf8'}
660 660 )
661 661 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
662 662 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
663 663 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
664 664 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
665 665
666 666 user = relationship('User')
667 667 repository = relationship('Repository')
668 668 permission = relationship('Permission')
669 669
670 670 def __unicode__(self):
671 671 return u'<user:%s => %s >' % (self.user, self.repository)
672 672
673 673
674 674 class UserToPerm(Base, BaseModel):
675 675 __tablename__ = 'user_to_perm'
676 676 __table_args__ = (
677 677 UniqueConstraint('user_id', 'permission_id'),
678 678 {'extend_existing': True, 'mysql_engine': 'InnoDB',
679 679 'mysql_charset': 'utf8'}
680 680 )
681 681 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
682 682 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
683 683 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
684 684
685 685 user = relationship('User')
686 686 permission = relationship('Permission', lazy='joined')
687 687
688 688
689 689 class UserGroupRepoToPerm(Base, BaseModel):
690 690 __tablename__ = 'users_group_repo_to_perm'
691 691 __table_args__ = (
692 692 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
693 693 {'extend_existing': True, 'mysql_engine': 'InnoDB',
694 694 'mysql_charset': 'utf8'}
695 695 )
696 696 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
697 697 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
698 698 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
699 699 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
700 700
701 701 users_group = relationship('UserGroup')
702 702 permission = relationship('Permission')
703 703 repository = relationship('Repository')
704 704
705 705 def __unicode__(self):
706 706 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
707 707
708 708
709 709 class UserGroupToPerm(Base, BaseModel):
710 710 __tablename__ = 'users_group_to_perm'
711 711 __table_args__ = (
712 712 UniqueConstraint('users_group_id', 'permission_id',),
713 713 {'extend_existing': True, 'mysql_engine': 'InnoDB',
714 714 'mysql_charset': 'utf8'}
715 715 )
716 716 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
717 717 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
718 718 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
719 719
720 720 users_group = relationship('UserGroup')
721 721 permission = relationship('Permission')
722 722
723 723
724 724 class UserRepoGroupToPerm(Base, BaseModel):
725 725 __tablename__ = 'user_repo_group_to_perm'
726 726 __table_args__ = (
727 727 UniqueConstraint('user_id', 'group_id', 'permission_id'),
728 728 {'extend_existing': True, 'mysql_engine': 'InnoDB',
729 729 'mysql_charset': 'utf8'}
730 730 )
731 731
732 732 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
733 733 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
734 734 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
735 735 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
736 736
737 737 user = relationship('User')
738 738 group = relationship('RepoGroup')
739 739 permission = relationship('Permission')
740 740
741 741
742 742 class UserGroupRepoGroupToPerm(Base, BaseModel):
743 743 __tablename__ = 'users_group_repo_group_to_perm'
744 744 __table_args__ = (
745 745 UniqueConstraint('users_group_id', 'group_id'),
746 746 {'extend_existing': True, 'mysql_engine': 'InnoDB',
747 747 'mysql_charset': 'utf8'}
748 748 )
749 749
750 750 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
751 751 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
752 752 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
753 753 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
754 754
755 755 users_group = relationship('UserGroup')
756 756 permission = relationship('Permission')
757 757 group = relationship('RepoGroup')
758 758
759 759
760 760 class Statistics(Base, BaseModel):
761 761 __tablename__ = 'statistics'
762 762 __table_args__ = (
763 763 UniqueConstraint('repository_id'),
764 764 {'extend_existing': True, 'mysql_engine': 'InnoDB',
765 765 'mysql_charset': 'utf8'}
766 766 )
767 767 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
768 768 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
769 769 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
770 770 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
771 771 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
772 772 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
773 773
774 774 repository = relationship('Repository', single_parent=True)
775 775
776 776
777 777 class UserFollowing(Base, BaseModel):
778 778 __tablename__ = 'user_followings'
779 779 __table_args__ = (
780 780 UniqueConstraint('user_id', 'follows_repository_id'),
781 781 UniqueConstraint('user_id', 'follows_user_id'),
782 782 {'extend_existing': True, 'mysql_engine': 'InnoDB',
783 783 'mysql_charset': 'utf8'}
784 784 )
785 785
786 786 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
787 787 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
788 788 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
789 789 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
790 790 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
791 791
792 792 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
793 793
794 794 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
795 795 follows_repository = relationship('Repository', order_by='Repository.repo_name')
796 796
797 797
798 798 class CacheInvalidation(Base, BaseModel):
799 799 __tablename__ = 'cache_invalidation'
800 800 __table_args__ = (
801 801 UniqueConstraint('cache_key'),
802 802 Index('key_idx', 'cache_key'),
803 803 {'extend_existing': True, 'mysql_engine': 'InnoDB',
804 804 'mysql_charset': 'utf8'},
805 805 )
806 806 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
807 807 cache_key = Column("cache_key", String(255), nullable=True, unique=None, default=None)
808 808 cache_args = Column("cache_args", String(255), nullable=True, unique=None, default=None)
809 809 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
810 810
811 811 def __init__(self, cache_key, cache_args=''):
812 812 self.cache_key = cache_key
813 813 self.cache_args = cache_args
814 814 self.cache_active = False
815 815
816 816
817 817 class ChangesetComment(Base, BaseModel):
818 818 __tablename__ = 'changeset_comments'
819 819 __table_args__ = (
820 820 Index('cc_revision_idx', 'revision'),
821 821 {'extend_existing': True, 'mysql_engine': 'InnoDB',
822 822 'mysql_charset': 'utf8'},
823 823 )
824 824 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
825 825 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
826 826 revision = Column('revision', String(40), nullable=True)
827 827 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
828 828 line_no = Column('line_no', Unicode(10), nullable=True)
829 829 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
830 830 f_path = Column('f_path', Unicode(1000), nullable=True)
831 831 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
832 832 text = Column('text', UnicodeText().with_variant(UnicodeText(25000), 'mysql'), nullable=False)
833 833 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
834 834 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
835 835
836 836 author = relationship('User', lazy='joined')
837 837 repo = relationship('Repository')
838 838 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
839 839 pull_request = relationship('PullRequest', lazy='joined')
840 840
841 841 @classmethod
842 842 def get_users(cls, revision=None, pull_request_id=None):
843 843 """
844 844 Returns user associated with this ChangesetComment. ie those
845 845 who actually commented
846 846
847 847 :param cls:
848 848 :param revision:
849 849 """
850 850 q = Session().query(User)\
851 851 .join(ChangesetComment.author)
852 852 if revision:
853 853 q = q.filter(cls.revision == revision)
854 854 elif pull_request_id:
855 855 q = q.filter(cls.pull_request_id == pull_request_id)
856 856 return q.all()
857 857
858 858
859 859 class ChangesetStatus(Base, BaseModel):
860 860 __tablename__ = 'changeset_statuses'
861 861 __table_args__ = (
862 862 Index('cs_revision_idx', 'revision'),
863 863 Index('cs_version_idx', 'version'),
864 864 UniqueConstraint('repo_id', 'revision', 'version'),
865 865 {'extend_existing': True, 'mysql_engine': 'InnoDB',
866 866 'mysql_charset': 'utf8'}
867 867 )
868 868 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
869 869 STATUS_APPROVED = 'approved'
870 870 STATUS_REJECTED = 'rejected'
871 871 STATUS_UNDER_REVIEW = 'under_review'
872 872
873 873 STATUSES = [
874 874 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
875 875 (STATUS_APPROVED, _("Approved")),
876 876 (STATUS_REJECTED, _("Rejected")),
877 877 (STATUS_UNDER_REVIEW, _("Under Review")),
878 878 ]
879 879
880 880 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
881 881 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
882 882 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
883 883 revision = Column('revision', String(40), nullable=False)
884 884 status = Column('status', String(128), nullable=False, default=DEFAULT)
885 885 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
886 886 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
887 887 version = Column('version', Integer(), nullable=False, default=0)
888 888 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
889 889
890 890 author = relationship('User', lazy='joined')
891 891 repo = relationship('Repository')
892 892 comment = relationship('ChangesetComment', lazy='joined')
893 893 pull_request = relationship('PullRequest', lazy='joined')
894 894
895 895
896 896
897 897 class PullRequest(Base, BaseModel):
898 898 __tablename__ = 'pull_requests'
899 899 __table_args__ = (
900 900 {'extend_existing': True, 'mysql_engine': 'InnoDB',
901 901 'mysql_charset': 'utf8'},
902 902 )
903 903
904 904 STATUS_NEW = u'new'
905 905 STATUS_OPEN = u'open'
906 906 STATUS_CLOSED = u'closed'
907 907
908 908 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
909 909 title = Column('title', Unicode(256), nullable=True)
910 910 description = Column('description', UnicodeText().with_variant(UnicodeText(10240), 'mysql'), nullable=True)
911 911 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
912 912 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
913 913 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
914 914 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
915 915 _revisions = Column('revisions', UnicodeText().with_variant(UnicodeText(20500), 'mysql'))
916 916 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
917 917 org_ref = Column('org_ref', Unicode(256), nullable=False)
918 918 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
919 919 other_ref = Column('other_ref', Unicode(256), nullable=False)
920 920
921 921 author = relationship('User', lazy='joined')
922 922 reviewers = relationship('PullRequestReviewers',
923 923 cascade="all, delete, delete-orphan")
924 924 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
925 925 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
926 926 statuses = relationship('ChangesetStatus')
927 927 comments = relationship('ChangesetComment',
928 928 cascade="all, delete, delete-orphan")
929 929
930 930
931 931 class PullRequestReviewers(Base, BaseModel):
932 932 __tablename__ = 'pull_request_reviewers'
933 933 __table_args__ = (
934 934 {'extend_existing': True, 'mysql_engine': 'InnoDB',
935 935 'mysql_charset': 'utf8'},
936 936 )
937 937
938 938 def __init__(self, user=None, pull_request=None):
939 939 self.user = user
940 940 self.pull_request = pull_request
941 941
942 942 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
943 943 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
944 944 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
945 945
946 946 user = relationship('User')
947 947 pull_request = relationship('PullRequest')
948 948
949 949
950 950 class Notification(Base, BaseModel):
951 951 __tablename__ = 'notifications'
952 952 __table_args__ = (
953 953 Index('notification_type_idx', 'type'),
954 954 {'extend_existing': True, 'mysql_engine': 'InnoDB',
955 955 'mysql_charset': 'utf8'},
956 956 )
957 957
958 958 TYPE_CHANGESET_COMMENT = u'cs_comment'
959 959 TYPE_MESSAGE = u'message'
960 960 TYPE_MENTION = u'mention'
961 961 TYPE_REGISTRATION = u'registration'
962 962 TYPE_PULL_REQUEST = u'pull_request'
963 963 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
964 964
965 965 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
966 966 subject = Column('subject', Unicode(512), nullable=True)
967 967 body = Column('body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True)
968 968 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
969 969 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
970 970 type_ = Column('type', Unicode(256))
971 971
972 972 created_by_user = relationship('User')
973 973 notifications_to_users = relationship('UserNotification', lazy='joined',
974 974 cascade="all, delete, delete-orphan")
975 975
976 976
977 977 class UserNotification(Base, BaseModel):
978 978 __tablename__ = 'user_to_notification'
979 979 __table_args__ = (
980 980 UniqueConstraint('user_id', 'notification_id'),
981 981 {'extend_existing': True, 'mysql_engine': 'InnoDB',
982 982 'mysql_charset': 'utf8'}
983 983 )
984 984 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
985 985 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
986 986 read = Column('read', Boolean, default=False)
987 987 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
988 988
989 989 user = relationship('User', lazy="joined")
990 990 notification = relationship('Notification', lazy="joined",
991 991 order_by=lambda: Notification.created_on.desc(),)
992 992
993 993
994 994 class DbMigrateVersion(Base, BaseModel):
995 995 __tablename__ = 'db_migrate_version'
996 996 __table_args__ = (
997 997 {'extend_existing': True, 'mysql_engine': 'InnoDB',
998 998 'mysql_charset': 'utf8'},
999 999 )
1000 1000 repository_id = Column('repository_id', String(250), primary_key=True)
1001 1001 repository_path = Column('repository_path', Text)
1002 1002 version = Column('version', Integer)
@@ -1,1087 +1,1087 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import os
22 22 import time
23 23 import logging
24 24 import datetime
25 25 import traceback
26 26 import hashlib
27 27 import collections
28 28
29 29 from sqlalchemy import *
30 30 from sqlalchemy.ext.hybrid import hybrid_property
31 31 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
32 32 from sqlalchemy.exc import DatabaseError
33 33 from beaker.cache import cache_region, region_invalidate
34 34 from webob.exc import HTTPNotFound
35 35
36 from pylons.i18n.translation import lazy_ugettext as _
36 from rhodecode.translation import _
37 37
38 38 from rhodecode.lib.vcs import get_backend
39 39 from rhodecode.lib.vcs.utils.helpers import get_scm
40 40 from rhodecode.lib.vcs.exceptions import VCSError
41 41 from zope.cachedescriptors.property import Lazy as LazyProperty
42 42 from rhodecode.lib.vcs.backends.base import EmptyCommit
43 43
44 44 from rhodecode.lib.utils2 import str2bool, safe_str, get_commit_safe, \
45 45 safe_unicode, remove_suffix, remove_prefix, time_to_datetime
46 46 from rhodecode.lib.ext_json import json
47 47 from rhodecode.lib.caching_query import FromCache
48 48
49 49 from rhodecode.model.meta import Base, Session
50 50
51 51 URL_SEP = '/'
52 52 log = logging.getLogger(__name__)
53 53
54 54 #==============================================================================
55 55 # BASE CLASSES
56 56 #==============================================================================
57 57
58 58 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
59 59
60 60
61 61 class BaseModel(object):
62 62 """
63 63 Base Model for all classes
64 64 """
65 65
66 66 @classmethod
67 67 def _get_keys(cls):
68 68 """return column names for this model """
69 69 return class_mapper(cls).c.keys()
70 70
71 71 def get_dict(self):
72 72 """
73 73 return dict with keys and values corresponding
74 74 to this model data """
75 75
76 76 d = {}
77 77 for k in self._get_keys():
78 78 d[k] = getattr(self, k)
79 79
80 80 # also use __json__() if present to get additional fields
81 81 _json_attr = getattr(self, '__json__', None)
82 82 if _json_attr:
83 83 # update with attributes from __json__
84 84 if callable(_json_attr):
85 85 _json_attr = _json_attr()
86 86 for k, val in _json_attr.iteritems():
87 87 d[k] = val
88 88 return d
89 89
90 90 def get_appstruct(self):
91 91 """return list with keys and values tupples corresponding
92 92 to this model data """
93 93
94 94 l = []
95 95 for k in self._get_keys():
96 96 l.append((k, getattr(self, k),))
97 97 return l
98 98
99 99 def populate_obj(self, populate_dict):
100 100 """populate model with data from given populate_dict"""
101 101
102 102 for k in self._get_keys():
103 103 if k in populate_dict:
104 104 setattr(self, k, populate_dict[k])
105 105
106 106 @classmethod
107 107 def query(cls):
108 108 return Session().query(cls)
109 109
110 110 @classmethod
111 111 def get(cls, id_):
112 112 if id_:
113 113 return cls.query().get(id_)
114 114
115 115 @classmethod
116 116 def get_or_404(cls, id_):
117 117 try:
118 118 id_ = int(id_)
119 119 except (TypeError, ValueError):
120 120 raise HTTPNotFound
121 121
122 122 res = cls.query().get(id_)
123 123 if not res:
124 124 raise HTTPNotFound
125 125 return res
126 126
127 127 @classmethod
128 128 def getAll(cls):
129 129 # deprecated and left for backward compatibility
130 130 return cls.get_all()
131 131
132 132 @classmethod
133 133 def get_all(cls):
134 134 return cls.query().all()
135 135
136 136 @classmethod
137 137 def delete(cls, id_):
138 138 obj = cls.query().get(id_)
139 139 Session().delete(obj)
140 140
141 141 def __repr__(self):
142 142 if hasattr(self, '__unicode__'):
143 143 # python repr needs to return str
144 144 return safe_str(self.__unicode__())
145 145 return '<DB:%s>' % (self.__class__.__name__)
146 146
147 147
148 148 class RhodeCodeSetting(Base, BaseModel):
149 149 __tablename__ = 'rhodecode_settings'
150 150 __table_args__ = (
151 151 UniqueConstraint('app_settings_name'),
152 152 {'extend_existing': True, 'mysql_engine': 'InnoDB',
153 153 'mysql_charset': 'utf8'}
154 154 )
155 155 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
156 156 app_settings_name = Column("app_settings_name", String(255), nullable=True, unique=None, default=None)
157 157 _app_settings_value = Column("app_settings_value", String(255), nullable=True, unique=None, default=None)
158 158
159 159 def __init__(self, k='', v=''):
160 160 self.app_settings_name = k
161 161 self.app_settings_value = v
162 162
163 163 @validates('_app_settings_value')
164 164 def validate_settings_value(self, key, val):
165 165 assert type(val) == unicode
166 166 return val
167 167
168 168 @hybrid_property
169 169 def app_settings_value(self):
170 170 v = self._app_settings_value
171 171 if self.app_settings_name in ["ldap_active",
172 172 "default_repo_enable_statistics",
173 173 "default_repo_enable_locking",
174 174 "default_repo_private",
175 175 "default_repo_enable_downloads"]:
176 176 v = str2bool(v)
177 177 return v
178 178
179 179 @app_settings_value.setter
180 180 def app_settings_value(self, val):
181 181 """
182 182 Setter that will always make sure we use unicode in app_settings_value
183 183
184 184 :param val:
185 185 """
186 186 self._app_settings_value = safe_unicode(val)
187 187
188 188 def __unicode__(self):
189 189 return u"<%s('%s:%s')>" % (
190 190 self.__class__.__name__,
191 191 self.app_settings_name, self.app_settings_value
192 192 )
193 193
194 194
195 195 class RhodeCodeUi(Base, BaseModel):
196 196 __tablename__ = 'rhodecode_ui'
197 197 __table_args__ = (
198 198 UniqueConstraint('ui_key'),
199 199 {'extend_existing': True, 'mysql_engine': 'InnoDB',
200 200 'mysql_charset': 'utf8'}
201 201 )
202 202
203 203 HOOK_REPO_SIZE = 'changegroup.repo_size'
204 204 HOOK_PUSH = 'changegroup.push_logger'
205 205 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
206 206 HOOK_PULL = 'outgoing.pull_logger'
207 207 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
208 208
209 209 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
210 210 ui_section = Column("ui_section", String(255), nullable=True, unique=None, default=None)
211 211 ui_key = Column("ui_key", String(255), nullable=True, unique=None, default=None)
212 212 ui_value = Column("ui_value", String(255), nullable=True, unique=None, default=None)
213 213 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
214 214
215 215
216 216
217 217 class User(Base, BaseModel):
218 218 __tablename__ = 'users'
219 219 __table_args__ = (
220 220 UniqueConstraint('username'), UniqueConstraint('email'),
221 221 Index('u_username_idx', 'username'),
222 222 Index('u_email_idx', 'email'),
223 223 {'extend_existing': True, 'mysql_engine': 'InnoDB',
224 224 'mysql_charset': 'utf8'}
225 225 )
226 226 DEFAULT_USER = 'default'
227 227 DEFAULT_PERMISSIONS = [
228 228 'hg.register.manual_activate', 'hg.create.repository',
229 229 'hg.fork.repository', 'repository.read', 'group.read'
230 230 ]
231 231 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
232 232 username = Column("username", String(255), nullable=True, unique=None, default=None)
233 233 password = Column("password", String(255), nullable=True, unique=None, default=None)
234 234 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
235 235 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
236 236 name = Column("firstname", String(255), nullable=True, unique=None, default=None)
237 237 lastname = Column("lastname", String(255), nullable=True, unique=None, default=None)
238 238 _email = Column("email", String(255), nullable=True, unique=None, default=None)
239 239 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
240 240 ldap_dn = Column("ldap_dn", String(255), nullable=True, unique=None, default=None)
241 241 api_key = Column("api_key", String(255), nullable=True, unique=None, default=None)
242 242 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
243 243
244 244 user_log = relationship('UserLog')
245 245 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
246 246
247 247 repositories = relationship('Repository')
248 248 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
249 249 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
250 250
251 251 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
252 252 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
253 253
254 254 group_member = relationship('UserGroupMember', cascade='all')
255 255
256 256 notifications = relationship('UserNotification', cascade='all')
257 257 # notifications assigned to this user
258 258 user_created_notifications = relationship('Notification', cascade='all')
259 259 # comments created by this user
260 260 user_comments = relationship('ChangesetComment', cascade='all')
261 261 user_emails = relationship('UserEmailMap', cascade='all')
262 262
263 263 @hybrid_property
264 264 def email(self):
265 265 return self._email
266 266
267 267 @email.setter
268 268 def email(self, val):
269 269 self._email = val.lower() if val else None
270 270
271 271 @property
272 272 def firstname(self):
273 273 # alias for future
274 274 return self.name
275 275
276 276 @property
277 277 def username_and_name(self):
278 278 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
279 279
280 280 @property
281 281 def full_name(self):
282 282 return '%s %s' % (self.firstname, self.lastname)
283 283
284 284 @property
285 285 def full_contact(self):
286 286 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
287 287
288 288 @property
289 289 def short_contact(self):
290 290 return '%s %s' % (self.firstname, self.lastname)
291 291
292 292 @property
293 293 def is_admin(self):
294 294 return self.admin
295 295
296 296 @classmethod
297 297 def get_by_username(cls, username, case_insensitive=False, cache=False):
298 298 if case_insensitive:
299 299 q = cls.query().filter(cls.username.ilike(username))
300 300 else:
301 301 q = cls.query().filter(cls.username == username)
302 302
303 303 if cache:
304 304 q = q.options(FromCache(
305 305 "sql_cache_short",
306 306 "get_user_%s" % _hash_key(username)
307 307 )
308 308 )
309 309 return q.scalar()
310 310
311 311 @classmethod
312 312 def get_by_auth_token(cls, auth_token, cache=False):
313 313 q = cls.query().filter(cls.api_key == auth_token)
314 314
315 315 if cache:
316 316 q = q.options(FromCache("sql_cache_short",
317 317 "get_auth_token_%s" % auth_token))
318 318 return q.scalar()
319 319
320 320 @classmethod
321 321 def get_by_email(cls, email, case_insensitive=False, cache=False):
322 322 if case_insensitive:
323 323 q = cls.query().filter(cls.email.ilike(email))
324 324 else:
325 325 q = cls.query().filter(cls.email == email)
326 326
327 327 if cache:
328 328 q = q.options(FromCache("sql_cache_short",
329 329 "get_email_key_%s" % email))
330 330
331 331 ret = q.scalar()
332 332 if ret is None:
333 333 q = UserEmailMap.query()
334 334 # try fetching in alternate email map
335 335 if case_insensitive:
336 336 q = q.filter(UserEmailMap.email.ilike(email))
337 337 else:
338 338 q = q.filter(UserEmailMap.email == email)
339 339 q = q.options(joinedload(UserEmailMap.user))
340 340 if cache:
341 341 q = q.options(FromCache("sql_cache_short",
342 342 "get_email_map_key_%s" % email))
343 343 ret = getattr(q.scalar(), 'user', None)
344 344
345 345 return ret
346 346
347 347
348 348 class UserEmailMap(Base, BaseModel):
349 349 __tablename__ = 'user_email_map'
350 350 __table_args__ = (
351 351 Index('uem_email_idx', 'email'),
352 352 UniqueConstraint('email'),
353 353 {'extend_existing': True, 'mysql_engine': 'InnoDB',
354 354 'mysql_charset': 'utf8'}
355 355 )
356 356 __mapper_args__ = {}
357 357
358 358 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
359 359 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
360 360 _email = Column("email", String(255), nullable=True, unique=False, default=None)
361 361 user = relationship('User', lazy='joined')
362 362
363 363 @validates('_email')
364 364 def validate_email(self, key, email):
365 365 # check if this email is not main one
366 366 main_email = Session().query(User).filter(User.email == email).scalar()
367 367 if main_email is not None:
368 368 raise AttributeError('email %s is present is user table' % email)
369 369 return email
370 370
371 371 @hybrid_property
372 372 def email(self):
373 373 return self._email
374 374
375 375 @email.setter
376 376 def email(self, val):
377 377 self._email = val.lower() if val else None
378 378
379 379
380 380 class UserIpMap(Base, BaseModel):
381 381 __tablename__ = 'user_ip_map'
382 382 __table_args__ = (
383 383 UniqueConstraint('user_id', 'ip_addr'),
384 384 {'extend_existing': True, 'mysql_engine': 'InnoDB',
385 385 'mysql_charset': 'utf8'}
386 386 )
387 387 __mapper_args__ = {}
388 388
389 389 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
390 390 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
391 391 ip_addr = Column("ip_addr", String(255), nullable=True, unique=False, default=None)
392 392 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
393 393 user = relationship('User', lazy='joined')
394 394
395 395
396 396 class UserLog(Base, BaseModel):
397 397 __tablename__ = 'user_logs'
398 398 __table_args__ = (
399 399 {'extend_existing': True, 'mysql_engine': 'InnoDB',
400 400 'mysql_charset': 'utf8'},
401 401 )
402 402 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
403 403 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
404 404 username = Column("username", String(255), nullable=True, unique=None, default=None)
405 405 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
406 406 repository_name = Column("repository_name", String(255), nullable=True, unique=None, default=None)
407 407 user_ip = Column("user_ip", String(255), nullable=True, unique=None, default=None)
408 408 action = Column("action", String(1200000), nullable=True, unique=None, default=None)
409 409 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
410 410
411 411
412 412 user = relationship('User')
413 413 repository = relationship('Repository', cascade='')
414 414
415 415
416 416 class UserGroup(Base, BaseModel):
417 417 __tablename__ = 'users_groups'
418 418 __table_args__ = (
419 419 {'extend_existing': True, 'mysql_engine': 'InnoDB',
420 420 'mysql_charset': 'utf8'},
421 421 )
422 422
423 423 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
424 424 users_group_name = Column("users_group_name", String(255), nullable=False, unique=True, default=None)
425 425 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
426 426 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
427 427
428 428 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
429 429 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
430 430 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
431 431
432 432 def __unicode__(self):
433 433 return u'<userGroup(%s)>' % (self.users_group_name)
434 434
435 435 @classmethod
436 436 def get_by_group_name(cls, group_name, cache=False,
437 437 case_insensitive=False):
438 438 if case_insensitive:
439 439 q = cls.query().filter(cls.users_group_name.ilike(group_name))
440 440 else:
441 441 q = cls.query().filter(cls.users_group_name == group_name)
442 442 if cache:
443 443 q = q.options(FromCache(
444 444 "sql_cache_short",
445 445 "get_user_%s" % _hash_key(group_name)
446 446 )
447 447 )
448 448 return q.scalar()
449 449
450 450 @classmethod
451 451 def get(cls, users_group_id, cache=False):
452 452 user_group = cls.query()
453 453 if cache:
454 454 user_group = user_group.options(FromCache("sql_cache_short",
455 455 "get_users_group_%s" % users_group_id))
456 456 return user_group.get(users_group_id)
457 457
458 458
459 459 class UserGroupMember(Base, BaseModel):
460 460 __tablename__ = 'users_groups_members'
461 461 __table_args__ = (
462 462 {'extend_existing': True, 'mysql_engine': 'InnoDB',
463 463 'mysql_charset': 'utf8'},
464 464 )
465 465
466 466 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
467 467 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
468 468 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
469 469
470 470 user = relationship('User', lazy='joined')
471 471 users_group = relationship('UserGroup')
472 472
473 473 def __init__(self, gr_id='', u_id=''):
474 474 self.users_group_id = gr_id
475 475 self.user_id = u_id
476 476
477 477
478 478 class RepositoryField(Base, BaseModel):
479 479 __tablename__ = 'repositories_fields'
480 480 __table_args__ = (
481 481 UniqueConstraint('repository_id', 'field_key'), # no-multi field
482 482 {'extend_existing': True, 'mysql_engine': 'InnoDB',
483 483 'mysql_charset': 'utf8'},
484 484 )
485 485 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
486 486
487 487 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
488 488 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
489 489 field_key = Column("field_key", String(250))
490 490 field_label = Column("field_label", String(1024), nullable=False)
491 491 field_value = Column("field_value", String(10000), nullable=False)
492 492 field_desc = Column("field_desc", String(1024), nullable=False)
493 493 field_type = Column("field_type", String(256), nullable=False, unique=None)
494 494 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
495 495
496 496 repository = relationship('Repository')
497 497
498 498 @classmethod
499 499 def get_by_key_name(cls, key, repo):
500 500 row = cls.query()\
501 501 .filter(cls.repository == repo)\
502 502 .filter(cls.field_key == key).scalar()
503 503 return row
504 504
505 505
506 506 class Repository(Base, BaseModel):
507 507 __tablename__ = 'repositories'
508 508 __table_args__ = (
509 509 UniqueConstraint('repo_name'),
510 510 Index('r_repo_name_idx', 'repo_name'),
511 511 {'extend_existing': True, 'mysql_engine': 'InnoDB',
512 512 'mysql_charset': 'utf8'},
513 513 )
514 514
515 515 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
516 516 repo_name = Column("repo_name", String(255), nullable=False, unique=True, default=None)
517 517 clone_uri = Column("clone_uri", String(255), nullable=True, unique=False, default=None)
518 518 repo_type = Column("repo_type", String(255), nullable=False, unique=False, default=None)
519 519 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
520 520 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
521 521 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
522 522 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
523 523 description = Column("description", String(10000), nullable=True, unique=None, default=None)
524 524 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
525 525 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
526 526 landing_rev = Column("landing_revision", String(255), nullable=False, unique=False, default=None)
527 527 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
528 528 _locked = Column("locked", String(255), nullable=True, unique=False, default=None)
529 529 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
530 530
531 531 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
532 532 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
533 533
534 534 user = relationship('User')
535 535 fork = relationship('Repository', remote_side=repo_id)
536 536 group = relationship('RepoGroup')
537 537 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
538 538 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
539 539 stats = relationship('Statistics', cascade='all', uselist=False)
540 540
541 541 followers = relationship('UserFollowing',
542 542 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
543 543 cascade='all')
544 544 extra_fields = relationship('RepositoryField',
545 545 cascade="all, delete, delete-orphan")
546 546
547 547 logs = relationship('UserLog')
548 548 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
549 549
550 550 pull_requests_org = relationship('PullRequest',
551 551 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
552 552 cascade="all, delete, delete-orphan")
553 553
554 554 pull_requests_other = relationship('PullRequest',
555 555 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
556 556 cascade="all, delete, delete-orphan")
557 557
558 558 def __unicode__(self):
559 559 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
560 560 safe_unicode(self.repo_name))
561 561
562 562 #NOTE for this migration we are required tio have it
563 563 @hybrid_property
564 564 def changeset_cache(self):
565 565 from rhodecode.lib.vcs.backends.base import EmptyCommit
566 566 dummy = EmptyCommit().__json__()
567 567 if not self._changeset_cache:
568 568 return dummy
569 569 try:
570 570 return json.loads(self._changeset_cache)
571 571 except TypeError:
572 572 return dummy
573 573
574 574 @changeset_cache.setter
575 575 def changeset_cache(self, val):
576 576 try:
577 577 self._changeset_cache = json.dumps(val)
578 578 except Exception:
579 579 log.error(traceback.format_exc())
580 580
581 581 @classmethod
582 582 def get_by_repo_name(cls, repo_name):
583 583 q = Session().query(cls).filter(cls.repo_name == repo_name)
584 584 q = q.options(joinedload(Repository.fork))\
585 585 .options(joinedload(Repository.user))\
586 586 .options(joinedload(Repository.group))
587 587 return q.scalar()
588 588
589 589 #NOTE this is required for this migration to work
590 590 def update_commit_cache(self, cs_cache=None):
591 591 """
592 592 Update cache of last changeset for repository, keys should be::
593 593
594 594 short_id
595 595 raw_id
596 596 revision
597 597 message
598 598 date
599 599 author
600 600
601 601 :param cs_cache:
602 602 """
603 603 from rhodecode.lib.vcs.backends.base import BaseChangeset
604 604 if cs_cache is None:
605 605 cs_cache = EmptyCommit()
606 606 # Note: Using always the empty commit here in case we are
607 607 # upgrading towards version 3.0 and above. Reason is that in this
608 608 # case the vcsclient connection is not available and things
609 609 # would explode here.
610 610
611 611 if isinstance(cs_cache, BaseChangeset):
612 612 cs_cache = cs_cache.__json__()
613 613
614 614 if (cs_cache != self.changeset_cache or not self.changeset_cache):
615 615 _default = datetime.datetime.fromtimestamp(0)
616 616 last_change = cs_cache.get('date') or _default
617 617 log.debug('updated repo %s with new cs cache %s'
618 618 % (self.repo_name, cs_cache))
619 619 self.updated_on = last_change
620 620 self.changeset_cache = cs_cache
621 621 Session().add(self)
622 622 Session().commit()
623 623 else:
624 624 log.debug('Skipping repo:%s already with latest changes'
625 625 % self.repo_name)
626 626
627 627 class RepoGroup(Base, BaseModel):
628 628 __tablename__ = 'groups'
629 629 __table_args__ = (
630 630 UniqueConstraint('group_name', 'group_parent_id'),
631 631 CheckConstraint('group_id != group_parent_id'),
632 632 {'extend_existing': True, 'mysql_engine': 'InnoDB',
633 633 'mysql_charset': 'utf8'},
634 634 )
635 635 __mapper_args__ = {'order_by': 'group_name'}
636 636
637 637 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
638 638 group_name = Column("group_name", String(255), nullable=False, unique=True, default=None)
639 639 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
640 640 group_description = Column("group_description", String(10000), nullable=True, unique=None, default=None)
641 641 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
642 642
643 643 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
644 644 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
645 645 parent_group = relationship('RepoGroup', remote_side=group_id)
646 646
647 647 def __init__(self, group_name='', parent_group=None):
648 648 self.group_name = group_name
649 649 self.parent_group = parent_group
650 650
651 651 def __unicode__(self):
652 652 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
653 653 self.group_name)
654 654
655 655 @classmethod
656 656 def url_sep(cls):
657 657 return URL_SEP
658 658
659 659 @classmethod
660 660 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
661 661 if case_insensitive:
662 662 gr = cls.query()\
663 663 .filter(cls.group_name.ilike(group_name))
664 664 else:
665 665 gr = cls.query()\
666 666 .filter(cls.group_name == group_name)
667 667 if cache:
668 668 gr = gr.options(FromCache(
669 669 "sql_cache_short",
670 670 "get_group_%s" % _hash_key(group_name)
671 671 )
672 672 )
673 673 return gr.scalar()
674 674
675 675
676 676 class Permission(Base, BaseModel):
677 677 __tablename__ = 'permissions'
678 678 __table_args__ = (
679 679 Index('p_perm_name_idx', 'permission_name'),
680 680 {'extend_existing': True, 'mysql_engine': 'InnoDB',
681 681 'mysql_charset': 'utf8'},
682 682 )
683 683 PERMS = [
684 684 ('repository.none', _('Repository no access')),
685 685 ('repository.read', _('Repository read access')),
686 686 ('repository.write', _('Repository write access')),
687 687 ('repository.admin', _('Repository admin access')),
688 688
689 689 ('group.none', _('Repository group no access')),
690 690 ('group.read', _('Repository group read access')),
691 691 ('group.write', _('Repository group write access')),
692 692 ('group.admin', _('Repository group admin access')),
693 693
694 694 ('hg.admin', _('RhodeCode Administrator')),
695 695 ('hg.create.none', _('Repository creation disabled')),
696 696 ('hg.create.repository', _('Repository creation enabled')),
697 697 ('hg.fork.none', _('Repository forking disabled')),
698 698 ('hg.fork.repository', _('Repository forking enabled')),
699 699 ('hg.register.none', _('Register disabled')),
700 700 ('hg.register.manual_activate', _('Register new user with RhodeCode '
701 701 'with manual activation')),
702 702
703 703 ('hg.register.auto_activate', _('Register new user with RhodeCode '
704 704 'with auto activation')),
705 705 ]
706 706
707 707 # defines which permissions are more important higher the more important
708 708 PERM_WEIGHTS = {
709 709 'repository.none': 0,
710 710 'repository.read': 1,
711 711 'repository.write': 3,
712 712 'repository.admin': 4,
713 713
714 714 'group.none': 0,
715 715 'group.read': 1,
716 716 'group.write': 3,
717 717 'group.admin': 4,
718 718
719 719 'hg.fork.none': 0,
720 720 'hg.fork.repository': 1,
721 721 'hg.create.none': 0,
722 722 'hg.create.repository':1
723 723 }
724 724
725 725 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
726 726 permission_name = Column("permission_name", String(255), nullable=True, unique=None, default=None)
727 727 permission_longname = Column("permission_longname", String(255), nullable=True, unique=None, default=None)
728 728
729 729 def __unicode__(self):
730 730 return u"<%s('%s:%s')>" % (
731 731 self.__class__.__name__, self.permission_id, self.permission_name
732 732 )
733 733
734 734 @classmethod
735 735 def get_by_key(cls, key):
736 736 return cls.query().filter(cls.permission_name == key).scalar()
737 737
738 738
739 739 class UserRepoToPerm(Base, BaseModel):
740 740 __tablename__ = 'repo_to_perm'
741 741 __table_args__ = (
742 742 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
743 743 {'extend_existing': True, 'mysql_engine': 'InnoDB',
744 744 'mysql_charset': 'utf8'}
745 745 )
746 746 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
747 747 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
748 748 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
749 749 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
750 750
751 751 user = relationship('User')
752 752 repository = relationship('Repository')
753 753 permission = relationship('Permission')
754 754
755 755 def __unicode__(self):
756 756 return u'<user:%s => %s >' % (self.user, self.repository)
757 757
758 758
759 759 class UserToPerm(Base, BaseModel):
760 760 __tablename__ = 'user_to_perm'
761 761 __table_args__ = (
762 762 UniqueConstraint('user_id', 'permission_id'),
763 763 {'extend_existing': True, 'mysql_engine': 'InnoDB',
764 764 'mysql_charset': 'utf8'}
765 765 )
766 766 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
767 767 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
768 768 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
769 769
770 770 user = relationship('User')
771 771 permission = relationship('Permission', lazy='joined')
772 772
773 773
774 774 class UserGroupRepoToPerm(Base, BaseModel):
775 775 __tablename__ = 'users_group_repo_to_perm'
776 776 __table_args__ = (
777 777 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
778 778 {'extend_existing': True, 'mysql_engine': 'InnoDB',
779 779 'mysql_charset': 'utf8'}
780 780 )
781 781 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
782 782 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
783 783 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
784 784 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
785 785
786 786 users_group = relationship('UserGroup')
787 787 permission = relationship('Permission')
788 788 repository = relationship('Repository')
789 789
790 790 def __unicode__(self):
791 791 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
792 792
793 793
794 794 class UserGroupToPerm(Base, BaseModel):
795 795 __tablename__ = 'users_group_to_perm'
796 796 __table_args__ = (
797 797 UniqueConstraint('users_group_id', 'permission_id',),
798 798 {'extend_existing': True, 'mysql_engine': 'InnoDB',
799 799 'mysql_charset': 'utf8'}
800 800 )
801 801 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
802 802 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
803 803 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
804 804
805 805 users_group = relationship('UserGroup')
806 806 permission = relationship('Permission')
807 807
808 808
809 809 class UserRepoGroupToPerm(Base, BaseModel):
810 810 __tablename__ = 'user_repo_group_to_perm'
811 811 __table_args__ = (
812 812 UniqueConstraint('user_id', 'group_id', 'permission_id'),
813 813 {'extend_existing': True, 'mysql_engine': 'InnoDB',
814 814 'mysql_charset': 'utf8'}
815 815 )
816 816
817 817 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
818 818 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
819 819 group_id = Column("group_id", Integer(), ForeignKey('groups.group_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 group = relationship('RepoGroup')
824 824 permission = relationship('Permission')
825 825
826 826
827 827 class UserGroupRepoGroupToPerm(Base, BaseModel):
828 828 __tablename__ = 'users_group_repo_group_to_perm'
829 829 __table_args__ = (
830 830 UniqueConstraint('users_group_id', 'group_id'),
831 831 {'extend_existing': True, 'mysql_engine': 'InnoDB',
832 832 'mysql_charset': 'utf8'}
833 833 )
834 834
835 835 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)
836 836 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
837 837 group_id = Column("group_id", Integer(), ForeignKey('groups.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
840 840 users_group = relationship('UserGroup')
841 841 permission = relationship('Permission')
842 842 group = relationship('RepoGroup')
843 843
844 844
845 845 class Statistics(Base, BaseModel):
846 846 __tablename__ = 'statistics'
847 847 __table_args__ = (
848 848 UniqueConstraint('repository_id'),
849 849 {'extend_existing': True, 'mysql_engine': 'InnoDB',
850 850 'mysql_charset': 'utf8'}
851 851 )
852 852 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
853 853 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
854 854 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
855 855 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
856 856 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
857 857 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
858 858
859 859 repository = relationship('Repository', single_parent=True)
860 860
861 861
862 862 class UserFollowing(Base, BaseModel):
863 863 __tablename__ = 'user_followings'
864 864 __table_args__ = (
865 865 UniqueConstraint('user_id', 'follows_repository_id'),
866 866 UniqueConstraint('user_id', 'follows_user_id'),
867 867 {'extend_existing': True, 'mysql_engine': 'InnoDB',
868 868 'mysql_charset': 'utf8'}
869 869 )
870 870
871 871 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
872 872 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
873 873 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
874 874 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
875 875 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
876 876
877 877 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
878 878
879 879 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
880 880 follows_repository = relationship('Repository', order_by='Repository.repo_name')
881 881
882 882
883 883 class CacheInvalidation(Base, BaseModel):
884 884 __tablename__ = 'cache_invalidation'
885 885 __table_args__ = (
886 886 UniqueConstraint('cache_key'),
887 887 Index('key_idx', 'cache_key'),
888 888 {'extend_existing': True, 'mysql_engine': 'InnoDB',
889 889 'mysql_charset': 'utf8'},
890 890 )
891 891 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
892 892 cache_key = Column("cache_key", String(255), nullable=True, unique=None, default=None)
893 893 cache_args = Column("cache_args", String(255), nullable=True, unique=None, default=None)
894 894 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
895 895
896 896 def __init__(self, cache_key, cache_args=''):
897 897 self.cache_key = cache_key
898 898 self.cache_args = cache_args
899 899 self.cache_active = False
900 900
901 901
902 902 class ChangesetComment(Base, BaseModel):
903 903 __tablename__ = 'changeset_comments'
904 904 __table_args__ = (
905 905 Index('cc_revision_idx', 'revision'),
906 906 {'extend_existing': True, 'mysql_engine': 'InnoDB',
907 907 'mysql_charset': 'utf8'},
908 908 )
909 909 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
910 910 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
911 911 revision = Column('revision', String(40), nullable=True)
912 912 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
913 913 line_no = Column('line_no', Unicode(10), nullable=True)
914 914 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
915 915 f_path = Column('f_path', Unicode(1000), nullable=True)
916 916 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
917 917 text = Column('text', UnicodeText().with_variant(UnicodeText(25000), 'mysql'), nullable=False)
918 918 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
919 919 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
920 920
921 921 author = relationship('User', lazy='joined')
922 922 repo = relationship('Repository')
923 923 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
924 924 pull_request = relationship('PullRequest', lazy='joined')
925 925
926 926 @classmethod
927 927 def get_users(cls, revision=None, pull_request_id=None):
928 928 """
929 929 Returns user associated with this ChangesetComment. ie those
930 930 who actually commented
931 931
932 932 :param cls:
933 933 :param revision:
934 934 """
935 935 q = Session().query(User)\
936 936 .join(ChangesetComment.author)
937 937 if revision:
938 938 q = q.filter(cls.revision == revision)
939 939 elif pull_request_id:
940 940 q = q.filter(cls.pull_request_id == pull_request_id)
941 941 return q.all()
942 942
943 943
944 944 class ChangesetStatus(Base, BaseModel):
945 945 __tablename__ = 'changeset_statuses'
946 946 __table_args__ = (
947 947 Index('cs_revision_idx', 'revision'),
948 948 Index('cs_version_idx', 'version'),
949 949 UniqueConstraint('repo_id', 'revision', 'version'),
950 950 {'extend_existing': True, 'mysql_engine': 'InnoDB',
951 951 'mysql_charset': 'utf8'}
952 952 )
953 953 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
954 954 STATUS_APPROVED = 'approved'
955 955 STATUS_REJECTED = 'rejected'
956 956 STATUS_UNDER_REVIEW = 'under_review'
957 957
958 958 STATUSES = [
959 959 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
960 960 (STATUS_APPROVED, _("Approved")),
961 961 (STATUS_REJECTED, _("Rejected")),
962 962 (STATUS_UNDER_REVIEW, _("Under Review")),
963 963 ]
964 964
965 965 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
966 966 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
967 967 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
968 968 revision = Column('revision', String(40), nullable=False)
969 969 status = Column('status', String(128), nullable=False, default=DEFAULT)
970 970 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
971 971 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
972 972 version = Column('version', Integer(), nullable=False, default=0)
973 973 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
974 974
975 975 author = relationship('User', lazy='joined')
976 976 repo = relationship('Repository')
977 977 comment = relationship('ChangesetComment', lazy='joined')
978 978 pull_request = relationship('PullRequest', lazy='joined')
979 979
980 980
981 981
982 982 class PullRequest(Base, BaseModel):
983 983 __tablename__ = 'pull_requests'
984 984 __table_args__ = (
985 985 {'extend_existing': True, 'mysql_engine': 'InnoDB',
986 986 'mysql_charset': 'utf8'},
987 987 )
988 988
989 989 STATUS_NEW = u'new'
990 990 STATUS_OPEN = u'open'
991 991 STATUS_CLOSED = u'closed'
992 992
993 993 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
994 994 title = Column('title', Unicode(256), nullable=True)
995 995 description = Column('description', UnicodeText().with_variant(UnicodeText(10240), 'mysql'), nullable=True)
996 996 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
997 997 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
998 998 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
999 999 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1000 1000 _revisions = Column('revisions', UnicodeText().with_variant(UnicodeText(20500), 'mysql'))
1001 1001 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1002 1002 org_ref = Column('org_ref', Unicode(256), nullable=False)
1003 1003 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1004 1004 other_ref = Column('other_ref', Unicode(256), nullable=False)
1005 1005
1006 1006 author = relationship('User', lazy='joined')
1007 1007 reviewers = relationship('PullRequestReviewers',
1008 1008 cascade="all, delete, delete-orphan")
1009 1009 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1010 1010 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1011 1011 statuses = relationship('ChangesetStatus')
1012 1012 comments = relationship('ChangesetComment',
1013 1013 cascade="all, delete, delete-orphan")
1014 1014
1015 1015
1016 1016 class PullRequestReviewers(Base, BaseModel):
1017 1017 __tablename__ = 'pull_request_reviewers'
1018 1018 __table_args__ = (
1019 1019 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1020 1020 'mysql_charset': 'utf8'},
1021 1021 )
1022 1022
1023 1023 def __init__(self, user=None, pull_request=None):
1024 1024 self.user = user
1025 1025 self.pull_request = pull_request
1026 1026
1027 1027 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1028 1028 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1029 1029 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1030 1030
1031 1031 user = relationship('User')
1032 1032 pull_request = relationship('PullRequest')
1033 1033
1034 1034
1035 1035 class Notification(Base, BaseModel):
1036 1036 __tablename__ = 'notifications'
1037 1037 __table_args__ = (
1038 1038 Index('notification_type_idx', 'type'),
1039 1039 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1040 1040 'mysql_charset': 'utf8'},
1041 1041 )
1042 1042
1043 1043 TYPE_CHANGESET_COMMENT = u'cs_comment'
1044 1044 TYPE_MESSAGE = u'message'
1045 1045 TYPE_MENTION = u'mention'
1046 1046 TYPE_REGISTRATION = u'registration'
1047 1047 TYPE_PULL_REQUEST = u'pull_request'
1048 1048 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1049 1049
1050 1050 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1051 1051 subject = Column('subject', Unicode(512), nullable=True)
1052 1052 body = Column('body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True)
1053 1053 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1054 1054 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1055 1055 type_ = Column('type', Unicode(256))
1056 1056
1057 1057 created_by_user = relationship('User')
1058 1058 notifications_to_users = relationship('UserNotification', lazy='joined',
1059 1059 cascade="all, delete, delete-orphan")
1060 1060
1061 1061
1062 1062 class UserNotification(Base, BaseModel):
1063 1063 __tablename__ = 'user_to_notification'
1064 1064 __table_args__ = (
1065 1065 UniqueConstraint('user_id', 'notification_id'),
1066 1066 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1067 1067 'mysql_charset': 'utf8'}
1068 1068 )
1069 1069 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1070 1070 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1071 1071 read = Column('read', Boolean, default=False)
1072 1072 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1073 1073
1074 1074 user = relationship('User', lazy="joined")
1075 1075 notification = relationship('Notification', lazy="joined",
1076 1076 order_by=lambda: Notification.created_on.desc(),)
1077 1077
1078 1078
1079 1079 class DbMigrateVersion(Base, BaseModel):
1080 1080 __tablename__ = 'db_migrate_version'
1081 1081 __table_args__ = (
1082 1082 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1083 1083 'mysql_charset': 'utf8'},
1084 1084 )
1085 1085 repository_id = Column('repository_id', String(250), primary_key=True)
1086 1086 repository_path = Column('repository_path', Text)
1087 1087 version = Column('version', Integer)
@@ -1,1146 +1,1146 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import os
22 22 import time
23 23 import logging
24 24 import datetime
25 25 import traceback
26 26 import hashlib
27 27 import collections
28 28
29 29 from sqlalchemy import *
30 30 from sqlalchemy.ext.hybrid import hybrid_property
31 31 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
32 32 from sqlalchemy.exc import DatabaseError
33 33 from beaker.cache import cache_region, region_invalidate
34 34 from webob.exc import HTTPNotFound
35 35
36 from pylons.i18n.translation import lazy_ugettext as _
36 from rhodecode.translation import _
37 37
38 38 from rhodecode.lib.vcs import get_backend
39 39 from rhodecode.lib.vcs.utils.helpers import get_scm
40 40 from rhodecode.lib.vcs.exceptions import VCSError
41 41 from zope.cachedescriptors.property import Lazy as LazyProperty
42 42 from rhodecode.lib.vcs.backends.base import EmptyCommit
43 43
44 44 from rhodecode.lib.utils2 import str2bool, safe_str, get_commit_safe, \
45 45 safe_unicode, remove_suffix, remove_prefix, time_to_datetime
46 46 from rhodecode.lib.ext_json import json
47 47 from rhodecode.lib.caching_query import FromCache
48 48
49 49 from rhodecode.model.meta import Base, Session
50 50
51 51 URL_SEP = '/'
52 52 log = logging.getLogger(__name__)
53 53
54 54 #==============================================================================
55 55 # BASE CLASSES
56 56 #==============================================================================
57 57
58 58 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
59 59
60 60
61 61 class BaseModel(object):
62 62 """
63 63 Base Model for all classes
64 64 """
65 65
66 66 @classmethod
67 67 def _get_keys(cls):
68 68 """return column names for this model """
69 69 return class_mapper(cls).c.keys()
70 70
71 71 def get_dict(self):
72 72 """
73 73 return dict with keys and values corresponding
74 74 to this model data """
75 75
76 76 d = {}
77 77 for k in self._get_keys():
78 78 d[k] = getattr(self, k)
79 79
80 80 # also use __json__() if present to get additional fields
81 81 _json_attr = getattr(self, '__json__', None)
82 82 if _json_attr:
83 83 # update with attributes from __json__
84 84 if callable(_json_attr):
85 85 _json_attr = _json_attr()
86 86 for k, val in _json_attr.iteritems():
87 87 d[k] = val
88 88 return d
89 89
90 90 def get_appstruct(self):
91 91 """return list with keys and values tupples corresponding
92 92 to this model data """
93 93
94 94 l = []
95 95 for k in self._get_keys():
96 96 l.append((k, getattr(self, k),))
97 97 return l
98 98
99 99 def populate_obj(self, populate_dict):
100 100 """populate model with data from given populate_dict"""
101 101
102 102 for k in self._get_keys():
103 103 if k in populate_dict:
104 104 setattr(self, k, populate_dict[k])
105 105
106 106 @classmethod
107 107 def query(cls):
108 108 return Session().query(cls)
109 109
110 110 @classmethod
111 111 def get(cls, id_):
112 112 if id_:
113 113 return cls.query().get(id_)
114 114
115 115 @classmethod
116 116 def get_or_404(cls, id_):
117 117 try:
118 118 id_ = int(id_)
119 119 except (TypeError, ValueError):
120 120 raise HTTPNotFound
121 121
122 122 res = cls.query().get(id_)
123 123 if not res:
124 124 raise HTTPNotFound
125 125 return res
126 126
127 127 @classmethod
128 128 def getAll(cls):
129 129 # deprecated and left for backward compatibility
130 130 return cls.get_all()
131 131
132 132 @classmethod
133 133 def get_all(cls):
134 134 return cls.query().all()
135 135
136 136 @classmethod
137 137 def delete(cls, id_):
138 138 obj = cls.query().get(id_)
139 139 Session().delete(obj)
140 140
141 141 def __repr__(self):
142 142 if hasattr(self, '__unicode__'):
143 143 # python repr needs to return str
144 144 return safe_str(self.__unicode__())
145 145 return '<DB:%s>' % (self.__class__.__name__)
146 146
147 147
148 148 class RhodeCodeSetting(Base, BaseModel):
149 149 __tablename__ = 'rhodecode_settings'
150 150 __table_args__ = (
151 151 UniqueConstraint('app_settings_name'),
152 152 {'extend_existing': True, 'mysql_engine': 'InnoDB',
153 153 'mysql_charset': 'utf8'}
154 154 )
155 155 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
156 156 app_settings_name = Column("app_settings_name", String(255), nullable=True, unique=None, default=None)
157 157 _app_settings_value = Column("app_settings_value", String(255), nullable=True, unique=None, default=None)
158 158
159 159 def __init__(self, k='', v=''):
160 160 self.app_settings_name = k
161 161 self.app_settings_value = v
162 162
163 163 @validates('_app_settings_value')
164 164 def validate_settings_value(self, key, val):
165 165 assert type(val) == unicode
166 166 return val
167 167
168 168 @hybrid_property
169 169 def app_settings_value(self):
170 170 v = self._app_settings_value
171 171 if self.app_settings_name in ["ldap_active",
172 172 "default_repo_enable_statistics",
173 173 "default_repo_enable_locking",
174 174 "default_repo_private",
175 175 "default_repo_enable_downloads"]:
176 176 v = str2bool(v)
177 177 return v
178 178
179 179 @app_settings_value.setter
180 180 def app_settings_value(self, val):
181 181 """
182 182 Setter that will always make sure we use unicode in app_settings_value
183 183
184 184 :param val:
185 185 """
186 186 self._app_settings_value = safe_unicode(val)
187 187
188 188 def __unicode__(self):
189 189 return u"<%s('%s:%s')>" % (
190 190 self.__class__.__name__,
191 191 self.app_settings_name, self.app_settings_value
192 192 )
193 193
194 194
195 195 class RhodeCodeUi(Base, BaseModel):
196 196 __tablename__ = 'rhodecode_ui'
197 197 __table_args__ = (
198 198 UniqueConstraint('ui_key'),
199 199 {'extend_existing': True, 'mysql_engine': 'InnoDB',
200 200 'mysql_charset': 'utf8'}
201 201 )
202 202
203 203 HOOK_REPO_SIZE = 'changegroup.repo_size'
204 204 HOOK_PUSH = 'changegroup.push_logger'
205 205 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
206 206 HOOK_PULL = 'outgoing.pull_logger'
207 207 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
208 208
209 209 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
210 210 ui_section = Column("ui_section", String(255), nullable=True, unique=None, default=None)
211 211 ui_key = Column("ui_key", String(255), nullable=True, unique=None, default=None)
212 212 ui_value = Column("ui_value", String(255), nullable=True, unique=None, default=None)
213 213 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
214 214
215 215
216 216
217 217 class User(Base, BaseModel):
218 218 __tablename__ = 'users'
219 219 __table_args__ = (
220 220 UniqueConstraint('username'), UniqueConstraint('email'),
221 221 Index('u_username_idx', 'username'),
222 222 Index('u_email_idx', 'email'),
223 223 {'extend_existing': True, 'mysql_engine': 'InnoDB',
224 224 'mysql_charset': 'utf8'}
225 225 )
226 226 DEFAULT_USER = 'default'
227 227
228 228 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
229 229 username = Column("username", String(255), nullable=True, unique=None, default=None)
230 230 password = Column("password", String(255), nullable=True, unique=None, default=None)
231 231 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
232 232 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
233 233 name = Column("firstname", String(255), nullable=True, unique=None, default=None)
234 234 lastname = Column("lastname", String(255), nullable=True, unique=None, default=None)
235 235 _email = Column("email", String(255), nullable=True, unique=None, default=None)
236 236 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
237 237 ldap_dn = Column("ldap_dn", String(255), nullable=True, unique=None, default=None)
238 238 api_key = Column("api_key", String(255), nullable=True, unique=None, default=None)
239 239 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
240 240
241 241 user_log = relationship('UserLog')
242 242 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
243 243
244 244 repositories = relationship('Repository')
245 245 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
246 246 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
247 247
248 248 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
249 249 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
250 250
251 251 group_member = relationship('UserGroupMember', cascade='all')
252 252
253 253 notifications = relationship('UserNotification', cascade='all')
254 254 # notifications assigned to this user
255 255 user_created_notifications = relationship('Notification', cascade='all')
256 256 # comments created by this user
257 257 user_comments = relationship('ChangesetComment', cascade='all')
258 258 user_emails = relationship('UserEmailMap', cascade='all')
259 259
260 260 @hybrid_property
261 261 def email(self):
262 262 return self._email
263 263
264 264 @email.setter
265 265 def email(self, val):
266 266 self._email = val.lower() if val else None
267 267
268 268 @property
269 269 def firstname(self):
270 270 # alias for future
271 271 return self.name
272 272
273 273 @property
274 274 def username_and_name(self):
275 275 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
276 276
277 277 @property
278 278 def full_name(self):
279 279 return '%s %s' % (self.firstname, self.lastname)
280 280
281 281 @property
282 282 def full_contact(self):
283 283 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
284 284
285 285 @property
286 286 def short_contact(self):
287 287 return '%s %s' % (self.firstname, self.lastname)
288 288
289 289 @property
290 290 def is_admin(self):
291 291 return self.admin
292 292
293 293 @classmethod
294 294 def get_by_username(cls, username, case_insensitive=False, cache=False):
295 295 if case_insensitive:
296 296 q = cls.query().filter(cls.username.ilike(username))
297 297 else:
298 298 q = cls.query().filter(cls.username == username)
299 299
300 300 if cache:
301 301 q = q.options(FromCache(
302 302 "sql_cache_short",
303 303 "get_user_%s" % _hash_key(username)
304 304 )
305 305 )
306 306 return q.scalar()
307 307
308 308 @classmethod
309 309 def get_by_auth_token(cls, auth_token, cache=False):
310 310 q = cls.query().filter(cls.api_key == auth_token)
311 311
312 312 if cache:
313 313 q = q.options(FromCache("sql_cache_short",
314 314 "get_auth_token_%s" % auth_token))
315 315 return q.scalar()
316 316
317 317 @classmethod
318 318 def get_by_email(cls, email, case_insensitive=False, cache=False):
319 319 if case_insensitive:
320 320 q = cls.query().filter(cls.email.ilike(email))
321 321 else:
322 322 q = cls.query().filter(cls.email == email)
323 323
324 324 if cache:
325 325 q = q.options(FromCache("sql_cache_short",
326 326 "get_email_key_%s" % email))
327 327
328 328 ret = q.scalar()
329 329 if ret is None:
330 330 q = UserEmailMap.query()
331 331 # try fetching in alternate email map
332 332 if case_insensitive:
333 333 q = q.filter(UserEmailMap.email.ilike(email))
334 334 else:
335 335 q = q.filter(UserEmailMap.email == email)
336 336 q = q.options(joinedload(UserEmailMap.user))
337 337 if cache:
338 338 q = q.options(FromCache("sql_cache_short",
339 339 "get_email_map_key_%s" % email))
340 340 ret = getattr(q.scalar(), 'user', None)
341 341
342 342 return ret
343 343
344 344 @classmethod
345 345 def get_first_admin(cls):
346 346 user = User.query().filter(User.admin == True).first()
347 347 if user is None:
348 348 raise Exception('Missing administrative account!')
349 349 return user
350 350
351 351 @classmethod
352 352 def get_default_user(cls, cache=False):
353 353 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
354 354 if user is None:
355 355 raise Exception('Missing default account!')
356 356 return user
357 357
358 358
359 359
360 360
361 361 class UserEmailMap(Base, BaseModel):
362 362 __tablename__ = 'user_email_map'
363 363 __table_args__ = (
364 364 Index('uem_email_idx', 'email'),
365 365 UniqueConstraint('email'),
366 366 {'extend_existing': True, 'mysql_engine': 'InnoDB',
367 367 'mysql_charset': 'utf8'}
368 368 )
369 369 __mapper_args__ = {}
370 370
371 371 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
372 372 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
373 373 _email = Column("email", String(255), nullable=True, unique=False, default=None)
374 374 user = relationship('User', lazy='joined')
375 375
376 376 @validates('_email')
377 377 def validate_email(self, key, email):
378 378 # check if this email is not main one
379 379 main_email = Session().query(User).filter(User.email == email).scalar()
380 380 if main_email is not None:
381 381 raise AttributeError('email %s is present is user table' % email)
382 382 return email
383 383
384 384 @hybrid_property
385 385 def email(self):
386 386 return self._email
387 387
388 388 @email.setter
389 389 def email(self, val):
390 390 self._email = val.lower() if val else None
391 391
392 392
393 393 class UserIpMap(Base, BaseModel):
394 394 __tablename__ = 'user_ip_map'
395 395 __table_args__ = (
396 396 UniqueConstraint('user_id', 'ip_addr'),
397 397 {'extend_existing': True, 'mysql_engine': 'InnoDB',
398 398 'mysql_charset': 'utf8'}
399 399 )
400 400 __mapper_args__ = {}
401 401
402 402 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
403 403 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
404 404 ip_addr = Column("ip_addr", String(255), nullable=True, unique=False, default=None)
405 405 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
406 406 user = relationship('User', lazy='joined')
407 407
408 408
409 409 class UserLog(Base, BaseModel):
410 410 __tablename__ = 'user_logs'
411 411 __table_args__ = (
412 412 {'extend_existing': True, 'mysql_engine': 'InnoDB',
413 413 'mysql_charset': 'utf8'},
414 414 )
415 415 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
416 416 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
417 417 username = Column("username", String(255), nullable=True, unique=None, default=None)
418 418 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
419 419 repository_name = Column("repository_name", String(255), nullable=True, unique=None, default=None)
420 420 user_ip = Column("user_ip", String(255), nullable=True, unique=None, default=None)
421 421 action = Column("action", String(1200000), nullable=True, unique=None, default=None)
422 422 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
423 423
424 424 def __unicode__(self):
425 425 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
426 426 self.repository_name,
427 427 self.action)
428 428
429 429 user = relationship('User')
430 430 repository = relationship('Repository', cascade='')
431 431
432 432
433 433 class UserGroup(Base, BaseModel):
434 434 __tablename__ = 'users_groups'
435 435 __table_args__ = (
436 436 {'extend_existing': True, 'mysql_engine': 'InnoDB',
437 437 'mysql_charset': 'utf8'},
438 438 )
439 439
440 440 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
441 441 users_group_name = Column("users_group_name", String(255), nullable=False, unique=True, default=None)
442 442 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
443 443 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
444 444 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
445 445
446 446 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
447 447 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
448 448 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
449 449 users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
450 450 user_user_group_to_perm = relationship('UserUserGroupToPerm', cascade='all')
451 451 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm ', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
452 452
453 453 user = relationship('User')
454 454
455 455 def __unicode__(self):
456 456 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
457 457 self.users_group_id,
458 458 self.users_group_name)
459 459
460 460 @classmethod
461 461 def get_by_group_name(cls, group_name, cache=False,
462 462 case_insensitive=False):
463 463 if case_insensitive:
464 464 q = cls.query().filter(cls.users_group_name.ilike(group_name))
465 465 else:
466 466 q = cls.query().filter(cls.users_group_name == group_name)
467 467 if cache:
468 468 q = q.options(FromCache(
469 469 "sql_cache_short",
470 470 "get_user_%s" % _hash_key(group_name)
471 471 )
472 472 )
473 473 return q.scalar()
474 474
475 475 @classmethod
476 476 def get(cls, users_group_id, cache=False):
477 477 user_group = cls.query()
478 478 if cache:
479 479 user_group = user_group.options(FromCache("sql_cache_short",
480 480 "get_users_group_%s" % users_group_id))
481 481 return user_group.get(users_group_id)
482 482
483 483
484 484 class UserGroupMember(Base, BaseModel):
485 485 __tablename__ = 'users_groups_members'
486 486 __table_args__ = (
487 487 {'extend_existing': True, 'mysql_engine': 'InnoDB',
488 488 'mysql_charset': 'utf8'},
489 489 )
490 490
491 491 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
492 492 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
493 493 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
494 494
495 495 user = relationship('User', lazy='joined')
496 496 users_group = relationship('UserGroup')
497 497
498 498 def __init__(self, gr_id='', u_id=''):
499 499 self.users_group_id = gr_id
500 500 self.user_id = u_id
501 501
502 502
503 503 class RepositoryField(Base, BaseModel):
504 504 __tablename__ = 'repositories_fields'
505 505 __table_args__ = (
506 506 UniqueConstraint('repository_id', 'field_key'), # no-multi field
507 507 {'extend_existing': True, 'mysql_engine': 'InnoDB',
508 508 'mysql_charset': 'utf8'},
509 509 )
510 510 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
511 511
512 512 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
513 513 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
514 514 field_key = Column("field_key", String(250))
515 515 field_label = Column("field_label", String(1024), nullable=False)
516 516 field_value = Column("field_value", String(10000), nullable=False)
517 517 field_desc = Column("field_desc", String(1024), nullable=False)
518 518 field_type = Column("field_type", String(256), nullable=False, unique=None)
519 519 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
520 520
521 521 repository = relationship('Repository')
522 522
523 523 @classmethod
524 524 def get_by_key_name(cls, key, repo):
525 525 row = cls.query()\
526 526 .filter(cls.repository == repo)\
527 527 .filter(cls.field_key == key).scalar()
528 528 return row
529 529
530 530
531 531 class Repository(Base, BaseModel):
532 532 __tablename__ = 'repositories'
533 533 __table_args__ = (
534 534 UniqueConstraint('repo_name'),
535 535 Index('r_repo_name_idx', 'repo_name'),
536 536 {'extend_existing': True, 'mysql_engine': 'InnoDB',
537 537 'mysql_charset': 'utf8'},
538 538 )
539 539
540 540 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
541 541 repo_name = Column("repo_name", String(255), nullable=False, unique=True, default=None)
542 542 clone_uri = Column("clone_uri", String(255), nullable=True, unique=False, default=None)
543 543 repo_type = Column("repo_type", String(255), nullable=False, unique=False, default=None)
544 544 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
545 545 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
546 546 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
547 547 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
548 548 description = Column("description", String(10000), nullable=True, unique=None, default=None)
549 549 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
550 550 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
551 551 landing_rev = Column("landing_revision", String(255), nullable=False, unique=False, default=None)
552 552 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
553 553 _locked = Column("locked", String(255), nullable=True, unique=False, default=None)
554 554 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
555 555
556 556 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
557 557 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
558 558
559 559 user = relationship('User')
560 560 fork = relationship('Repository', remote_side=repo_id)
561 561 group = relationship('RepoGroup')
562 562 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
563 563 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
564 564 stats = relationship('Statistics', cascade='all', uselist=False)
565 565
566 566 followers = relationship('UserFollowing',
567 567 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
568 568 cascade='all')
569 569 extra_fields = relationship('RepositoryField',
570 570 cascade="all, delete, delete-orphan")
571 571
572 572 logs = relationship('UserLog')
573 573 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
574 574
575 575 pull_requests_org = relationship('PullRequest',
576 576 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
577 577 cascade="all, delete, delete-orphan")
578 578
579 579 pull_requests_other = relationship('PullRequest',
580 580 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
581 581 cascade="all, delete, delete-orphan")
582 582
583 583 def __unicode__(self):
584 584 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
585 585 safe_unicode(self.repo_name))
586 586
587 587 @classmethod
588 588 def get_by_repo_name(cls, repo_name):
589 589 q = Session().query(cls).filter(cls.repo_name == repo_name)
590 590 q = q.options(joinedload(Repository.fork))\
591 591 .options(joinedload(Repository.user))\
592 592 .options(joinedload(Repository.group))
593 593 return q.scalar()
594 594
595 595
596 596 class RepoGroup(Base, BaseModel):
597 597 __tablename__ = 'groups'
598 598 __table_args__ = (
599 599 UniqueConstraint('group_name', 'group_parent_id'),
600 600 CheckConstraint('group_id != group_parent_id'),
601 601 {'extend_existing': True, 'mysql_engine': 'InnoDB',
602 602 'mysql_charset': 'utf8'},
603 603 )
604 604 __mapper_args__ = {'order_by': 'group_name'}
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'},
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'}
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'}
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'}
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'}
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'}
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'}
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'}
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'}
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'}
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'}
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'},
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'},
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'}
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'},
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'},
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'},
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'}
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'}
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(), 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'},
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,1148 +1,1148 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import os
22 22 import time
23 23 import logging
24 24 import datetime
25 25 import traceback
26 26 import hashlib
27 27 import collections
28 28
29 29 from sqlalchemy import *
30 30 from sqlalchemy.ext.hybrid import hybrid_property
31 31 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
32 32 from sqlalchemy.exc import DatabaseError
33 33 from beaker.cache import cache_region, region_invalidate
34 34 from webob.exc import HTTPNotFound
35 35
36 from pylons.i18n.translation import lazy_ugettext as _
36 from rhodecode.translation import _
37 37
38 38 from rhodecode.lib.vcs import get_backend
39 39 from rhodecode.lib.vcs.utils.helpers import get_scm
40 40 from rhodecode.lib.vcs.exceptions import VCSError
41 41 from zope.cachedescriptors.property import Lazy as LazyProperty
42 42 from rhodecode.lib.vcs.backends.base import EmptyCommit
43 43
44 44 from rhodecode.lib.utils2 import str2bool, safe_str, get_commit_safe, \
45 45 safe_unicode, remove_suffix, remove_prefix, time_to_datetime
46 46 from rhodecode.lib.ext_json import json
47 47 from rhodecode.lib.caching_query import FromCache
48 48
49 49 from rhodecode.model.meta import Base, Session
50 50
51 51 URL_SEP = '/'
52 52 log = logging.getLogger(__name__)
53 53
54 54 #==============================================================================
55 55 # BASE CLASSES
56 56 #==============================================================================
57 57
58 58 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
59 59
60 60
61 61 class BaseModel(object):
62 62 """
63 63 Base Model for all classes
64 64 """
65 65
66 66 @classmethod
67 67 def _get_keys(cls):
68 68 """return column names for this model """
69 69 return class_mapper(cls).c.keys()
70 70
71 71 def get_dict(self):
72 72 """
73 73 return dict with keys and values corresponding
74 74 to this model data """
75 75
76 76 d = {}
77 77 for k in self._get_keys():
78 78 d[k] = getattr(self, k)
79 79
80 80 # also use __json__() if present to get additional fields
81 81 _json_attr = getattr(self, '__json__', None)
82 82 if _json_attr:
83 83 # update with attributes from __json__
84 84 if callable(_json_attr):
85 85 _json_attr = _json_attr()
86 86 for k, val in _json_attr.iteritems():
87 87 d[k] = val
88 88 return d
89 89
90 90 def get_appstruct(self):
91 91 """return list with keys and values tupples corresponding
92 92 to this model data """
93 93
94 94 l = []
95 95 for k in self._get_keys():
96 96 l.append((k, getattr(self, k),))
97 97 return l
98 98
99 99 def populate_obj(self, populate_dict):
100 100 """populate model with data from given populate_dict"""
101 101
102 102 for k in self._get_keys():
103 103 if k in populate_dict:
104 104 setattr(self, k, populate_dict[k])
105 105
106 106 @classmethod
107 107 def query(cls):
108 108 return Session().query(cls)
109 109
110 110 @classmethod
111 111 def get(cls, id_):
112 112 if id_:
113 113 return cls.query().get(id_)
114 114
115 115 @classmethod
116 116 def get_or_404(cls, id_):
117 117 try:
118 118 id_ = int(id_)
119 119 except (TypeError, ValueError):
120 120 raise HTTPNotFound
121 121
122 122 res = cls.query().get(id_)
123 123 if not res:
124 124 raise HTTPNotFound
125 125 return res
126 126
127 127 @classmethod
128 128 def getAll(cls):
129 129 # deprecated and left for backward compatibility
130 130 return cls.get_all()
131 131
132 132 @classmethod
133 133 def get_all(cls):
134 134 return cls.query().all()
135 135
136 136 @classmethod
137 137 def delete(cls, id_):
138 138 obj = cls.query().get(id_)
139 139 Session().delete(obj)
140 140
141 141 def __repr__(self):
142 142 if hasattr(self, '__unicode__'):
143 143 # python repr needs to return str
144 144 return safe_str(self.__unicode__())
145 145 return '<DB:%s>' % (self.__class__.__name__)
146 146
147 147
148 148 class RhodeCodeSetting(Base, BaseModel):
149 149 __tablename__ = 'rhodecode_settings'
150 150 __table_args__ = (
151 151 UniqueConstraint('app_settings_name'),
152 152 {'extend_existing': True, 'mysql_engine': 'InnoDB',
153 153 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
154 154 )
155 155 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
156 156 app_settings_name = Column("app_settings_name", String(255), nullable=True, unique=None, default=None)
157 157 _app_settings_value = Column("app_settings_value", String(255), nullable=True, unique=None, default=None)
158 158 _app_settings_type = Column("app_settings_type", String(255), nullable=True, unique=None, default=None)
159 159
160 160 def __init__(self, key='', val='', type='unicode'):
161 161 self.app_settings_name = key
162 162 self.app_settings_value = val
163 163 self.app_settings_type = type
164 164
165 165 @validates('_app_settings_value')
166 166 def validate_settings_value(self, key, val):
167 167 assert type(val) == unicode
168 168 return val
169 169
170 170 @hybrid_property
171 171 def app_settings_value(self):
172 172 v = self._app_settings_value
173 173 if self.app_settings_name in ["ldap_active",
174 174 "default_repo_enable_statistics",
175 175 "default_repo_enable_locking",
176 176 "default_repo_private",
177 177 "default_repo_enable_downloads"]:
178 178 v = str2bool(v)
179 179 return v
180 180
181 181 @app_settings_value.setter
182 182 def app_settings_value(self, val):
183 183 """
184 184 Setter that will always make sure we use unicode in app_settings_value
185 185
186 186 :param val:
187 187 """
188 188 self._app_settings_value = safe_unicode(val)
189 189
190 190 def __unicode__(self):
191 191 return u"<%s('%s:%s')>" % (
192 192 self.__class__.__name__,
193 193 self.app_settings_name, self.app_settings_value
194 194 )
195 195
196 196
197 197 class RhodeCodeUi(Base, BaseModel):
198 198 __tablename__ = 'rhodecode_ui'
199 199 __table_args__ = (
200 200 UniqueConstraint('ui_key'),
201 201 {'extend_existing': True, 'mysql_engine': 'InnoDB',
202 202 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
203 203 )
204 204
205 205 HOOK_REPO_SIZE = 'changegroup.repo_size'
206 206 HOOK_PUSH = 'changegroup.push_logger'
207 207 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
208 208 HOOK_PULL = 'outgoing.pull_logger'
209 209 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
210 210
211 211 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
212 212 ui_section = Column("ui_section", String(255), nullable=True, unique=None, default=None)
213 213 ui_key = Column("ui_key", String(255), nullable=True, unique=None, default=None)
214 214 ui_value = Column("ui_value", String(255), nullable=True, unique=None, default=None)
215 215 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
216 216
217 217
218 218
219 219 class User(Base, BaseModel):
220 220 __tablename__ = 'users'
221 221 __table_args__ = (
222 222 UniqueConstraint('username'), UniqueConstraint('email'),
223 223 Index('u_username_idx', 'username'),
224 224 Index('u_email_idx', 'email'),
225 225 {'extend_existing': True, 'mysql_engine': 'InnoDB',
226 226 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
227 227 )
228 228 DEFAULT_USER = 'default'
229 229
230 230 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
231 231 username = Column("username", String(255), nullable=True, unique=None, default=None)
232 232 password = Column("password", String(255), nullable=True, unique=None, default=None)
233 233 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
234 234 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
235 235 name = Column("firstname", String(255), nullable=True, unique=None, default=None)
236 236 lastname = Column("lastname", String(255), nullable=True, unique=None, default=None)
237 237 _email = Column("email", String(255), nullable=True, unique=None, default=None)
238 238 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
239 239 ldap_dn = Column("ldap_dn", String(255), nullable=True, unique=None, default=None)
240 240 api_key = Column("api_key", String(255), nullable=True, unique=None, default=None)
241 241 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
242 242
243 243 user_log = relationship('UserLog')
244 244 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
245 245
246 246 repositories = relationship('Repository')
247 247 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
248 248 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
249 249
250 250 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
251 251 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
252 252
253 253 group_member = relationship('UserGroupMember', cascade='all')
254 254
255 255 notifications = relationship('UserNotification', cascade='all')
256 256 # notifications assigned to this user
257 257 user_created_notifications = relationship('Notification', cascade='all')
258 258 # comments created by this user
259 259 user_comments = relationship('ChangesetComment', cascade='all')
260 260 user_emails = relationship('UserEmailMap', cascade='all')
261 261
262 262 @hybrid_property
263 263 def email(self):
264 264 return self._email
265 265
266 266 @email.setter
267 267 def email(self, val):
268 268 self._email = val.lower() if val else None
269 269
270 270 @property
271 271 def firstname(self):
272 272 # alias for future
273 273 return self.name
274 274
275 275 @property
276 276 def username_and_name(self):
277 277 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
278 278
279 279 @property
280 280 def full_name(self):
281 281 return '%s %s' % (self.firstname, self.lastname)
282 282
283 283 @property
284 284 def full_contact(self):
285 285 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
286 286
287 287 @property
288 288 def short_contact(self):
289 289 return '%s %s' % (self.firstname, self.lastname)
290 290
291 291 @property
292 292 def is_admin(self):
293 293 return self.admin
294 294
295 295 @classmethod
296 296 def get_by_username(cls, username, case_insensitive=False, cache=False):
297 297 if case_insensitive:
298 298 q = cls.query().filter(cls.username.ilike(username))
299 299 else:
300 300 q = cls.query().filter(cls.username == username)
301 301
302 302 if cache:
303 303 q = q.options(FromCache(
304 304 "sql_cache_short",
305 305 "get_user_%s" % _hash_key(username)
306 306 )
307 307 )
308 308 return q.scalar()
309 309
310 310 @classmethod
311 311 def get_by_auth_token(cls, auth_token, cache=False):
312 312 q = cls.query().filter(cls.api_key == auth_token)
313 313
314 314 if cache:
315 315 q = q.options(FromCache("sql_cache_short",
316 316 "get_auth_token_%s" % auth_token))
317 317 return q.scalar()
318 318
319 319 @classmethod
320 320 def get_by_email(cls, email, case_insensitive=False, cache=False):
321 321 if case_insensitive:
322 322 q = cls.query().filter(cls.email.ilike(email))
323 323 else:
324 324 q = cls.query().filter(cls.email == email)
325 325
326 326 if cache:
327 327 q = q.options(FromCache("sql_cache_short",
328 328 "get_email_key_%s" % email))
329 329
330 330 ret = q.scalar()
331 331 if ret is None:
332 332 q = UserEmailMap.query()
333 333 # try fetching in alternate email map
334 334 if case_insensitive:
335 335 q = q.filter(UserEmailMap.email.ilike(email))
336 336 else:
337 337 q = q.filter(UserEmailMap.email == email)
338 338 q = q.options(joinedload(UserEmailMap.user))
339 339 if cache:
340 340 q = q.options(FromCache("sql_cache_short",
341 341 "get_email_map_key_%s" % email))
342 342 ret = getattr(q.scalar(), 'user', None)
343 343
344 344 return ret
345 345
346 346 @classmethod
347 347 def get_first_admin(cls):
348 348 user = User.query().filter(User.admin == True).first()
349 349 if user is None:
350 350 raise Exception('Missing administrative account!')
351 351 return user
352 352
353 353 @classmethod
354 354 def get_default_user(cls, cache=False):
355 355 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
356 356 if user is None:
357 357 raise Exception('Missing default account!')
358 358 return user
359 359
360 360
361 361
362 362
363 363 class UserEmailMap(Base, BaseModel):
364 364 __tablename__ = 'user_email_map'
365 365 __table_args__ = (
366 366 Index('uem_email_idx', 'email'),
367 367 UniqueConstraint('email'),
368 368 {'extend_existing': True, 'mysql_engine': 'InnoDB',
369 369 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
370 370 )
371 371 __mapper_args__ = {}
372 372
373 373 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
374 374 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
375 375 _email = Column("email", String(255), nullable=True, unique=False, default=None)
376 376 user = relationship('User', lazy='joined')
377 377
378 378 @validates('_email')
379 379 def validate_email(self, key, email):
380 380 # check if this email is not main one
381 381 main_email = Session().query(User).filter(User.email == email).scalar()
382 382 if main_email is not None:
383 383 raise AttributeError('email %s is present is user table' % email)
384 384 return email
385 385
386 386 @hybrid_property
387 387 def email(self):
388 388 return self._email
389 389
390 390 @email.setter
391 391 def email(self, val):
392 392 self._email = val.lower() if val else None
393 393
394 394
395 395 class UserIpMap(Base, BaseModel):
396 396 __tablename__ = 'user_ip_map'
397 397 __table_args__ = (
398 398 UniqueConstraint('user_id', 'ip_addr'),
399 399 {'extend_existing': True, 'mysql_engine': 'InnoDB',
400 400 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
401 401 )
402 402 __mapper_args__ = {}
403 403
404 404 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
405 405 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
406 406 ip_addr = Column("ip_addr", String(255), nullable=True, unique=False, default=None)
407 407 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
408 408 user = relationship('User', lazy='joined')
409 409
410 410
411 411 class UserLog(Base, BaseModel):
412 412 __tablename__ = 'user_logs'
413 413 __table_args__ = (
414 414 {'extend_existing': True, 'mysql_engine': 'InnoDB',
415 415 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
416 416 )
417 417 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
418 418 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
419 419 username = Column("username", String(255), nullable=True, unique=None, default=None)
420 420 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
421 421 repository_name = Column("repository_name", String(255), nullable=True, unique=None, default=None)
422 422 user_ip = Column("user_ip", String(255), nullable=True, unique=None, default=None)
423 423 action = Column("action", String(1200000), nullable=True, unique=None, default=None)
424 424 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
425 425
426 426 def __unicode__(self):
427 427 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
428 428 self.repository_name,
429 429 self.action)
430 430
431 431 user = relationship('User')
432 432 repository = relationship('Repository', cascade='')
433 433
434 434
435 435 class UserGroup(Base, BaseModel):
436 436 __tablename__ = 'users_groups'
437 437 __table_args__ = (
438 438 {'extend_existing': True, 'mysql_engine': 'InnoDB',
439 439 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
440 440 )
441 441
442 442 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
443 443 users_group_name = Column("users_group_name", String(255), nullable=False, unique=True, default=None)
444 444 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
445 445 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
446 446 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
447 447
448 448 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
449 449 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
450 450 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
451 451 users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
452 452 user_user_group_to_perm = relationship('UserUserGroupToPerm', cascade='all')
453 453 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm ', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
454 454
455 455 user = relationship('User')
456 456
457 457 def __unicode__(self):
458 458 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
459 459 self.users_group_id,
460 460 self.users_group_name)
461 461
462 462 @classmethod
463 463 def get_by_group_name(cls, group_name, cache=False,
464 464 case_insensitive=False):
465 465 if case_insensitive:
466 466 q = cls.query().filter(cls.users_group_name.ilike(group_name))
467 467 else:
468 468 q = cls.query().filter(cls.users_group_name == group_name)
469 469 if cache:
470 470 q = q.options(FromCache(
471 471 "sql_cache_short",
472 472 "get_user_%s" % _hash_key(group_name)
473 473 )
474 474 )
475 475 return q.scalar()
476 476
477 477 @classmethod
478 478 def get(cls, user_group_id, cache=False):
479 479 user_group = cls.query()
480 480 if cache:
481 481 user_group = user_group.options(FromCache("sql_cache_short",
482 482 "get_users_group_%s" % user_group_id))
483 483 return user_group.get(user_group_id)
484 484
485 485
486 486 class UserGroupMember(Base, BaseModel):
487 487 __tablename__ = 'users_groups_members'
488 488 __table_args__ = (
489 489 {'extend_existing': True, 'mysql_engine': 'InnoDB',
490 490 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
491 491 )
492 492
493 493 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
494 494 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
495 495 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
496 496
497 497 user = relationship('User', lazy='joined')
498 498 users_group = relationship('UserGroup')
499 499
500 500 def __init__(self, gr_id='', u_id=''):
501 501 self.users_group_id = gr_id
502 502 self.user_id = u_id
503 503
504 504
505 505 class RepositoryField(Base, BaseModel):
506 506 __tablename__ = 'repositories_fields'
507 507 __table_args__ = (
508 508 UniqueConstraint('repository_id', 'field_key'), # no-multi field
509 509 {'extend_existing': True, 'mysql_engine': 'InnoDB',
510 510 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
511 511 )
512 512 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
513 513
514 514 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
515 515 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
516 516 field_key = Column("field_key", String(250))
517 517 field_label = Column("field_label", String(1024), nullable=False)
518 518 field_value = Column("field_value", String(10000), nullable=False)
519 519 field_desc = Column("field_desc", String(1024), nullable=False)
520 520 field_type = Column("field_type", String(256), nullable=False, unique=None)
521 521 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
522 522
523 523 repository = relationship('Repository')
524 524
525 525 @classmethod
526 526 def get_by_key_name(cls, key, repo):
527 527 row = cls.query()\
528 528 .filter(cls.repository == repo)\
529 529 .filter(cls.field_key == key).scalar()
530 530 return row
531 531
532 532
533 533 class Repository(Base, BaseModel):
534 534 __tablename__ = 'repositories'
535 535 __table_args__ = (
536 536 UniqueConstraint('repo_name'),
537 537 Index('r_repo_name_idx', 'repo_name'),
538 538 {'extend_existing': True, 'mysql_engine': 'InnoDB',
539 539 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
540 540 )
541 541
542 542 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
543 543 repo_name = Column("repo_name", String(255), nullable=False, unique=True, default=None)
544 544 clone_uri = Column("clone_uri", String(255), nullable=True, unique=False, default=None)
545 545 repo_type = Column("repo_type", String(255), nullable=False, unique=False, default=None)
546 546 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
547 547 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
548 548 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
549 549 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
550 550 description = Column("description", String(10000), nullable=True, unique=None, default=None)
551 551 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
552 552 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
553 553 landing_rev = Column("landing_revision", String(255), nullable=False, unique=False, default=None)
554 554 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
555 555 _locked = Column("locked", String(255), nullable=True, unique=False, default=None)
556 556 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
557 557
558 558 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
559 559 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
560 560
561 561 user = relationship('User')
562 562 fork = relationship('Repository', remote_side=repo_id)
563 563 group = relationship('RepoGroup')
564 564 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
565 565 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
566 566 stats = relationship('Statistics', cascade='all', uselist=False)
567 567
568 568 followers = relationship('UserFollowing',
569 569 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
570 570 cascade='all')
571 571 extra_fields = relationship('RepositoryField',
572 572 cascade="all, delete, delete-orphan")
573 573
574 574 logs = relationship('UserLog')
575 575 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
576 576
577 577 pull_requests_org = relationship('PullRequest',
578 578 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
579 579 cascade="all, delete, delete-orphan")
580 580
581 581 pull_requests_other = relationship('PullRequest',
582 582 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
583 583 cascade="all, delete, delete-orphan")
584 584
585 585 def __unicode__(self):
586 586 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
587 587 safe_unicode(self.repo_name))
588 588
589 589 @classmethod
590 590 def get_by_repo_name(cls, repo_name):
591 591 q = Session().query(cls).filter(cls.repo_name == repo_name)
592 592 q = q.options(joinedload(Repository.fork))\
593 593 .options(joinedload(Repository.user))\
594 594 .options(joinedload(Repository.group))
595 595 return q.scalar()
596 596
597 597
598 598 class RepoGroup(Base, BaseModel):
599 599 __tablename__ = 'groups'
600 600 __table_args__ = (
601 601 UniqueConstraint('group_name', 'group_parent_id'),
602 602 CheckConstraint('group_id != group_parent_id'),
603 603 {'extend_existing': True, 'mysql_engine': 'InnoDB',
604 604 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
605 605 )
606 606 __mapper_args__ = {'order_by': 'group_name'}
607 607
608 608 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
609 609 group_name = Column("group_name", String(255), nullable=False, unique=True, default=None)
610 610 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
611 611 group_description = Column("group_description", String(10000), nullable=True, unique=None, default=None)
612 612 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
613 613 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
614 614
615 615 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
616 616 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
617 617 parent_group = relationship('RepoGroup', remote_side=group_id)
618 618 user = relationship('User')
619 619
620 620 def __init__(self, group_name='', parent_group=None):
621 621 self.group_name = group_name
622 622 self.parent_group = parent_group
623 623
624 624 def __unicode__(self):
625 625 return u"<%s('id:%s:%s')>" % (self.__class__.__name__, self.group_id,
626 626 self.group_name)
627 627
628 628 @classmethod
629 629 def url_sep(cls):
630 630 return URL_SEP
631 631
632 632 @classmethod
633 633 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
634 634 if case_insensitive:
635 635 gr = cls.query()\
636 636 .filter(cls.group_name.ilike(group_name))
637 637 else:
638 638 gr = cls.query()\
639 639 .filter(cls.group_name == group_name)
640 640 if cache:
641 641 gr = gr.options(FromCache(
642 642 "sql_cache_short",
643 643 "get_group_%s" % _hash_key(group_name)
644 644 )
645 645 )
646 646 return gr.scalar()
647 647
648 648
649 649 class Permission(Base, BaseModel):
650 650 __tablename__ = 'permissions'
651 651 __table_args__ = (
652 652 Index('p_perm_name_idx', 'permission_name'),
653 653 {'extend_existing': True, 'mysql_engine': 'InnoDB',
654 654 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
655 655 )
656 656 PERMS = [
657 657 ('hg.admin', _('RhodeCode Administrator')),
658 658
659 659 ('repository.none', _('Repository no access')),
660 660 ('repository.read', _('Repository read access')),
661 661 ('repository.write', _('Repository write access')),
662 662 ('repository.admin', _('Repository admin access')),
663 663
664 664 ('group.none', _('Repository group no access')),
665 665 ('group.read', _('Repository group read access')),
666 666 ('group.write', _('Repository group write access')),
667 667 ('group.admin', _('Repository group admin access')),
668 668
669 669 ('usergroup.none', _('User group no access')),
670 670 ('usergroup.read', _('User group read access')),
671 671 ('usergroup.write', _('User group write access')),
672 672 ('usergroup.admin', _('User group admin access')),
673 673
674 674 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
675 675 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
676 676
677 677 ('hg.usergroup.create.false', _('User Group creation disabled')),
678 678 ('hg.usergroup.create.true', _('User Group creation enabled')),
679 679
680 680 ('hg.create.none', _('Repository creation disabled')),
681 681 ('hg.create.repository', _('Repository creation enabled')),
682 682
683 683 ('hg.fork.none', _('Repository forking disabled')),
684 684 ('hg.fork.repository', _('Repository forking enabled')),
685 685
686 686 ('hg.register.none', _('Registration disabled')),
687 687 ('hg.register.manual_activate', _('User Registration with manual account activation')),
688 688 ('hg.register.auto_activate', _('User Registration with automatic account activation')),
689 689
690 690 ('hg.extern_activate.manual', _('Manual activation of external account')),
691 691 ('hg.extern_activate.auto', _('Automatic activation of external account')),
692 692
693 693 ]
694 694
695 695 #definition of system default permissions for DEFAULT user
696 696 DEFAULT_USER_PERMISSIONS = [
697 697 'repository.read',
698 698 'group.read',
699 699 'usergroup.read',
700 700 'hg.create.repository',
701 701 'hg.fork.repository',
702 702 'hg.register.manual_activate',
703 703 'hg.extern_activate.auto',
704 704 ]
705 705
706 706 # defines which permissions are more important higher the more important
707 707 # Weight defines which permissions are more important.
708 708 # The higher number the more important.
709 709 PERM_WEIGHTS = {
710 710 'repository.none': 0,
711 711 'repository.read': 1,
712 712 'repository.write': 3,
713 713 'repository.admin': 4,
714 714
715 715 'group.none': 0,
716 716 'group.read': 1,
717 717 'group.write': 3,
718 718 'group.admin': 4,
719 719
720 720 'usergroup.none': 0,
721 721 'usergroup.read': 1,
722 722 'usergroup.write': 3,
723 723 'usergroup.admin': 4,
724 724 'hg.repogroup.create.false': 0,
725 725 'hg.repogroup.create.true': 1,
726 726
727 727 'hg.usergroup.create.false': 0,
728 728 'hg.usergroup.create.true': 1,
729 729
730 730 'hg.fork.none': 0,
731 731 'hg.fork.repository': 1,
732 732 'hg.create.none': 0,
733 733 'hg.create.repository': 1
734 734 }
735 735
736 736 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
737 737 permission_name = Column("permission_name", String(255), nullable=True, unique=None, default=None)
738 738 permission_longname = Column("permission_longname", String(255), nullable=True, unique=None, default=None)
739 739
740 740 def __unicode__(self):
741 741 return u"<%s('%s:%s')>" % (
742 742 self.__class__.__name__, self.permission_id, self.permission_name
743 743 )
744 744
745 745 @classmethod
746 746 def get_by_key(cls, key):
747 747 return cls.query().filter(cls.permission_name == key).scalar()
748 748
749 749
750 750 class UserRepoToPerm(Base, BaseModel):
751 751 __tablename__ = 'repo_to_perm'
752 752 __table_args__ = (
753 753 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
754 754 {'extend_existing': True, 'mysql_engine': 'InnoDB',
755 755 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
756 756 )
757 757 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
758 758 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
759 759 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
760 760 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
761 761
762 762 user = relationship('User')
763 763 repository = relationship('Repository')
764 764 permission = relationship('Permission')
765 765
766 766 def __unicode__(self):
767 767 return u'<%s => %s >' % (self.user, self.repository)
768 768
769 769
770 770 class UserUserGroupToPerm(Base, BaseModel):
771 771 __tablename__ = 'user_user_group_to_perm'
772 772 __table_args__ = (
773 773 UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
774 774 {'extend_existing': True, 'mysql_engine': 'InnoDB',
775 775 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
776 776 )
777 777 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
778 778 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
779 779 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
780 780 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
781 781
782 782 user = relationship('User')
783 783 user_group = relationship('UserGroup')
784 784 permission = relationship('Permission')
785 785
786 786 def __unicode__(self):
787 787 return u'<%s => %s >' % (self.user, self.user_group)
788 788
789 789
790 790 class UserToPerm(Base, BaseModel):
791 791 __tablename__ = 'user_to_perm'
792 792 __table_args__ = (
793 793 UniqueConstraint('user_id', 'permission_id'),
794 794 {'extend_existing': True, 'mysql_engine': 'InnoDB',
795 795 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
796 796 )
797 797 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
798 798 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
799 799 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
800 800
801 801 user = relationship('User')
802 802 permission = relationship('Permission', lazy='joined')
803 803
804 804 def __unicode__(self):
805 805 return u'<%s => %s >' % (self.user, self.permission)
806 806
807 807
808 808 class UserGroupRepoToPerm(Base, BaseModel):
809 809 __tablename__ = 'users_group_repo_to_perm'
810 810 __table_args__ = (
811 811 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
812 812 {'extend_existing': True, 'mysql_engine': 'InnoDB',
813 813 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
814 814 )
815 815 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
816 816 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
817 817 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
818 818 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
819 819
820 820 users_group = relationship('UserGroup')
821 821 permission = relationship('Permission')
822 822 repository = relationship('Repository')
823 823
824 824 def __unicode__(self):
825 825 return u'<UserGroupRepoToPerm:%s => %s >' % (self.users_group, self.repository)
826 826
827 827
828 828 class UserGroupUserGroupToPerm(Base, BaseModel):
829 829 __tablename__ = 'user_group_user_group_to_perm'
830 830 __table_args__ = (
831 831 UniqueConstraint('target_user_group_id', 'user_group_id', 'permission_id'),
832 832 CheckConstraint('target_user_group_id != user_group_id'),
833 833 {'extend_existing': True, 'mysql_engine': 'InnoDB',
834 834 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
835 835 )
836 836 user_group_user_group_to_perm_id = Column("user_group_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
837 837 target_user_group_id = Column("target_user_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 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
840 840
841 841 target_user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
842 842 user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
843 843 permission = relationship('Permission')
844 844
845 845 def __unicode__(self):
846 846 return u'<UserGroupUserGroup:%s => %s >' % (self.target_user_group, self.user_group)
847 847
848 848
849 849 class UserGroupToPerm(Base, BaseModel):
850 850 __tablename__ = 'users_group_to_perm'
851 851 __table_args__ = (
852 852 UniqueConstraint('users_group_id', 'permission_id',),
853 853 {'extend_existing': True, 'mysql_engine': 'InnoDB',
854 854 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
855 855 )
856 856 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
857 857 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
858 858 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
859 859
860 860 users_group = relationship('UserGroup')
861 861 permission = relationship('Permission')
862 862
863 863
864 864 class UserRepoGroupToPerm(Base, BaseModel):
865 865 __tablename__ = 'user_repo_group_to_perm'
866 866 __table_args__ = (
867 867 UniqueConstraint('user_id', 'group_id', 'permission_id'),
868 868 {'extend_existing': True, 'mysql_engine': 'InnoDB',
869 869 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
870 870 )
871 871
872 872 group_to_perm_id = Column("group_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 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
875 875 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
876 876
877 877 user = relationship('User')
878 878 group = relationship('RepoGroup')
879 879 permission = relationship('Permission')
880 880
881 881
882 882 class UserGroupRepoGroupToPerm(Base, BaseModel):
883 883 __tablename__ = 'users_group_repo_group_to_perm'
884 884 __table_args__ = (
885 885 UniqueConstraint('users_group_id', 'group_id'),
886 886 {'extend_existing': True, 'mysql_engine': 'InnoDB',
887 887 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
888 888 )
889 889
890 890 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
891 891 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
892 892 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
893 893 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
894 894
895 895 users_group = relationship('UserGroup')
896 896 permission = relationship('Permission')
897 897 group = relationship('RepoGroup')
898 898
899 899
900 900 class Statistics(Base, BaseModel):
901 901 __tablename__ = 'statistics'
902 902 __table_args__ = (
903 903 UniqueConstraint('repository_id'),
904 904 {'extend_existing': True, 'mysql_engine': 'InnoDB',
905 905 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
906 906 )
907 907 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
908 908 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
909 909 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
910 910 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
911 911 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
912 912 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
913 913
914 914 repository = relationship('Repository', single_parent=True)
915 915
916 916
917 917 class UserFollowing(Base, BaseModel):
918 918 __tablename__ = 'user_followings'
919 919 __table_args__ = (
920 920 UniqueConstraint('user_id', 'follows_repository_id'),
921 921 UniqueConstraint('user_id', 'follows_user_id'),
922 922 {'extend_existing': True, 'mysql_engine': 'InnoDB',
923 923 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
924 924 )
925 925
926 926 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
927 927 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
928 928 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
929 929 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
930 930 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
931 931
932 932 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
933 933
934 934 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
935 935 follows_repository = relationship('Repository', order_by='Repository.repo_name')
936 936
937 937
938 938 class CacheInvalidation(Base, BaseModel):
939 939 __tablename__ = 'cache_invalidation'
940 940 __table_args__ = (
941 941 UniqueConstraint('cache_key'),
942 942 Index('key_idx', 'cache_key'),
943 943 {'extend_existing': True, 'mysql_engine': 'InnoDB',
944 944 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
945 945 )
946 946 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
947 947 cache_key = Column("cache_key", String(255), nullable=True, unique=None, default=None)
948 948 cache_args = Column("cache_args", String(255), nullable=True, unique=None, default=None)
949 949 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
950 950
951 951 def __init__(self, cache_key, cache_args=''):
952 952 self.cache_key = cache_key
953 953 self.cache_args = cache_args
954 954 self.cache_active = False
955 955
956 956
957 957 class ChangesetComment(Base, BaseModel):
958 958 __tablename__ = 'changeset_comments'
959 959 __table_args__ = (
960 960 Index('cc_revision_idx', 'revision'),
961 961 {'extend_existing': True, 'mysql_engine': 'InnoDB',
962 962 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
963 963 )
964 964 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
965 965 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
966 966 revision = Column('revision', String(40), nullable=True)
967 967 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
968 968 line_no = Column('line_no', Unicode(10), nullable=True)
969 969 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
970 970 f_path = Column('f_path', Unicode(1000), nullable=True)
971 971 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
972 972 text = Column('text', UnicodeText().with_variant(UnicodeText(25000), 'mysql'), nullable=False)
973 973 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
974 974 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
975 975
976 976 author = relationship('User', lazy='joined')
977 977 repo = relationship('Repository')
978 978 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
979 979 pull_request = relationship('PullRequest', lazy='joined')
980 980
981 981
982 982 class ChangesetStatus(Base, BaseModel):
983 983 __tablename__ = 'changeset_statuses'
984 984 __table_args__ = (
985 985 Index('cs_revision_idx', 'revision'),
986 986 Index('cs_version_idx', 'version'),
987 987 UniqueConstraint('repo_id', 'revision', 'version'),
988 988 {'extend_existing': True, 'mysql_engine': 'InnoDB',
989 989 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
990 990 )
991 991 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
992 992 STATUS_APPROVED = 'approved'
993 993 STATUS_REJECTED = 'rejected'
994 994 STATUS_UNDER_REVIEW = 'under_review'
995 995
996 996 STATUSES = [
997 997 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
998 998 (STATUS_APPROVED, _("Approved")),
999 999 (STATUS_REJECTED, _("Rejected")),
1000 1000 (STATUS_UNDER_REVIEW, _("Under Review")),
1001 1001 ]
1002 1002
1003 1003 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1004 1004 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1005 1005 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1006 1006 revision = Column('revision', String(40), nullable=False)
1007 1007 status = Column('status', String(128), nullable=False, default=DEFAULT)
1008 1008 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1009 1009 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1010 1010 version = Column('version', Integer(), nullable=False, default=0)
1011 1011 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1012 1012
1013 1013 author = relationship('User', lazy='joined')
1014 1014 repo = relationship('Repository')
1015 1015 comment = relationship('ChangesetComment', lazy='joined')
1016 1016 pull_request = relationship('PullRequest', lazy='joined')
1017 1017
1018 1018
1019 1019
1020 1020 class PullRequest(Base, BaseModel):
1021 1021 __tablename__ = 'pull_requests'
1022 1022 __table_args__ = (
1023 1023 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1024 1024 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1025 1025 )
1026 1026
1027 1027 STATUS_NEW = u'new'
1028 1028 STATUS_OPEN = u'open'
1029 1029 STATUS_CLOSED = u'closed'
1030 1030
1031 1031 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1032 1032 title = Column('title', Unicode(256), nullable=True)
1033 1033 description = Column('description', UnicodeText().with_variant(UnicodeText(10240), 'mysql'), nullable=True)
1034 1034 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1035 1035 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1036 1036 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1037 1037 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1038 1038 _revisions = Column('revisions', UnicodeText().with_variant(UnicodeText(20500), 'mysql'))
1039 1039 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1040 1040 org_ref = Column('org_ref', Unicode(256), nullable=False)
1041 1041 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1042 1042 other_ref = Column('other_ref', Unicode(256), nullable=False)
1043 1043
1044 1044 author = relationship('User', lazy='joined')
1045 1045 reviewers = relationship('PullRequestReviewers',
1046 1046 cascade="all, delete, delete-orphan")
1047 1047 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1048 1048 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1049 1049 statuses = relationship('ChangesetStatus')
1050 1050 comments = relationship('ChangesetComment',
1051 1051 cascade="all, delete, delete-orphan")
1052 1052
1053 1053
1054 1054 class PullRequestReviewers(Base, BaseModel):
1055 1055 __tablename__ = 'pull_request_reviewers'
1056 1056 __table_args__ = (
1057 1057 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1058 1058 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1059 1059 )
1060 1060
1061 1061 def __init__(self, user=None, pull_request=None):
1062 1062 self.user = user
1063 1063 self.pull_request = pull_request
1064 1064
1065 1065 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1066 1066 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1067 1067 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1068 1068
1069 1069 user = relationship('User')
1070 1070 pull_request = relationship('PullRequest')
1071 1071
1072 1072
1073 1073 class Notification(Base, BaseModel):
1074 1074 __tablename__ = 'notifications'
1075 1075 __table_args__ = (
1076 1076 Index('notification_type_idx', 'type'),
1077 1077 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1078 1078 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1079 1079 )
1080 1080
1081 1081 TYPE_CHANGESET_COMMENT = u'cs_comment'
1082 1082 TYPE_MESSAGE = u'message'
1083 1083 TYPE_MENTION = u'mention'
1084 1084 TYPE_REGISTRATION = u'registration'
1085 1085 TYPE_PULL_REQUEST = u'pull_request'
1086 1086 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1087 1087
1088 1088 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1089 1089 subject = Column('subject', Unicode(512), nullable=True)
1090 1090 body = Column('body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True)
1091 1091 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1092 1092 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1093 1093 type_ = Column('type', Unicode(256))
1094 1094
1095 1095 created_by_user = relationship('User')
1096 1096 notifications_to_users = relationship('UserNotification', lazy='joined',
1097 1097 cascade="all, delete, delete-orphan")
1098 1098
1099 1099
1100 1100 class UserNotification(Base, BaseModel):
1101 1101 __tablename__ = 'user_to_notification'
1102 1102 __table_args__ = (
1103 1103 UniqueConstraint('user_id', 'notification_id'),
1104 1104 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1105 1105 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1106 1106 )
1107 1107 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1108 1108 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1109 1109 read = Column('read', Boolean, default=False)
1110 1110 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1111 1111
1112 1112 user = relationship('User', lazy="joined")
1113 1113 notification = relationship('Notification', lazy="joined",
1114 1114 order_by=lambda: Notification.created_on.desc(),)
1115 1115
1116 1116
1117 1117 class Gist(Base, BaseModel):
1118 1118 __tablename__ = 'gists'
1119 1119 __table_args__ = (
1120 1120 Index('g_gist_access_id_idx', 'gist_access_id'),
1121 1121 Index('g_created_on_idx', 'created_on'),
1122 1122 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1123 1123 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1124 1124 )
1125 1125 GIST_PUBLIC = u'public'
1126 1126 GIST_PRIVATE = u'private'
1127 1127
1128 1128 gist_id = Column('gist_id', Integer(), primary_key=True)
1129 1129 gist_access_id = Column('gist_access_id', Unicode(250))
1130 1130 gist_description = Column('gist_description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
1131 1131 gist_owner = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=True)
1132 1132 gist_expires = Column('gist_expires', Float(53), nullable=False)
1133 1133 gist_type = Column('gist_type', Unicode(128), nullable=False)
1134 1134 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1135 1135 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1136 1136
1137 1137 owner = relationship('User')
1138 1138
1139 1139
1140 1140 class DbMigrateVersion(Base, BaseModel):
1141 1141 __tablename__ = 'db_migrate_version'
1142 1142 __table_args__ = (
1143 1143 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1144 1144 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1145 1145 )
1146 1146 repository_id = Column('repository_id', String(250), primary_key=True)
1147 1147 repository_path = Column('repository_path', Text)
1148 1148 version = Column('version', Integer)
@@ -1,1171 +1,1171 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import os
22 22 import time
23 23 import logging
24 24 import datetime
25 25 import traceback
26 26 import hashlib
27 27 import collections
28 28 import functools
29 29
30 30 from sqlalchemy import *
31 31 from sqlalchemy.ext.hybrid import hybrid_property
32 32 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
33 33 from sqlalchemy.exc import DatabaseError
34 34 from beaker.cache import cache_region, region_invalidate
35 35 from webob.exc import HTTPNotFound
36 36
37 from pylons.i18n.translation import lazy_ugettext as _
37 from rhodecode.translation import _
38 38
39 39 from rhodecode.lib.vcs import get_backend
40 40 from rhodecode.lib.vcs.utils.helpers import get_scm
41 41 from rhodecode.lib.vcs.exceptions import VCSError
42 42 from zope.cachedescriptors.property import Lazy as LazyProperty
43 43 from rhodecode.lib.vcs.backends.base import EmptyCommit
44 44
45 45 from rhodecode.lib.utils2 import str2bool, safe_str, get_commit_safe, \
46 46 safe_unicode, remove_prefix, time_to_datetime, aslist, Optional, safe_int
47 47 from rhodecode.lib.ext_json import json
48 48 from rhodecode.lib.caching_query import FromCache
49 49
50 50 from rhodecode.model.meta import Base, Session
51 51
52 52 URL_SEP = '/'
53 53 log = logging.getLogger(__name__)
54 54
55 55 #==============================================================================
56 56 # BASE CLASSES
57 57 #==============================================================================
58 58
59 59 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
60 60
61 61
62 62 class BaseModel(object):
63 63 """
64 64 Base Model for all classes
65 65 """
66 66
67 67 @classmethod
68 68 def _get_keys(cls):
69 69 """return column names for this model """
70 70 return class_mapper(cls).c.keys()
71 71
72 72 def get_dict(self):
73 73 """
74 74 return dict with keys and values corresponding
75 75 to this model data """
76 76
77 77 d = {}
78 78 for k in self._get_keys():
79 79 d[k] = getattr(self, k)
80 80
81 81 # also use __json__() if present to get additional fields
82 82 _json_attr = getattr(self, '__json__', None)
83 83 if _json_attr:
84 84 # update with attributes from __json__
85 85 if callable(_json_attr):
86 86 _json_attr = _json_attr()
87 87 for k, val in _json_attr.iteritems():
88 88 d[k] = val
89 89 return d
90 90
91 91 def get_appstruct(self):
92 92 """return list with keys and values tupples corresponding
93 93 to this model data """
94 94
95 95 l = []
96 96 for k in self._get_keys():
97 97 l.append((k, getattr(self, k),))
98 98 return l
99 99
100 100 def populate_obj(self, populate_dict):
101 101 """populate model with data from given populate_dict"""
102 102
103 103 for k in self._get_keys():
104 104 if k in populate_dict:
105 105 setattr(self, k, populate_dict[k])
106 106
107 107 @classmethod
108 108 def query(cls):
109 109 return Session().query(cls)
110 110
111 111 @classmethod
112 112 def get(cls, id_):
113 113 if id_:
114 114 return cls.query().get(id_)
115 115
116 116 @classmethod
117 117 def get_or_404(cls, id_):
118 118 try:
119 119 id_ = int(id_)
120 120 except (TypeError, ValueError):
121 121 raise HTTPNotFound
122 122
123 123 res = cls.query().get(id_)
124 124 if not res:
125 125 raise HTTPNotFound
126 126 return res
127 127
128 128 @classmethod
129 129 def getAll(cls):
130 130 # deprecated and left for backward compatibility
131 131 return cls.get_all()
132 132
133 133 @classmethod
134 134 def get_all(cls):
135 135 return cls.query().all()
136 136
137 137 @classmethod
138 138 def delete(cls, id_):
139 139 obj = cls.query().get(id_)
140 140 Session().delete(obj)
141 141
142 142 def __repr__(self):
143 143 if hasattr(self, '__unicode__'):
144 144 # python repr needs to return str
145 145 return safe_str(self.__unicode__())
146 146 return '<DB:%s>' % (self.__class__.__name__)
147 147
148 148
149 149 class RhodeCodeSetting(Base, BaseModel):
150 150 SETTINGS_TYPES = {
151 151 'str': safe_str,
152 152 'int': safe_int,
153 153 'unicode': safe_unicode,
154 154 'bool': str2bool,
155 155 'list': functools.partial(aslist, sep=',')
156 156 }
157 157 __tablename__ = 'rhodecode_settings'
158 158 __table_args__ = (
159 159 UniqueConstraint('app_settings_name'),
160 160 {'extend_existing': True, 'mysql_engine': 'InnoDB',
161 161 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
162 162 )
163 163 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
164 164 app_settings_name = Column("app_settings_name", String(255), nullable=True, unique=None, default=None)
165 165 _app_settings_value = Column("app_settings_value", String(4096), nullable=True, unique=None, default=None)
166 166 _app_settings_type = Column("app_settings_type", String(255), nullable=True, unique=None, default=None)
167 167
168 168 def __init__(self, key='', val='', type='unicode'):
169 169 self.app_settings_name = key
170 170 self.app_settings_value = val
171 171 self.app_settings_type = type
172 172
173 173 @validates('_app_settings_value')
174 174 def validate_settings_value(self, key, val):
175 175 assert type(val) == unicode
176 176 return val
177 177
178 178 @hybrid_property
179 179 def app_settings_value(self):
180 180 v = self._app_settings_value
181 181 _type = self.app_settings_type
182 182 converter = self.SETTINGS_TYPES.get(_type) or self.SETTINGS_TYPES['unicode']
183 183 return converter(v)
184 184
185 185 @app_settings_value.setter
186 186 def app_settings_value(self, val):
187 187 """
188 188 Setter that will always make sure we use unicode in app_settings_value
189 189
190 190 :param val:
191 191 """
192 192 self._app_settings_value = safe_unicode(val)
193 193
194 194 @hybrid_property
195 195 def app_settings_type(self):
196 196 return self._app_settings_type
197 197
198 198 @app_settings_type.setter
199 199 def app_settings_type(self, val):
200 200 if val not in self.SETTINGS_TYPES:
201 201 raise Exception('type must be one of %s got %s'
202 202 % (self.SETTINGS_TYPES.keys(), val))
203 203 self._app_settings_type = val
204 204
205 205 def __unicode__(self):
206 206 return u"<%s('%s:%s[%s]')>" % (
207 207 self.__class__.__name__,
208 208 self.app_settings_name, self.app_settings_value, self.app_settings_type
209 209 )
210 210
211 211
212 212 class RhodeCodeUi(Base, BaseModel):
213 213 __tablename__ = 'rhodecode_ui'
214 214 __table_args__ = (
215 215 UniqueConstraint('ui_key'),
216 216 {'extend_existing': True, 'mysql_engine': 'InnoDB',
217 217 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
218 218 )
219 219
220 220 HOOK_REPO_SIZE = 'changegroup.repo_size'
221 221 HOOK_PUSH = 'changegroup.push_logger'
222 222 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
223 223 HOOK_PULL = 'outgoing.pull_logger'
224 224 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
225 225
226 226 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
227 227 ui_section = Column("ui_section", String(255), nullable=True, unique=None, default=None)
228 228 ui_key = Column("ui_key", String(255), nullable=True, unique=None, default=None)
229 229 ui_value = Column("ui_value", String(255), nullable=True, unique=None, default=None)
230 230 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
231 231
232 232
233 233
234 234 class User(Base, BaseModel):
235 235 __tablename__ = 'users'
236 236 __table_args__ = (
237 237 UniqueConstraint('username'), UniqueConstraint('email'),
238 238 Index('u_username_idx', 'username'),
239 239 Index('u_email_idx', 'email'),
240 240 {'extend_existing': True, 'mysql_engine': 'InnoDB',
241 241 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
242 242 )
243 243 DEFAULT_USER = 'default'
244 244
245 245 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
246 246 username = Column("username", String(255), nullable=True, unique=None, default=None)
247 247 password = Column("password", String(255), nullable=True, unique=None, default=None)
248 248 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
249 249 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
250 250 name = Column("firstname", String(255), nullable=True, unique=None, default=None)
251 251 lastname = Column("lastname", String(255), nullable=True, unique=None, default=None)
252 252 _email = Column("email", String(255), nullable=True, unique=None, default=None)
253 253 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
254 254 extern_type = Column("extern_type", String(255), nullable=True, unique=None, default=None)
255 255 extern_name = Column("extern_name", String(255), nullable=True, unique=None, default=None)
256 256 #for migration reasons, this is going to be later deleted
257 257 ldap_dn = Column("ldap_dn", String(255), nullable=True, unique=None, default=None)
258 258
259 259 api_key = Column("api_key", String(255), nullable=True, unique=None, default=None)
260 260 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
261 261 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
262 262
263 263 user_log = relationship('UserLog')
264 264 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
265 265
266 266 repositories = relationship('Repository')
267 267 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
268 268 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
269 269
270 270 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
271 271 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
272 272
273 273 group_member = relationship('UserGroupMember', cascade='all')
274 274
275 275 notifications = relationship('UserNotification', cascade='all')
276 276 # notifications assigned to this user
277 277 user_created_notifications = relationship('Notification', cascade='all')
278 278 # comments created by this user
279 279 user_comments = relationship('ChangesetComment', cascade='all')
280 280 user_emails = relationship('UserEmailMap', cascade='all')
281 281
282 282 @hybrid_property
283 283 def email(self):
284 284 return self._email
285 285
286 286 @email.setter
287 287 def email(self, val):
288 288 self._email = val.lower() if val else None
289 289
290 290 @property
291 291 def firstname(self):
292 292 # alias for future
293 293 return self.name
294 294
295 295 @property
296 296 def username_and_name(self):
297 297 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
298 298
299 299 @property
300 300 def full_name(self):
301 301 return '%s %s' % (self.firstname, self.lastname)
302 302
303 303 @property
304 304 def full_contact(self):
305 305 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
306 306
307 307 @property
308 308 def short_contact(self):
309 309 return '%s %s' % (self.firstname, self.lastname)
310 310
311 311 @property
312 312 def is_admin(self):
313 313 return self.admin
314 314
315 315 @classmethod
316 316 def get_by_username(cls, username, case_insensitive=False, cache=False):
317 317 if case_insensitive:
318 318 q = cls.query().filter(cls.username.ilike(username))
319 319 else:
320 320 q = cls.query().filter(cls.username == username)
321 321
322 322 if cache:
323 323 q = q.options(FromCache(
324 324 "sql_cache_short",
325 325 "get_user_%s" % _hash_key(username)
326 326 )
327 327 )
328 328 return q.scalar()
329 329
330 330 @classmethod
331 331 def get_by_auth_token(cls, auth_token, cache=False):
332 332 q = cls.query().filter(cls.api_key == auth_token)
333 333
334 334 if cache:
335 335 q = q.options(FromCache("sql_cache_short",
336 336 "get_auth_token_%s" % auth_token))
337 337 return q.scalar()
338 338
339 339 @classmethod
340 340 def get_by_email(cls, email, case_insensitive=False, cache=False):
341 341 if case_insensitive:
342 342 q = cls.query().filter(cls.email.ilike(email))
343 343 else:
344 344 q = cls.query().filter(cls.email == email)
345 345
346 346 if cache:
347 347 q = q.options(FromCache("sql_cache_short",
348 348 "get_email_key_%s" % email))
349 349
350 350 ret = q.scalar()
351 351 if ret is None:
352 352 q = UserEmailMap.query()
353 353 # try fetching in alternate email map
354 354 if case_insensitive:
355 355 q = q.filter(UserEmailMap.email.ilike(email))
356 356 else:
357 357 q = q.filter(UserEmailMap.email == email)
358 358 q = q.options(joinedload(UserEmailMap.user))
359 359 if cache:
360 360 q = q.options(FromCache("sql_cache_short",
361 361 "get_email_map_key_%s" % email))
362 362 ret = getattr(q.scalar(), 'user', None)
363 363
364 364 return ret
365 365
366 366 @classmethod
367 367 def get_first_admin(cls):
368 368 user = User.query().filter(User.admin == True).first()
369 369 if user is None:
370 370 raise Exception('Missing administrative account!')
371 371 return user
372 372
373 373 @classmethod
374 374 def get_default_user(cls, cache=False):
375 375 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
376 376 if user is None:
377 377 raise Exception('Missing default account!')
378 378 return user
379 379
380 380
381 381
382 382
383 383 class UserEmailMap(Base, BaseModel):
384 384 __tablename__ = 'user_email_map'
385 385 __table_args__ = (
386 386 Index('uem_email_idx', 'email'),
387 387 UniqueConstraint('email'),
388 388 {'extend_existing': True, 'mysql_engine': 'InnoDB',
389 389 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
390 390 )
391 391 __mapper_args__ = {}
392 392
393 393 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
394 394 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
395 395 _email = Column("email", String(255), nullable=True, unique=False, default=None)
396 396 user = relationship('User', lazy='joined')
397 397
398 398 @validates('_email')
399 399 def validate_email(self, key, email):
400 400 # check if this email is not main one
401 401 main_email = Session().query(User).filter(User.email == email).scalar()
402 402 if main_email is not None:
403 403 raise AttributeError('email %s is present is user table' % email)
404 404 return email
405 405
406 406 @hybrid_property
407 407 def email(self):
408 408 return self._email
409 409
410 410 @email.setter
411 411 def email(self, val):
412 412 self._email = val.lower() if val else None
413 413
414 414
415 415 class UserIpMap(Base, BaseModel):
416 416 __tablename__ = 'user_ip_map'
417 417 __table_args__ = (
418 418 UniqueConstraint('user_id', 'ip_addr'),
419 419 {'extend_existing': True, 'mysql_engine': 'InnoDB',
420 420 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
421 421 )
422 422 __mapper_args__ = {}
423 423
424 424 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
425 425 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
426 426 ip_addr = Column("ip_addr", String(255), nullable=True, unique=False, default=None)
427 427 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
428 428 user = relationship('User', lazy='joined')
429 429
430 430
431 431 class UserLog(Base, BaseModel):
432 432 __tablename__ = 'user_logs'
433 433 __table_args__ = (
434 434 {'extend_existing': True, 'mysql_engine': 'InnoDB',
435 435 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
436 436 )
437 437 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
438 438 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
439 439 username = Column("username", String(255), nullable=True, unique=None, default=None)
440 440 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
441 441 repository_name = Column("repository_name", String(255), nullable=True, unique=None, default=None)
442 442 user_ip = Column("user_ip", String(255), nullable=True, unique=None, default=None)
443 443 action = Column("action", String(1200000), nullable=True, unique=None, default=None)
444 444 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
445 445
446 446 def __unicode__(self):
447 447 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
448 448 self.repository_name,
449 449 self.action)
450 450
451 451 user = relationship('User')
452 452 repository = relationship('Repository', cascade='')
453 453
454 454
455 455 class UserGroup(Base, BaseModel):
456 456 __tablename__ = 'users_groups'
457 457 __table_args__ = (
458 458 {'extend_existing': True, 'mysql_engine': 'InnoDB',
459 459 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
460 460 )
461 461
462 462 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
463 463 users_group_name = Column("users_group_name", String(255), nullable=False, unique=True, default=None)
464 464 user_group_description = Column("user_group_description", String(10000), nullable=True, unique=None, default=None)
465 465 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
466 466 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
467 467 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
468 468 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
469 469
470 470 # don't trigger lazy load for migrations
471 471 #members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
472 472 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
473 473 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
474 474 users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
475 475 user_user_group_to_perm = relationship('UserUserGroupToPerm', cascade='all')
476 476 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm ', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
477 477
478 478 user = relationship('User')
479 479
480 480 def __unicode__(self):
481 481 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
482 482 self.users_group_id,
483 483 self.users_group_name)
484 484
485 485 @classmethod
486 486 def get_by_group_name(cls, group_name, cache=False,
487 487 case_insensitive=False):
488 488 if case_insensitive:
489 489 q = cls.query().filter(cls.users_group_name.ilike(group_name))
490 490 else:
491 491 q = cls.query().filter(cls.users_group_name == group_name)
492 492 if cache:
493 493 q = q.options(FromCache(
494 494 "sql_cache_short",
495 495 "get_user_%s" % _hash_key(group_name)
496 496 )
497 497 )
498 498 return q.scalar()
499 499
500 500 @classmethod
501 501 def get(cls, user_group_id, cache=False):
502 502 user_group = cls.query()
503 503 if cache:
504 504 user_group = user_group.options(FromCache("sql_cache_short",
505 505 "get_users_group_%s" % user_group_id))
506 506 return user_group.get(user_group_id)
507 507
508 508
509 509 class UserGroupMember(Base, BaseModel):
510 510 __tablename__ = 'users_groups_members'
511 511 __table_args__ = (
512 512 {'extend_existing': True, 'mysql_engine': 'InnoDB',
513 513 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
514 514 )
515 515
516 516 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
517 517 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
518 518 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
519 519
520 520 user = relationship('User', lazy='joined')
521 521 users_group = relationship('UserGroup')
522 522
523 523 def __init__(self, gr_id='', u_id=''):
524 524 self.users_group_id = gr_id
525 525 self.user_id = u_id
526 526
527 527
528 528 class RepositoryField(Base, BaseModel):
529 529 __tablename__ = 'repositories_fields'
530 530 __table_args__ = (
531 531 UniqueConstraint('repository_id', 'field_key'), # no-multi field
532 532 {'extend_existing': True, 'mysql_engine': 'InnoDB',
533 533 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
534 534 )
535 535 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
536 536
537 537 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
538 538 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
539 539 field_key = Column("field_key", String(250))
540 540 field_label = Column("field_label", String(1024), nullable=False)
541 541 field_value = Column("field_value", String(10000), nullable=False)
542 542 field_desc = Column("field_desc", String(1024), nullable=False)
543 543 field_type = Column("field_type", String(256), nullable=False, unique=None)
544 544 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
545 545
546 546 repository = relationship('Repository')
547 547
548 548 @classmethod
549 549 def get_by_key_name(cls, key, repo):
550 550 row = cls.query()\
551 551 .filter(cls.repository == repo)\
552 552 .filter(cls.field_key == key).scalar()
553 553 return row
554 554
555 555
556 556 class Repository(Base, BaseModel):
557 557 __tablename__ = 'repositories'
558 558 __table_args__ = (
559 559 UniqueConstraint('repo_name'),
560 560 Index('r_repo_name_idx', 'repo_name'),
561 561 {'extend_existing': True, 'mysql_engine': 'InnoDB',
562 562 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
563 563 )
564 564
565 565 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
566 566 repo_name = Column("repo_name", String(255), nullable=False, unique=True, default=None)
567 567 clone_uri = Column("clone_uri", String(255), nullable=True, unique=False, default=None)
568 568 repo_type = Column("repo_type", String(255), nullable=False, unique=False, default=None)
569 569 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
570 570 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
571 571 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
572 572 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
573 573 description = Column("description", String(10000), nullable=True, unique=None, default=None)
574 574 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
575 575 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
576 576 landing_rev = Column("landing_revision", String(255), nullable=False, unique=False, default=None)
577 577 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
578 578 _locked = Column("locked", String(255), nullable=True, unique=False, default=None)
579 579 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
580 580
581 581 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
582 582 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
583 583
584 584 user = relationship('User')
585 585 fork = relationship('Repository', remote_side=repo_id)
586 586 group = relationship('RepoGroup')
587 587 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
588 588 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
589 589 stats = relationship('Statistics', cascade='all', uselist=False)
590 590
591 591 followers = relationship('UserFollowing',
592 592 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
593 593 cascade='all')
594 594 extra_fields = relationship('RepositoryField',
595 595 cascade="all, delete, delete-orphan")
596 596
597 597 logs = relationship('UserLog')
598 598 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
599 599
600 600 pull_requests_org = relationship('PullRequest',
601 601 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
602 602 cascade="all, delete, delete-orphan")
603 603
604 604 pull_requests_other = relationship('PullRequest',
605 605 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
606 606 cascade="all, delete, delete-orphan")
607 607
608 608 def __unicode__(self):
609 609 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
610 610 safe_unicode(self.repo_name))
611 611
612 612 @classmethod
613 613 def get_by_repo_name(cls, repo_name):
614 614 q = Session().query(cls).filter(cls.repo_name == repo_name)
615 615 q = q.options(joinedload(Repository.fork))\
616 616 .options(joinedload(Repository.user))\
617 617 .options(joinedload(Repository.group))
618 618 return q.scalar()
619 619
620 620
621 621 class RepoGroup(Base, BaseModel):
622 622 __tablename__ = 'groups'
623 623 __table_args__ = (
624 624 UniqueConstraint('group_name', 'group_parent_id'),
625 625 CheckConstraint('group_id != group_parent_id'),
626 626 {'extend_existing': True, 'mysql_engine': 'InnoDB',
627 627 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
628 628 )
629 629 __mapper_args__ = {'order_by': 'group_name'}
630 630
631 631 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
632 632 group_name = Column("group_name", String(255), nullable=False, unique=True, default=None)
633 633 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
634 634 group_description = Column("group_description", String(10000), nullable=True, unique=None, default=None)
635 635 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
636 636 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
637 637
638 638 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
639 639 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
640 640 parent_group = relationship('RepoGroup', remote_side=group_id)
641 641 user = relationship('User')
642 642
643 643 def __init__(self, group_name='', parent_group=None):
644 644 self.group_name = group_name
645 645 self.parent_group = parent_group
646 646
647 647 def __unicode__(self):
648 648 return u"<%s('id:%s:%s')>" % (self.__class__.__name__, self.group_id,
649 649 self.group_name)
650 650
651 651 @classmethod
652 652 def url_sep(cls):
653 653 return URL_SEP
654 654
655 655 @classmethod
656 656 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
657 657 if case_insensitive:
658 658 gr = cls.query()\
659 659 .filter(cls.group_name.ilike(group_name))
660 660 else:
661 661 gr = cls.query()\
662 662 .filter(cls.group_name == group_name)
663 663 if cache:
664 664 gr = gr.options(FromCache(
665 665 "sql_cache_short",
666 666 "get_group_%s" % _hash_key(group_name)
667 667 )
668 668 )
669 669 return gr.scalar()
670 670
671 671
672 672 class Permission(Base, BaseModel):
673 673 __tablename__ = 'permissions'
674 674 __table_args__ = (
675 675 Index('p_perm_name_idx', 'permission_name'),
676 676 {'extend_existing': True, 'mysql_engine': 'InnoDB',
677 677 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
678 678 )
679 679 PERMS = [
680 680 ('hg.admin', _('RhodeCode Administrator')),
681 681
682 682 ('repository.none', _('Repository no access')),
683 683 ('repository.read', _('Repository read access')),
684 684 ('repository.write', _('Repository write access')),
685 685 ('repository.admin', _('Repository admin access')),
686 686
687 687 ('group.none', _('Repository group no access')),
688 688 ('group.read', _('Repository group read access')),
689 689 ('group.write', _('Repository group write access')),
690 690 ('group.admin', _('Repository group admin access')),
691 691
692 692 ('usergroup.none', _('User group no access')),
693 693 ('usergroup.read', _('User group read access')),
694 694 ('usergroup.write', _('User group write access')),
695 695 ('usergroup.admin', _('User group admin access')),
696 696
697 697 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
698 698 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
699 699
700 700 ('hg.usergroup.create.false', _('User Group creation disabled')),
701 701 ('hg.usergroup.create.true', _('User Group creation enabled')),
702 702
703 703 ('hg.create.none', _('Repository creation disabled')),
704 704 ('hg.create.repository', _('Repository creation enabled')),
705 705
706 706 ('hg.fork.none', _('Repository forking disabled')),
707 707 ('hg.fork.repository', _('Repository forking enabled')),
708 708
709 709 ('hg.register.none', _('Registration disabled')),
710 710 ('hg.register.manual_activate', _('User Registration with manual account activation')),
711 711 ('hg.register.auto_activate', _('User Registration with automatic account activation')),
712 712
713 713 ('hg.extern_activate.manual', _('Manual activation of external account')),
714 714 ('hg.extern_activate.auto', _('Automatic activation of external account')),
715 715
716 716 ]
717 717
718 718 #definition of system default permissions for DEFAULT user
719 719 DEFAULT_USER_PERMISSIONS = [
720 720 'repository.read',
721 721 'group.read',
722 722 'usergroup.read',
723 723 'hg.create.repository',
724 724 'hg.fork.repository',
725 725 'hg.register.manual_activate',
726 726 'hg.extern_activate.auto',
727 727 ]
728 728
729 729 # defines which permissions are more important higher the more important
730 730 # Weight defines which permissions are more important.
731 731 # The higher number the more important.
732 732 PERM_WEIGHTS = {
733 733 'repository.none': 0,
734 734 'repository.read': 1,
735 735 'repository.write': 3,
736 736 'repository.admin': 4,
737 737
738 738 'group.none': 0,
739 739 'group.read': 1,
740 740 'group.write': 3,
741 741 'group.admin': 4,
742 742
743 743 'usergroup.none': 0,
744 744 'usergroup.read': 1,
745 745 'usergroup.write': 3,
746 746 'usergroup.admin': 4,
747 747 'hg.repogroup.create.false': 0,
748 748 'hg.repogroup.create.true': 1,
749 749
750 750 'hg.usergroup.create.false': 0,
751 751 'hg.usergroup.create.true': 1,
752 752
753 753 'hg.fork.none': 0,
754 754 'hg.fork.repository': 1,
755 755 'hg.create.none': 0,
756 756 'hg.create.repository': 1
757 757 }
758 758
759 759 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
760 760 permission_name = Column("permission_name", String(255), nullable=True, unique=None, default=None)
761 761 permission_longname = Column("permission_longname", String(255), nullable=True, unique=None, default=None)
762 762
763 763 def __unicode__(self):
764 764 return u"<%s('%s:%s')>" % (
765 765 self.__class__.__name__, self.permission_id, self.permission_name
766 766 )
767 767
768 768 @classmethod
769 769 def get_by_key(cls, key):
770 770 return cls.query().filter(cls.permission_name == key).scalar()
771 771
772 772
773 773 class UserRepoToPerm(Base, BaseModel):
774 774 __tablename__ = 'repo_to_perm'
775 775 __table_args__ = (
776 776 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
777 777 {'extend_existing': True, 'mysql_engine': 'InnoDB',
778 778 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
779 779 )
780 780 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
781 781 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
782 782 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
783 783 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
784 784
785 785 user = relationship('User')
786 786 repository = relationship('Repository')
787 787 permission = relationship('Permission')
788 788
789 789 def __unicode__(self):
790 790 return u'<%s => %s >' % (self.user, self.repository)
791 791
792 792
793 793 class UserUserGroupToPerm(Base, BaseModel):
794 794 __tablename__ = 'user_user_group_to_perm'
795 795 __table_args__ = (
796 796 UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
797 797 {'extend_existing': True, 'mysql_engine': 'InnoDB',
798 798 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
799 799 )
800 800 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
801 801 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
802 802 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
803 803 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
804 804
805 805 user = relationship('User')
806 806 user_group = relationship('UserGroup')
807 807 permission = relationship('Permission')
808 808
809 809 def __unicode__(self):
810 810 return u'<%s => %s >' % (self.user, self.user_group)
811 811
812 812
813 813 class UserToPerm(Base, BaseModel):
814 814 __tablename__ = 'user_to_perm'
815 815 __table_args__ = (
816 816 UniqueConstraint('user_id', 'permission_id'),
817 817 {'extend_existing': True, 'mysql_engine': 'InnoDB',
818 818 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
819 819 )
820 820 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
821 821 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
822 822 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
823 823
824 824 user = relationship('User')
825 825 permission = relationship('Permission', lazy='joined')
826 826
827 827 def __unicode__(self):
828 828 return u'<%s => %s >' % (self.user, self.permission)
829 829
830 830
831 831 class UserGroupRepoToPerm(Base, BaseModel):
832 832 __tablename__ = 'users_group_repo_to_perm'
833 833 __table_args__ = (
834 834 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
835 835 {'extend_existing': True, 'mysql_engine': 'InnoDB',
836 836 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
837 837 )
838 838 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
839 839 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
840 840 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
841 841 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
842 842
843 843 users_group = relationship('UserGroup')
844 844 permission = relationship('Permission')
845 845 repository = relationship('Repository')
846 846
847 847 def __unicode__(self):
848 848 return u'<UserGroupRepoToPerm:%s => %s >' % (self.users_group, self.repository)
849 849
850 850
851 851 class UserGroupUserGroupToPerm(Base, BaseModel):
852 852 __tablename__ = 'user_group_user_group_to_perm'
853 853 __table_args__ = (
854 854 UniqueConstraint('target_user_group_id', 'user_group_id', 'permission_id'),
855 855 CheckConstraint('target_user_group_id != user_group_id'),
856 856 {'extend_existing': True, 'mysql_engine': 'InnoDB',
857 857 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
858 858 )
859 859 user_group_user_group_to_perm_id = Column("user_group_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
860 860 target_user_group_id = Column("target_user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
861 861 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
862 862 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
863 863
864 864 target_user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
865 865 user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
866 866 permission = relationship('Permission')
867 867
868 868 def __unicode__(self):
869 869 return u'<UserGroupUserGroup:%s => %s >' % (self.target_user_group, self.user_group)
870 870
871 871
872 872 class UserGroupToPerm(Base, BaseModel):
873 873 __tablename__ = 'users_group_to_perm'
874 874 __table_args__ = (
875 875 UniqueConstraint('users_group_id', 'permission_id',),
876 876 {'extend_existing': True, 'mysql_engine': 'InnoDB',
877 877 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
878 878 )
879 879 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
880 880 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
881 881 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
882 882
883 883 users_group = relationship('UserGroup')
884 884 permission = relationship('Permission')
885 885
886 886
887 887 class UserRepoGroupToPerm(Base, BaseModel):
888 888 __tablename__ = 'user_repo_group_to_perm'
889 889 __table_args__ = (
890 890 UniqueConstraint('user_id', 'group_id', 'permission_id'),
891 891 {'extend_existing': True, 'mysql_engine': 'InnoDB',
892 892 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
893 893 )
894 894
895 895 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
896 896 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
897 897 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
898 898 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
899 899
900 900 user = relationship('User')
901 901 group = relationship('RepoGroup')
902 902 permission = relationship('Permission')
903 903
904 904
905 905 class UserGroupRepoGroupToPerm(Base, BaseModel):
906 906 __tablename__ = 'users_group_repo_group_to_perm'
907 907 __table_args__ = (
908 908 UniqueConstraint('users_group_id', 'group_id'),
909 909 {'extend_existing': True, 'mysql_engine': 'InnoDB',
910 910 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
911 911 )
912 912
913 913 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
914 914 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
915 915 group_id = Column("group_id", Integer(), ForeignKey('groups.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
918 918 users_group = relationship('UserGroup')
919 919 permission = relationship('Permission')
920 920 group = relationship('RepoGroup')
921 921
922 922
923 923 class Statistics(Base, BaseModel):
924 924 __tablename__ = 'statistics'
925 925 __table_args__ = (
926 926 UniqueConstraint('repository_id'),
927 927 {'extend_existing': True, 'mysql_engine': 'InnoDB',
928 928 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
929 929 )
930 930 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
931 931 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
932 932 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
933 933 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
934 934 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
935 935 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
936 936
937 937 repository = relationship('Repository', single_parent=True)
938 938
939 939
940 940 class UserFollowing(Base, BaseModel):
941 941 __tablename__ = 'user_followings'
942 942 __table_args__ = (
943 943 UniqueConstraint('user_id', 'follows_repository_id'),
944 944 UniqueConstraint('user_id', 'follows_user_id'),
945 945 {'extend_existing': True, 'mysql_engine': 'InnoDB',
946 946 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
947 947 )
948 948
949 949 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
950 950 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
951 951 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
952 952 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
953 953 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
954 954
955 955 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
956 956
957 957 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
958 958 follows_repository = relationship('Repository', order_by='Repository.repo_name')
959 959
960 960
961 961 class CacheInvalidation(Base, BaseModel):
962 962 __tablename__ = 'cache_invalidation'
963 963 __table_args__ = (
964 964 UniqueConstraint('cache_key'),
965 965 Index('key_idx', 'cache_key'),
966 966 {'extend_existing': True, 'mysql_engine': 'InnoDB',
967 967 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
968 968 )
969 969 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
970 970 cache_key = Column("cache_key", String(255), nullable=True, unique=None, default=None)
971 971 cache_args = Column("cache_args", String(255), nullable=True, unique=None, default=None)
972 972 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
973 973
974 974 def __init__(self, cache_key, cache_args=''):
975 975 self.cache_key = cache_key
976 976 self.cache_args = cache_args
977 977 self.cache_active = False
978 978
979 979
980 980 class ChangesetComment(Base, BaseModel):
981 981 __tablename__ = 'changeset_comments'
982 982 __table_args__ = (
983 983 Index('cc_revision_idx', 'revision'),
984 984 {'extend_existing': True, 'mysql_engine': 'InnoDB',
985 985 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
986 986 )
987 987 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
988 988 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
989 989 revision = Column('revision', String(40), nullable=True)
990 990 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
991 991 line_no = Column('line_no', Unicode(10), nullable=True)
992 992 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
993 993 f_path = Column('f_path', Unicode(1000), nullable=True)
994 994 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
995 995 text = Column('text', UnicodeText().with_variant(UnicodeText(25000), 'mysql'), nullable=False)
996 996 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
997 997 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
998 998
999 999 author = relationship('User', lazy='joined')
1000 1000 repo = relationship('Repository')
1001 1001 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1002 1002 pull_request = relationship('PullRequest', lazy='joined')
1003 1003
1004 1004
1005 1005 class ChangesetStatus(Base, BaseModel):
1006 1006 __tablename__ = 'changeset_statuses'
1007 1007 __table_args__ = (
1008 1008 Index('cs_revision_idx', 'revision'),
1009 1009 Index('cs_version_idx', 'version'),
1010 1010 UniqueConstraint('repo_id', 'revision', 'version'),
1011 1011 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1012 1012 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1013 1013 )
1014 1014 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1015 1015 STATUS_APPROVED = 'approved'
1016 1016 STATUS_REJECTED = 'rejected'
1017 1017 STATUS_UNDER_REVIEW = 'under_review'
1018 1018
1019 1019 STATUSES = [
1020 1020 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1021 1021 (STATUS_APPROVED, _("Approved")),
1022 1022 (STATUS_REJECTED, _("Rejected")),
1023 1023 (STATUS_UNDER_REVIEW, _("Under Review")),
1024 1024 ]
1025 1025
1026 1026 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1027 1027 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1028 1028 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1029 1029 revision = Column('revision', String(40), nullable=False)
1030 1030 status = Column('status', String(128), nullable=False, default=DEFAULT)
1031 1031 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1032 1032 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1033 1033 version = Column('version', Integer(), nullable=False, default=0)
1034 1034 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1035 1035
1036 1036 author = relationship('User', lazy='joined')
1037 1037 repo = relationship('Repository')
1038 1038 comment = relationship('ChangesetComment', lazy='joined')
1039 1039 pull_request = relationship('PullRequest', lazy='joined')
1040 1040
1041 1041
1042 1042
1043 1043 class PullRequest(Base, BaseModel):
1044 1044 __tablename__ = 'pull_requests'
1045 1045 __table_args__ = (
1046 1046 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1047 1047 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1048 1048 )
1049 1049
1050 1050 STATUS_NEW = u'new'
1051 1051 STATUS_OPEN = u'open'
1052 1052 STATUS_CLOSED = u'closed'
1053 1053
1054 1054 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1055 1055 title = Column('title', Unicode(256), nullable=True)
1056 1056 description = Column('description', UnicodeText().with_variant(UnicodeText(10240), 'mysql'), nullable=True)
1057 1057 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1058 1058 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1059 1059 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1060 1060 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1061 1061 _revisions = Column('revisions', UnicodeText().with_variant(UnicodeText(20500), 'mysql'))
1062 1062 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1063 1063 org_ref = Column('org_ref', Unicode(256), nullable=False)
1064 1064 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1065 1065 other_ref = Column('other_ref', Unicode(256), nullable=False)
1066 1066
1067 1067 author = relationship('User', lazy='joined')
1068 1068 reviewers = relationship('PullRequestReviewers',
1069 1069 cascade="all, delete, delete-orphan")
1070 1070 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1071 1071 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1072 1072 statuses = relationship('ChangesetStatus')
1073 1073 comments = relationship('ChangesetComment',
1074 1074 cascade="all, delete, delete-orphan")
1075 1075
1076 1076
1077 1077 class PullRequestReviewers(Base, BaseModel):
1078 1078 __tablename__ = 'pull_request_reviewers'
1079 1079 __table_args__ = (
1080 1080 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1081 1081 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1082 1082 )
1083 1083
1084 1084 def __init__(self, user=None, pull_request=None):
1085 1085 self.user = user
1086 1086 self.pull_request = pull_request
1087 1087
1088 1088 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1089 1089 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1090 1090 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1091 1091
1092 1092 user = relationship('User')
1093 1093 pull_request = relationship('PullRequest')
1094 1094
1095 1095
1096 1096 class Notification(Base, BaseModel):
1097 1097 __tablename__ = 'notifications'
1098 1098 __table_args__ = (
1099 1099 Index('notification_type_idx', 'type'),
1100 1100 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1101 1101 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1102 1102 )
1103 1103
1104 1104 TYPE_CHANGESET_COMMENT = u'cs_comment'
1105 1105 TYPE_MESSAGE = u'message'
1106 1106 TYPE_MENTION = u'mention'
1107 1107 TYPE_REGISTRATION = u'registration'
1108 1108 TYPE_PULL_REQUEST = u'pull_request'
1109 1109 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1110 1110
1111 1111 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1112 1112 subject = Column('subject', Unicode(512), nullable=True)
1113 1113 body = Column('body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True)
1114 1114 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1115 1115 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1116 1116 type_ = Column('type', Unicode(256))
1117 1117
1118 1118 created_by_user = relationship('User')
1119 1119 notifications_to_users = relationship('UserNotification', lazy='joined',
1120 1120 cascade="all, delete, delete-orphan")
1121 1121
1122 1122
1123 1123 class UserNotification(Base, BaseModel):
1124 1124 __tablename__ = 'user_to_notification'
1125 1125 __table_args__ = (
1126 1126 UniqueConstraint('user_id', 'notification_id'),
1127 1127 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1128 1128 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1129 1129 )
1130 1130 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1131 1131 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1132 1132 read = Column('read', Boolean, default=False)
1133 1133 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1134 1134
1135 1135 user = relationship('User', lazy="joined")
1136 1136 notification = relationship('Notification', lazy="joined",
1137 1137 order_by=lambda: Notification.created_on.desc(),)
1138 1138
1139 1139
1140 1140 class Gist(Base, BaseModel):
1141 1141 __tablename__ = 'gists'
1142 1142 __table_args__ = (
1143 1143 Index('g_gist_access_id_idx', 'gist_access_id'),
1144 1144 Index('g_created_on_idx', 'created_on'),
1145 1145 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1146 1146 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1147 1147 )
1148 1148 GIST_PUBLIC = u'public'
1149 1149 GIST_PRIVATE = u'private'
1150 1150
1151 1151 gist_id = Column('gist_id', Integer(), primary_key=True)
1152 1152 gist_access_id = Column('gist_access_id', Unicode(250))
1153 1153 gist_description = Column('gist_description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
1154 1154 gist_owner = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=True)
1155 1155 gist_expires = Column('gist_expires', Float(53), nullable=False)
1156 1156 gist_type = Column('gist_type', Unicode(128), nullable=False)
1157 1157 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1158 1158 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1159 1159
1160 1160 owner = relationship('User')
1161 1161
1162 1162
1163 1163 class DbMigrateVersion(Base, BaseModel):
1164 1164 __tablename__ = 'db_migrate_version'
1165 1165 __table_args__ = (
1166 1166 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1167 1167 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1168 1168 )
1169 1169 repository_id = Column('repository_id', String(250), primary_key=True)
1170 1170 repository_path = Column('repository_path', Text)
1171 1171 version = Column('version', Integer)
@@ -1,1172 +1,1172 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import os
22 22 import time
23 23 import logging
24 24 import datetime
25 25 import traceback
26 26 import hashlib
27 27 import collections
28 28 import functools
29 29
30 30 from sqlalchemy import *
31 31 from sqlalchemy.ext.hybrid import hybrid_property
32 32 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
33 33 from sqlalchemy.exc import DatabaseError
34 34 from beaker.cache import cache_region, region_invalidate
35 35 from webob.exc import HTTPNotFound
36 36
37 from pylons.i18n.translation import lazy_ugettext as _
37 from rhodecode.translation import _
38 38
39 39 from rhodecode.lib.vcs import get_backend
40 40 from rhodecode.lib.vcs.utils.helpers import get_scm
41 41 from rhodecode.lib.vcs.exceptions import VCSError
42 42 from zope.cachedescriptors.property import Lazy as LazyProperty
43 43 from rhodecode.lib.vcs.backends.base import EmptyCommit
44 44
45 45 from rhodecode.lib.utils2 import str2bool, safe_str, get_commit_safe, \
46 46 safe_unicode, remove_prefix, time_to_datetime, aslist, Optional, safe_int
47 47 from rhodecode.lib.ext_json import json
48 48 from rhodecode.lib.caching_query import FromCache
49 49
50 50 from rhodecode.model.meta import Base, Session
51 51
52 52 URL_SEP = '/'
53 53 log = logging.getLogger(__name__)
54 54
55 55 #==============================================================================
56 56 # BASE CLASSES
57 57 #==============================================================================
58 58
59 59 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
60 60
61 61
62 62 class BaseModel(object):
63 63 """
64 64 Base Model for all classes
65 65 """
66 66
67 67 @classmethod
68 68 def _get_keys(cls):
69 69 """return column names for this model """
70 70 return class_mapper(cls).c.keys()
71 71
72 72 def get_dict(self):
73 73 """
74 74 return dict with keys and values corresponding
75 75 to this model data """
76 76
77 77 d = {}
78 78 for k in self._get_keys():
79 79 d[k] = getattr(self, k)
80 80
81 81 # also use __json__() if present to get additional fields
82 82 _json_attr = getattr(self, '__json__', None)
83 83 if _json_attr:
84 84 # update with attributes from __json__
85 85 if callable(_json_attr):
86 86 _json_attr = _json_attr()
87 87 for k, val in _json_attr.iteritems():
88 88 d[k] = val
89 89 return d
90 90
91 91 def get_appstruct(self):
92 92 """return list with keys and values tupples corresponding
93 93 to this model data """
94 94
95 95 l = []
96 96 for k in self._get_keys():
97 97 l.append((k, getattr(self, k),))
98 98 return l
99 99
100 100 def populate_obj(self, populate_dict):
101 101 """populate model with data from given populate_dict"""
102 102
103 103 for k in self._get_keys():
104 104 if k in populate_dict:
105 105 setattr(self, k, populate_dict[k])
106 106
107 107 @classmethod
108 108 def query(cls):
109 109 return Session().query(cls)
110 110
111 111 @classmethod
112 112 def get(cls, id_):
113 113 if id_:
114 114 return cls.query().get(id_)
115 115
116 116 @classmethod
117 117 def get_or_404(cls, id_):
118 118 try:
119 119 id_ = int(id_)
120 120 except (TypeError, ValueError):
121 121 raise HTTPNotFound
122 122
123 123 res = cls.query().get(id_)
124 124 if not res:
125 125 raise HTTPNotFound
126 126 return res
127 127
128 128 @classmethod
129 129 def getAll(cls):
130 130 # deprecated and left for backward compatibility
131 131 return cls.get_all()
132 132
133 133 @classmethod
134 134 def get_all(cls):
135 135 return cls.query().all()
136 136
137 137 @classmethod
138 138 def delete(cls, id_):
139 139 obj = cls.query().get(id_)
140 140 Session().delete(obj)
141 141
142 142 def __repr__(self):
143 143 if hasattr(self, '__unicode__'):
144 144 # python repr needs to return str
145 145 try:
146 146 return safe_str(self.__unicode__())
147 147 except UnicodeDecodeError:
148 148 pass
149 149 return '<DB:%s>' % (self.__class__.__name__)
150 150
151 151
152 152 class RhodeCodeSetting(Base, BaseModel):
153 153 SETTINGS_TYPES = {
154 154 'str': safe_str,
155 155 'int': safe_int,
156 156 'unicode': safe_unicode,
157 157 'bool': str2bool,
158 158 'list': functools.partial(aslist, sep=',')
159 159 }
160 160 __tablename__ = 'rhodecode_settings'
161 161 __table_args__ = (
162 162 UniqueConstraint('app_settings_name'),
163 163 {'extend_existing': True, 'mysql_engine': 'InnoDB',
164 164 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
165 165 )
166 166 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
167 167 app_settings_name = Column("app_settings_name", String(255), nullable=True, unique=None, default=None)
168 168 _app_settings_value = Column("app_settings_value", String(4096), nullable=True, unique=None, default=None)
169 169 _app_settings_type = Column("app_settings_type", String(255), nullable=True, unique=None, default=None)
170 170
171 171 def __init__(self, key='', val='', type='unicode'):
172 172 self.app_settings_name = key
173 173 self.app_settings_value = val
174 174 self.app_settings_type = type
175 175
176 176 @validates('_app_settings_value')
177 177 def validate_settings_value(self, key, val):
178 178 assert type(val) == unicode
179 179 return val
180 180
181 181 @hybrid_property
182 182 def app_settings_value(self):
183 183 v = self._app_settings_value
184 184 _type = self.app_settings_type
185 185 converter = self.SETTINGS_TYPES.get(_type) or self.SETTINGS_TYPES['unicode']
186 186 return converter(v)
187 187
188 188 @app_settings_value.setter
189 189 def app_settings_value(self, val):
190 190 """
191 191 Setter that will always make sure we use unicode in app_settings_value
192 192
193 193 :param val:
194 194 """
195 195 self._app_settings_value = safe_unicode(val)
196 196
197 197 @hybrid_property
198 198 def app_settings_type(self):
199 199 return self._app_settings_type
200 200
201 201 @app_settings_type.setter
202 202 def app_settings_type(self, val):
203 203 if val not in self.SETTINGS_TYPES:
204 204 raise Exception('type must be one of %s got %s'
205 205 % (self.SETTINGS_TYPES.keys(), val))
206 206 self._app_settings_type = val
207 207
208 208 def __unicode__(self):
209 209 return u"<%s('%s:%s[%s]')>" % (
210 210 self.__class__.__name__,
211 211 self.app_settings_name, self.app_settings_value, self.app_settings_type
212 212 )
213 213
214 214
215 215 class RhodeCodeUi(Base, BaseModel):
216 216 __tablename__ = 'rhodecode_ui'
217 217 __table_args__ = (
218 218 UniqueConstraint('ui_key'),
219 219 {'extend_existing': True, 'mysql_engine': 'InnoDB',
220 220 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
221 221 )
222 222
223 223 HOOK_REPO_SIZE = 'changegroup.repo_size'
224 224 HOOK_PUSH = 'changegroup.push_logger'
225 225 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
226 226 HOOK_PULL = 'outgoing.pull_logger'
227 227 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
228 228
229 229 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
230 230 ui_section = Column("ui_section", String(255), nullable=True, unique=None, default=None)
231 231 ui_key = Column("ui_key", String(255), nullable=True, unique=None, default=None)
232 232 ui_value = Column("ui_value", String(255), nullable=True, unique=None, default=None)
233 233 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
234 234
235 235
236 236
237 237 class User(Base, BaseModel):
238 238 __tablename__ = 'users'
239 239 __table_args__ = (
240 240 UniqueConstraint('username'), UniqueConstraint('email'),
241 241 Index('u_username_idx', 'username'),
242 242 Index('u_email_idx', 'email'),
243 243 {'extend_existing': True, 'mysql_engine': 'InnoDB',
244 244 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
245 245 )
246 246 DEFAULT_USER = 'default'
247 247
248 248 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
249 249 username = Column("username", String(255), nullable=True, unique=None, default=None)
250 250 password = Column("password", String(255), nullable=True, unique=None, default=None)
251 251 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
252 252 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
253 253 name = Column("firstname", String(255), nullable=True, unique=None, default=None)
254 254 lastname = Column("lastname", String(255), nullable=True, unique=None, default=None)
255 255 _email = Column("email", String(255), nullable=True, unique=None, default=None)
256 256 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
257 257 extern_type = Column("extern_type", String(255), nullable=True, unique=None, default=None)
258 258 extern_name = Column("extern_name", String(255), nullable=True, unique=None, default=None)
259 259 api_key = Column("api_key", String(255), nullable=True, unique=None, default=None)
260 260 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
261 261 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
262 262
263 263 user_log = relationship('UserLog')
264 264 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
265 265
266 266 repositories = relationship('Repository')
267 267 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
268 268 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
269 269
270 270 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
271 271 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
272 272
273 273 group_member = relationship('UserGroupMember', cascade='all')
274 274
275 275 notifications = relationship('UserNotification', cascade='all')
276 276 # notifications assigned to this user
277 277 user_created_notifications = relationship('Notification', cascade='all')
278 278 # comments created by this user
279 279 user_comments = relationship('ChangesetComment', cascade='all')
280 280 user_emails = relationship('UserEmailMap', cascade='all')
281 281
282 282 @hybrid_property
283 283 def email(self):
284 284 return self._email
285 285
286 286 @email.setter
287 287 def email(self, val):
288 288 self._email = val.lower() if val else None
289 289
290 290 @property
291 291 def firstname(self):
292 292 # alias for future
293 293 return self.name
294 294
295 295 @property
296 296 def username_and_name(self):
297 297 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
298 298
299 299 @property
300 300 def full_name(self):
301 301 return '%s %s' % (self.firstname, self.lastname)
302 302
303 303 @property
304 304 def full_contact(self):
305 305 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
306 306
307 307 @property
308 308 def short_contact(self):
309 309 return '%s %s' % (self.firstname, self.lastname)
310 310
311 311 @property
312 312 def is_admin(self):
313 313 return self.admin
314 314
315 315 @classmethod
316 316 def get_by_username(cls, username, case_insensitive=False, cache=False):
317 317 if case_insensitive:
318 318 q = cls.query().filter(cls.username.ilike(username))
319 319 else:
320 320 q = cls.query().filter(cls.username == username)
321 321
322 322 if cache:
323 323 q = q.options(FromCache(
324 324 "sql_cache_short",
325 325 "get_user_%s" % _hash_key(username)
326 326 )
327 327 )
328 328 return q.scalar()
329 329
330 330 @classmethod
331 331 def get_by_auth_token(cls, auth_token, cache=False):
332 332 q = cls.query().filter(cls.api_key == auth_token)
333 333
334 334 if cache:
335 335 q = q.options(FromCache("sql_cache_short",
336 336 "get_auth_token_%s" % auth_token))
337 337 return q.scalar()
338 338
339 339 @classmethod
340 340 def get_by_email(cls, email, case_insensitive=False, cache=False):
341 341 if case_insensitive:
342 342 q = cls.query().filter(cls.email.ilike(email))
343 343 else:
344 344 q = cls.query().filter(cls.email == email)
345 345
346 346 if cache:
347 347 q = q.options(FromCache("sql_cache_short",
348 348 "get_email_key_%s" % email))
349 349
350 350 ret = q.scalar()
351 351 if ret is None:
352 352 q = UserEmailMap.query()
353 353 # try fetching in alternate email map
354 354 if case_insensitive:
355 355 q = q.filter(UserEmailMap.email.ilike(email))
356 356 else:
357 357 q = q.filter(UserEmailMap.email == email)
358 358 q = q.options(joinedload(UserEmailMap.user))
359 359 if cache:
360 360 q = q.options(FromCache("sql_cache_short",
361 361 "get_email_map_key_%s" % email))
362 362 ret = getattr(q.scalar(), 'user', None)
363 363
364 364 return ret
365 365
366 366 @classmethod
367 367 def get_first_admin(cls):
368 368 user = User.query().filter(User.admin == True).first()
369 369 if user is None:
370 370 raise Exception('Missing administrative account!')
371 371 return user
372 372
373 373 @classmethod
374 374 def get_default_user(cls, cache=False):
375 375 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
376 376 if user is None:
377 377 raise Exception('Missing default account!')
378 378 return user
379 379
380 380
381 381
382 382
383 383 class UserEmailMap(Base, BaseModel):
384 384 __tablename__ = 'user_email_map'
385 385 __table_args__ = (
386 386 Index('uem_email_idx', 'email'),
387 387 UniqueConstraint('email'),
388 388 {'extend_existing': True, 'mysql_engine': 'InnoDB',
389 389 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
390 390 )
391 391 __mapper_args__ = {}
392 392
393 393 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
394 394 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
395 395 _email = Column("email", String(255), nullable=True, unique=False, default=None)
396 396 user = relationship('User', lazy='joined')
397 397
398 398 @validates('_email')
399 399 def validate_email(self, key, email):
400 400 # check if this email is not main one
401 401 main_email = Session().query(User).filter(User.email == email).scalar()
402 402 if main_email is not None:
403 403 raise AttributeError('email %s is present is user table' % email)
404 404 return email
405 405
406 406 @hybrid_property
407 407 def email(self):
408 408 return self._email
409 409
410 410 @email.setter
411 411 def email(self, val):
412 412 self._email = val.lower() if val else None
413 413
414 414
415 415 class UserIpMap(Base, BaseModel):
416 416 __tablename__ = 'user_ip_map'
417 417 __table_args__ = (
418 418 UniqueConstraint('user_id', 'ip_addr'),
419 419 {'extend_existing': True, 'mysql_engine': 'InnoDB',
420 420 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
421 421 )
422 422 __mapper_args__ = {}
423 423
424 424 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
425 425 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
426 426 ip_addr = Column("ip_addr", String(255), nullable=True, unique=False, default=None)
427 427 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
428 428 user = relationship('User', lazy='joined')
429 429
430 430
431 431 class UserLog(Base, BaseModel):
432 432 __tablename__ = 'user_logs'
433 433 __table_args__ = (
434 434 {'extend_existing': True, 'mysql_engine': 'InnoDB',
435 435 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
436 436 )
437 437 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
438 438 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
439 439 username = Column("username", String(255), nullable=True, unique=None, default=None)
440 440 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
441 441 repository_name = Column("repository_name", String(255), nullable=True, unique=None, default=None)
442 442 user_ip = Column("user_ip", String(255), nullable=True, unique=None, default=None)
443 443 action = Column("action", String(1200000), nullable=True, unique=None, default=None)
444 444 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
445 445
446 446 def __unicode__(self):
447 447 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
448 448 self.repository_name,
449 449 self.action)
450 450
451 451 user = relationship('User')
452 452 repository = relationship('Repository', cascade='')
453 453
454 454
455 455 class UserGroup(Base, BaseModel):
456 456 __tablename__ = 'users_groups'
457 457 __table_args__ = (
458 458 {'extend_existing': True, 'mysql_engine': 'InnoDB',
459 459 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
460 460 )
461 461
462 462 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
463 463 users_group_name = Column("users_group_name", String(255), nullable=False, unique=True, default=None)
464 464 user_group_description = Column("user_group_description", String(10000), nullable=True, unique=None, default=None)
465 465 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
466 466 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
467 467 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
468 468 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
469 469
470 470 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
471 471 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
472 472 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
473 473 users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
474 474 user_user_group_to_perm = relationship('UserUserGroupToPerm', cascade='all')
475 475 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm ', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
476 476
477 477 user = relationship('User')
478 478
479 479 def __unicode__(self):
480 480 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
481 481 self.users_group_id,
482 482 self.users_group_name)
483 483
484 484 @classmethod
485 485 def get_by_group_name(cls, group_name, cache=False,
486 486 case_insensitive=False):
487 487 if case_insensitive:
488 488 q = cls.query().filter(cls.users_group_name.ilike(group_name))
489 489 else:
490 490 q = cls.query().filter(cls.users_group_name == group_name)
491 491 if cache:
492 492 q = q.options(FromCache(
493 493 "sql_cache_short",
494 494 "get_user_%s" % _hash_key(group_name)
495 495 )
496 496 )
497 497 return q.scalar()
498 498
499 499 @classmethod
500 500 def get(cls, user_group_id, cache=False):
501 501 user_group = cls.query()
502 502 if cache:
503 503 user_group = user_group.options(FromCache("sql_cache_short",
504 504 "get_users_group_%s" % user_group_id))
505 505 return user_group.get(user_group_id)
506 506
507 507
508 508 class UserGroupMember(Base, BaseModel):
509 509 __tablename__ = 'users_groups_members'
510 510 __table_args__ = (
511 511 {'extend_existing': True, 'mysql_engine': 'InnoDB',
512 512 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
513 513 )
514 514
515 515 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
516 516 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
517 517 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
518 518
519 519 user = relationship('User', lazy='joined')
520 520 users_group = relationship('UserGroup')
521 521
522 522 def __init__(self, gr_id='', u_id=''):
523 523 self.users_group_id = gr_id
524 524 self.user_id = u_id
525 525
526 526
527 527 class RepositoryField(Base, BaseModel):
528 528 __tablename__ = 'repositories_fields'
529 529 __table_args__ = (
530 530 UniqueConstraint('repository_id', 'field_key'), # no-multi field
531 531 {'extend_existing': True, 'mysql_engine': 'InnoDB',
532 532 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
533 533 )
534 534 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
535 535
536 536 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
537 537 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
538 538 field_key = Column("field_key", String(250))
539 539 field_label = Column("field_label", String(1024), nullable=False)
540 540 field_value = Column("field_value", String(10000), nullable=False)
541 541 field_desc = Column("field_desc", String(1024), nullable=False)
542 542 field_type = Column("field_type", String(256), nullable=False, unique=None)
543 543 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
544 544
545 545 repository = relationship('Repository')
546 546
547 547 @classmethod
548 548 def get_by_key_name(cls, key, repo):
549 549 row = cls.query()\
550 550 .filter(cls.repository == repo)\
551 551 .filter(cls.field_key == key).scalar()
552 552 return row
553 553
554 554
555 555 class Repository(Base, BaseModel):
556 556 __tablename__ = 'repositories'
557 557 __table_args__ = (
558 558 UniqueConstraint('repo_name'),
559 559 Index('r_repo_name_idx', 'repo_name'),
560 560 {'extend_existing': True, 'mysql_engine': 'InnoDB',
561 561 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
562 562 )
563 563
564 564 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
565 565 repo_name = Column("repo_name", String(255), nullable=False, unique=True, default=None)
566 566 clone_uri = Column("clone_uri", String(255), nullable=True, unique=False, default=None)
567 567 repo_type = Column("repo_type", String(255), nullable=False, unique=False, default=None)
568 568 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
569 569 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
570 570 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
571 571 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
572 572 description = Column("description", String(10000), nullable=True, unique=None, default=None)
573 573 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
574 574 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
575 575 landing_rev = Column("landing_revision", String(255), nullable=False, unique=False, default=None)
576 576 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
577 577 _locked = Column("locked", String(255), nullable=True, unique=False, default=None)
578 578 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
579 579
580 580 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
581 581 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
582 582
583 583 user = relationship('User')
584 584 fork = relationship('Repository', remote_side=repo_id)
585 585 group = relationship('RepoGroup')
586 586 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
587 587 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
588 588 stats = relationship('Statistics', cascade='all', uselist=False)
589 589
590 590 followers = relationship('UserFollowing',
591 591 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
592 592 cascade='all')
593 593 extra_fields = relationship('RepositoryField',
594 594 cascade="all, delete, delete-orphan")
595 595
596 596 logs = relationship('UserLog')
597 597 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
598 598
599 599 pull_requests_org = relationship('PullRequest',
600 600 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
601 601 cascade="all, delete, delete-orphan")
602 602
603 603 pull_requests_other = relationship('PullRequest',
604 604 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
605 605 cascade="all, delete, delete-orphan")
606 606
607 607 def __unicode__(self):
608 608 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
609 609 safe_unicode(self.repo_name))
610 610
611 611 @classmethod
612 612 def get_by_repo_name(cls, repo_name):
613 613 q = Session().query(cls).filter(cls.repo_name == repo_name)
614 614 q = q.options(joinedload(Repository.fork))\
615 615 .options(joinedload(Repository.user))\
616 616 .options(joinedload(Repository.group))
617 617 return q.scalar()
618 618
619 619
620 620 class RepoGroup(Base, BaseModel):
621 621 __tablename__ = 'groups'
622 622 __table_args__ = (
623 623 UniqueConstraint('group_name', 'group_parent_id'),
624 624 CheckConstraint('group_id != group_parent_id'),
625 625 {'extend_existing': True, 'mysql_engine': 'InnoDB',
626 626 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
627 627 )
628 628 __mapper_args__ = {'order_by': 'group_name'}
629 629
630 630 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
631 631 group_name = Column("group_name", String(255), nullable=False, unique=True, default=None)
632 632 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
633 633 group_description = Column("group_description", String(10000), nullable=True, unique=None, default=None)
634 634 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
635 635 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
636 636 #TODO: create this field in migrations
637 637 #created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
638 638
639 639 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
640 640 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
641 641 parent_group = relationship('RepoGroup', remote_side=group_id)
642 642 user = relationship('User')
643 643
644 644 def __init__(self, group_name='', parent_group=None):
645 645 self.group_name = group_name
646 646 self.parent_group = parent_group
647 647
648 648 def __unicode__(self):
649 649 return u"<%s('id:%s:%s')>" % (self.__class__.__name__, self.group_id,
650 650 self.group_name)
651 651
652 652 @classmethod
653 653 def url_sep(cls):
654 654 return URL_SEP
655 655
656 656 @classmethod
657 657 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
658 658 if case_insensitive:
659 659 gr = cls.query()\
660 660 .filter(cls.group_name.ilike(group_name))
661 661 else:
662 662 gr = cls.query()\
663 663 .filter(cls.group_name == group_name)
664 664 if cache:
665 665 gr = gr.options(FromCache(
666 666 "sql_cache_short",
667 667 "get_group_%s" % _hash_key(group_name)
668 668 )
669 669 )
670 670 return gr.scalar()
671 671
672 672
673 673 class Permission(Base, BaseModel):
674 674 __tablename__ = 'permissions'
675 675 __table_args__ = (
676 676 Index('p_perm_name_idx', 'permission_name'),
677 677 {'extend_existing': True, 'mysql_engine': 'InnoDB',
678 678 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
679 679 )
680 680 PERMS = [
681 681 ('hg.admin', _('RhodeCode Administrator')),
682 682
683 683 ('repository.none', _('Repository no access')),
684 684 ('repository.read', _('Repository read access')),
685 685 ('repository.write', _('Repository write access')),
686 686 ('repository.admin', _('Repository admin access')),
687 687
688 688 ('group.none', _('Repository group no access')),
689 689 ('group.read', _('Repository group read access')),
690 690 ('group.write', _('Repository group write access')),
691 691 ('group.admin', _('Repository group admin access')),
692 692
693 693 ('usergroup.none', _('User group no access')),
694 694 ('usergroup.read', _('User group read access')),
695 695 ('usergroup.write', _('User group write access')),
696 696 ('usergroup.admin', _('User group admin access')),
697 697
698 698 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
699 699 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
700 700
701 701 ('hg.usergroup.create.false', _('User Group creation disabled')),
702 702 ('hg.usergroup.create.true', _('User Group creation enabled')),
703 703
704 704 ('hg.create.none', _('Repository creation disabled')),
705 705 ('hg.create.repository', _('Repository creation enabled')),
706 706
707 707 ('hg.fork.none', _('Repository forking disabled')),
708 708 ('hg.fork.repository', _('Repository forking enabled')),
709 709
710 710 ('hg.register.none', _('Registration disabled')),
711 711 ('hg.register.manual_activate', _('User Registration with manual account activation')),
712 712 ('hg.register.auto_activate', _('User Registration with automatic account activation')),
713 713
714 714 ('hg.extern_activate.manual', _('Manual activation of external account')),
715 715 ('hg.extern_activate.auto', _('Automatic activation of external account')),
716 716
717 717 ]
718 718
719 719 #definition of system default permissions for DEFAULT user
720 720 DEFAULT_USER_PERMISSIONS = [
721 721 'repository.read',
722 722 'group.read',
723 723 'usergroup.read',
724 724 'hg.create.repository',
725 725 'hg.fork.repository',
726 726 'hg.register.manual_activate',
727 727 'hg.extern_activate.auto',
728 728 ]
729 729
730 730 # defines which permissions are more important higher the more important
731 731 # Weight defines which permissions are more important.
732 732 # The higher number the more important.
733 733 PERM_WEIGHTS = {
734 734 'repository.none': 0,
735 735 'repository.read': 1,
736 736 'repository.write': 3,
737 737 'repository.admin': 4,
738 738
739 739 'group.none': 0,
740 740 'group.read': 1,
741 741 'group.write': 3,
742 742 'group.admin': 4,
743 743
744 744 'usergroup.none': 0,
745 745 'usergroup.read': 1,
746 746 'usergroup.write': 3,
747 747 'usergroup.admin': 4,
748 748 'hg.repogroup.create.false': 0,
749 749 'hg.repogroup.create.true': 1,
750 750
751 751 'hg.usergroup.create.false': 0,
752 752 'hg.usergroup.create.true': 1,
753 753
754 754 'hg.fork.none': 0,
755 755 'hg.fork.repository': 1,
756 756 'hg.create.none': 0,
757 757 'hg.create.repository': 1
758 758 }
759 759
760 760 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
761 761 permission_name = Column("permission_name", String(255), nullable=True, unique=None, default=None)
762 762 permission_longname = Column("permission_longname", String(255), nullable=True, unique=None, default=None)
763 763
764 764 def __unicode__(self):
765 765 return u"<%s('%s:%s')>" % (
766 766 self.__class__.__name__, self.permission_id, self.permission_name
767 767 )
768 768
769 769 @classmethod
770 770 def get_by_key(cls, key):
771 771 return cls.query().filter(cls.permission_name == key).scalar()
772 772
773 773
774 774 class UserRepoToPerm(Base, BaseModel):
775 775 __tablename__ = 'repo_to_perm'
776 776 __table_args__ = (
777 777 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
778 778 {'extend_existing': True, 'mysql_engine': 'InnoDB',
779 779 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
780 780 )
781 781 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
782 782 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
783 783 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
784 784 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
785 785
786 786 user = relationship('User')
787 787 repository = relationship('Repository')
788 788 permission = relationship('Permission')
789 789
790 790 def __unicode__(self):
791 791 return u'<%s => %s >' % (self.user, self.repository)
792 792
793 793
794 794 class UserUserGroupToPerm(Base, BaseModel):
795 795 __tablename__ = 'user_user_group_to_perm'
796 796 __table_args__ = (
797 797 UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
798 798 {'extend_existing': True, 'mysql_engine': 'InnoDB',
799 799 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
800 800 )
801 801 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
802 802 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
803 803 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
804 804 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
805 805
806 806 user = relationship('User')
807 807 user_group = relationship('UserGroup')
808 808 permission = relationship('Permission')
809 809
810 810 def __unicode__(self):
811 811 return u'<%s => %s >' % (self.user, self.user_group)
812 812
813 813
814 814 class UserToPerm(Base, BaseModel):
815 815 __tablename__ = 'user_to_perm'
816 816 __table_args__ = (
817 817 UniqueConstraint('user_id', 'permission_id'),
818 818 {'extend_existing': True, 'mysql_engine': 'InnoDB',
819 819 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
820 820 )
821 821 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
822 822 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
823 823 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
824 824
825 825 user = relationship('User')
826 826 permission = relationship('Permission', lazy='joined')
827 827
828 828 def __unicode__(self):
829 829 return u'<%s => %s >' % (self.user, self.permission)
830 830
831 831
832 832 class UserGroupRepoToPerm(Base, BaseModel):
833 833 __tablename__ = 'users_group_repo_to_perm'
834 834 __table_args__ = (
835 835 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
836 836 {'extend_existing': True, 'mysql_engine': 'InnoDB',
837 837 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
838 838 )
839 839 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
840 840 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
841 841 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
842 842 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
843 843
844 844 users_group = relationship('UserGroup')
845 845 permission = relationship('Permission')
846 846 repository = relationship('Repository')
847 847
848 848 def __unicode__(self):
849 849 return u'<UserGroupRepoToPerm:%s => %s >' % (self.users_group, self.repository)
850 850
851 851
852 852 class UserGroupUserGroupToPerm(Base, BaseModel):
853 853 __tablename__ = 'user_group_user_group_to_perm'
854 854 __table_args__ = (
855 855 UniqueConstraint('target_user_group_id', 'user_group_id', 'permission_id'),
856 856 CheckConstraint('target_user_group_id != user_group_id'),
857 857 {'extend_existing': True, 'mysql_engine': 'InnoDB',
858 858 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
859 859 )
860 860 user_group_user_group_to_perm_id = Column("user_group_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
861 861 target_user_group_id = Column("target_user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
862 862 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
863 863 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
864 864
865 865 target_user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
866 866 user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
867 867 permission = relationship('Permission')
868 868
869 869 def __unicode__(self):
870 870 return u'<UserGroupUserGroup:%s => %s >' % (self.target_user_group, self.user_group)
871 871
872 872
873 873 class UserGroupToPerm(Base, BaseModel):
874 874 __tablename__ = 'users_group_to_perm'
875 875 __table_args__ = (
876 876 UniqueConstraint('users_group_id', 'permission_id',),
877 877 {'extend_existing': True, 'mysql_engine': 'InnoDB',
878 878 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
879 879 )
880 880 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
881 881 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
882 882 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
883 883
884 884 users_group = relationship('UserGroup')
885 885 permission = relationship('Permission')
886 886
887 887
888 888 class UserRepoGroupToPerm(Base, BaseModel):
889 889 __tablename__ = 'user_repo_group_to_perm'
890 890 __table_args__ = (
891 891 UniqueConstraint('user_id', 'group_id', 'permission_id'),
892 892 {'extend_existing': True, 'mysql_engine': 'InnoDB',
893 893 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
894 894 )
895 895
896 896 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
897 897 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
898 898 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
899 899 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
900 900
901 901 user = relationship('User')
902 902 group = relationship('RepoGroup')
903 903 permission = relationship('Permission')
904 904
905 905
906 906 class UserGroupRepoGroupToPerm(Base, BaseModel):
907 907 __tablename__ = 'users_group_repo_group_to_perm'
908 908 __table_args__ = (
909 909 UniqueConstraint('users_group_id', 'group_id'),
910 910 {'extend_existing': True, 'mysql_engine': 'InnoDB',
911 911 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
912 912 )
913 913
914 914 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
915 915 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
916 916 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
917 917 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
918 918
919 919 users_group = relationship('UserGroup')
920 920 permission = relationship('Permission')
921 921 group = relationship('RepoGroup')
922 922
923 923
924 924 class Statistics(Base, BaseModel):
925 925 __tablename__ = 'statistics'
926 926 __table_args__ = (
927 927 UniqueConstraint('repository_id'),
928 928 {'extend_existing': True, 'mysql_engine': 'InnoDB',
929 929 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
930 930 )
931 931 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
932 932 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
933 933 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
934 934 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
935 935 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
936 936 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
937 937
938 938 repository = relationship('Repository', single_parent=True)
939 939
940 940
941 941 class UserFollowing(Base, BaseModel):
942 942 __tablename__ = 'user_followings'
943 943 __table_args__ = (
944 944 UniqueConstraint('user_id', 'follows_repository_id'),
945 945 UniqueConstraint('user_id', 'follows_user_id'),
946 946 {'extend_existing': True, 'mysql_engine': 'InnoDB',
947 947 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
948 948 )
949 949
950 950 user_following_id = Column("user_following_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 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
953 953 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
954 954 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
955 955
956 956 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
957 957
958 958 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
959 959 follows_repository = relationship('Repository', order_by='Repository.repo_name')
960 960
961 961
962 962 class CacheInvalidation(Base, BaseModel):
963 963 __tablename__ = 'cache_invalidation'
964 964 __table_args__ = (
965 965 UniqueConstraint('cache_key'),
966 966 Index('key_idx', 'cache_key'),
967 967 {'extend_existing': True, 'mysql_engine': 'InnoDB',
968 968 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
969 969 )
970 970 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
971 971 cache_key = Column("cache_key", String(255), nullable=True, unique=None, default=None)
972 972 cache_args = Column("cache_args", String(255), nullable=True, unique=None, default=None)
973 973 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
974 974
975 975 def __init__(self, cache_key, cache_args=''):
976 976 self.cache_key = cache_key
977 977 self.cache_args = cache_args
978 978 self.cache_active = False
979 979
980 980
981 981 class ChangesetComment(Base, BaseModel):
982 982 __tablename__ = 'changeset_comments'
983 983 __table_args__ = (
984 984 Index('cc_revision_idx', 'revision'),
985 985 {'extend_existing': True, 'mysql_engine': 'InnoDB',
986 986 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
987 987 )
988 988 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
989 989 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
990 990 revision = Column('revision', String(40), nullable=True)
991 991 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
992 992 line_no = Column('line_no', Unicode(10), nullable=True)
993 993 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
994 994 f_path = Column('f_path', Unicode(1000), nullable=True)
995 995 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
996 996 text = Column('text', UnicodeText().with_variant(UnicodeText(25000), 'mysql'), nullable=False)
997 997 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
998 998 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
999 999
1000 1000 author = relationship('User', lazy='joined')
1001 1001 repo = relationship('Repository')
1002 1002 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1003 1003 pull_request = relationship('PullRequest', lazy='joined')
1004 1004
1005 1005
1006 1006 class ChangesetStatus(Base, BaseModel):
1007 1007 __tablename__ = 'changeset_statuses'
1008 1008 __table_args__ = (
1009 1009 Index('cs_revision_idx', 'revision'),
1010 1010 Index('cs_version_idx', 'version'),
1011 1011 UniqueConstraint('repo_id', 'revision', 'version'),
1012 1012 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1013 1013 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1014 1014 )
1015 1015 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1016 1016 STATUS_APPROVED = 'approved'
1017 1017 STATUS_REJECTED = 'rejected'
1018 1018 STATUS_UNDER_REVIEW = 'under_review'
1019 1019
1020 1020 STATUSES = [
1021 1021 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1022 1022 (STATUS_APPROVED, _("Approved")),
1023 1023 (STATUS_REJECTED, _("Rejected")),
1024 1024 (STATUS_UNDER_REVIEW, _("Under Review")),
1025 1025 ]
1026 1026
1027 1027 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1028 1028 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1029 1029 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1030 1030 revision = Column('revision', String(40), nullable=False)
1031 1031 status = Column('status', String(128), nullable=False, default=DEFAULT)
1032 1032 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1033 1033 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1034 1034 version = Column('version', Integer(), nullable=False, default=0)
1035 1035 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1036 1036
1037 1037 author = relationship('User', lazy='joined')
1038 1038 repo = relationship('Repository')
1039 1039 comment = relationship('ChangesetComment', lazy='joined')
1040 1040 pull_request = relationship('PullRequest', lazy='joined')
1041 1041
1042 1042
1043 1043
1044 1044 class PullRequest(Base, BaseModel):
1045 1045 __tablename__ = 'pull_requests'
1046 1046 __table_args__ = (
1047 1047 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1048 1048 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1049 1049 )
1050 1050
1051 1051 STATUS_NEW = u'new'
1052 1052 STATUS_OPEN = u'open'
1053 1053 STATUS_CLOSED = u'closed'
1054 1054
1055 1055 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1056 1056 title = Column('title', Unicode(256), nullable=True)
1057 1057 description = Column('description', UnicodeText().with_variant(UnicodeText(10240), 'mysql'), nullable=True)
1058 1058 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1059 1059 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1060 1060 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1061 1061 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1062 1062 _revisions = Column('revisions', UnicodeText().with_variant(UnicodeText(20500), 'mysql'))
1063 1063 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1064 1064 org_ref = Column('org_ref', Unicode(256), nullable=False)
1065 1065 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1066 1066 other_ref = Column('other_ref', Unicode(256), nullable=False)
1067 1067
1068 1068 author = relationship('User', lazy='joined')
1069 1069 reviewers = relationship('PullRequestReviewers',
1070 1070 cascade="all, delete, delete-orphan")
1071 1071 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1072 1072 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1073 1073 statuses = relationship('ChangesetStatus')
1074 1074 comments = relationship('ChangesetComment',
1075 1075 cascade="all, delete, delete-orphan")
1076 1076
1077 1077
1078 1078 class PullRequestReviewers(Base, BaseModel):
1079 1079 __tablename__ = 'pull_request_reviewers'
1080 1080 __table_args__ = (
1081 1081 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1082 1082 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1083 1083 )
1084 1084
1085 1085 def __init__(self, user=None, pull_request=None):
1086 1086 self.user = user
1087 1087 self.pull_request = pull_request
1088 1088
1089 1089 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1090 1090 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1091 1091 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1092 1092
1093 1093 user = relationship('User')
1094 1094 pull_request = relationship('PullRequest')
1095 1095
1096 1096
1097 1097 class Notification(Base, BaseModel):
1098 1098 __tablename__ = 'notifications'
1099 1099 __table_args__ = (
1100 1100 Index('notification_type_idx', 'type'),
1101 1101 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1102 1102 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1103 1103 )
1104 1104
1105 1105 TYPE_CHANGESET_COMMENT = u'cs_comment'
1106 1106 TYPE_MESSAGE = u'message'
1107 1107 TYPE_MENTION = u'mention'
1108 1108 TYPE_REGISTRATION = u'registration'
1109 1109 TYPE_PULL_REQUEST = u'pull_request'
1110 1110 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1111 1111
1112 1112 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1113 1113 subject = Column('subject', Unicode(512), nullable=True)
1114 1114 body = Column('body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True)
1115 1115 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1116 1116 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1117 1117 type_ = Column('type', Unicode(256))
1118 1118
1119 1119 created_by_user = relationship('User')
1120 1120 notifications_to_users = relationship('UserNotification', lazy='joined',
1121 1121 cascade="all, delete, delete-orphan")
1122 1122
1123 1123
1124 1124 class UserNotification(Base, BaseModel):
1125 1125 __tablename__ = 'user_to_notification'
1126 1126 __table_args__ = (
1127 1127 UniqueConstraint('user_id', 'notification_id'),
1128 1128 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1129 1129 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1130 1130 )
1131 1131 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1132 1132 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1133 1133 read = Column('read', Boolean, default=False)
1134 1134 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1135 1135
1136 1136 user = relationship('User', lazy="joined")
1137 1137 notification = relationship('Notification', lazy="joined",
1138 1138 order_by=lambda: Notification.created_on.desc(),)
1139 1139
1140 1140
1141 1141 class Gist(Base, BaseModel):
1142 1142 __tablename__ = 'gists'
1143 1143 __table_args__ = (
1144 1144 Index('g_gist_access_id_idx', 'gist_access_id'),
1145 1145 Index('g_created_on_idx', 'created_on'),
1146 1146 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1147 1147 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1148 1148 )
1149 1149 GIST_PUBLIC = u'public'
1150 1150 GIST_PRIVATE = u'private'
1151 1151
1152 1152 gist_id = Column('gist_id', Integer(), primary_key=True)
1153 1153 gist_access_id = Column('gist_access_id', Unicode(250))
1154 1154 gist_description = Column('gist_description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
1155 1155 gist_owner = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=True)
1156 1156 gist_expires = Column('gist_expires', Float(53), nullable=False)
1157 1157 gist_type = Column('gist_type', Unicode(128), nullable=False)
1158 1158 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1159 1159 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1160 1160
1161 1161 owner = relationship('User')
1162 1162
1163 1163
1164 1164 class DbMigrateVersion(Base, BaseModel):
1165 1165 __tablename__ = 'db_migrate_version'
1166 1166 __table_args__ = (
1167 1167 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1168 1168 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1169 1169 )
1170 1170 repository_id = Column('repository_id', String(250), primary_key=True)
1171 1171 repository_path = Column('repository_path', Text)
1172 1172 version = Column('version', Integer)
@@ -1,1189 +1,1189 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import os
22 22 import time
23 23 import logging
24 24 import datetime
25 25 import traceback
26 26 import hashlib
27 27 import collections
28 28 import functools
29 29
30 30 from sqlalchemy import *
31 31 from sqlalchemy.ext.hybrid import hybrid_property
32 32 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
33 33 from sqlalchemy.exc import DatabaseError
34 34 from beaker.cache import cache_region, region_invalidate
35 35 from webob.exc import HTTPNotFound
36 36
37 from pylons.i18n.translation import lazy_ugettext as _
37 from rhodecode.translation import _
38 38
39 39 from rhodecode.lib.vcs import get_backend
40 40 from rhodecode.lib.vcs.utils.helpers import get_scm
41 41 from rhodecode.lib.vcs.exceptions import VCSError
42 42 from zope.cachedescriptors.property import Lazy as LazyProperty
43 43 from rhodecode.lib.vcs.backends.base import EmptyCommit
44 44
45 45 from rhodecode.lib.utils2 import str2bool, safe_str, get_commit_safe, \
46 46 safe_unicode, remove_prefix, time_to_datetime, aslist, Optional, safe_int
47 47 from rhodecode.lib.ext_json import json
48 48 from rhodecode.lib.caching_query import FromCache
49 49
50 50 from rhodecode.model.meta import Base, Session
51 51
52 52 URL_SEP = '/'
53 53 log = logging.getLogger(__name__)
54 54
55 55 #==============================================================================
56 56 # BASE CLASSES
57 57 #==============================================================================
58 58
59 59 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
60 60
61 61
62 62 class BaseModel(object):
63 63 """
64 64 Base Model for all classes
65 65 """
66 66
67 67 @classmethod
68 68 def _get_keys(cls):
69 69 """return column names for this model """
70 70 return class_mapper(cls).c.keys()
71 71
72 72 def get_dict(self):
73 73 """
74 74 return dict with keys and values corresponding
75 75 to this model data """
76 76
77 77 d = {}
78 78 for k in self._get_keys():
79 79 d[k] = getattr(self, k)
80 80
81 81 # also use __json__() if present to get additional fields
82 82 _json_attr = getattr(self, '__json__', None)
83 83 if _json_attr:
84 84 # update with attributes from __json__
85 85 if callable(_json_attr):
86 86 _json_attr = _json_attr()
87 87 for k, val in _json_attr.iteritems():
88 88 d[k] = val
89 89 return d
90 90
91 91 def get_appstruct(self):
92 92 """return list with keys and values tupples corresponding
93 93 to this model data """
94 94
95 95 l = []
96 96 for k in self._get_keys():
97 97 l.append((k, getattr(self, k),))
98 98 return l
99 99
100 100 def populate_obj(self, populate_dict):
101 101 """populate model with data from given populate_dict"""
102 102
103 103 for k in self._get_keys():
104 104 if k in populate_dict:
105 105 setattr(self, k, populate_dict[k])
106 106
107 107 @classmethod
108 108 def query(cls):
109 109 return Session().query(cls)
110 110
111 111 @classmethod
112 112 def get(cls, id_):
113 113 if id_:
114 114 return cls.query().get(id_)
115 115
116 116 @classmethod
117 117 def get_or_404(cls, id_):
118 118 try:
119 119 id_ = int(id_)
120 120 except (TypeError, ValueError):
121 121 raise HTTPNotFound
122 122
123 123 res = cls.query().get(id_)
124 124 if not res:
125 125 raise HTTPNotFound
126 126 return res
127 127
128 128 @classmethod
129 129 def getAll(cls):
130 130 # deprecated and left for backward compatibility
131 131 return cls.get_all()
132 132
133 133 @classmethod
134 134 def get_all(cls):
135 135 return cls.query().all()
136 136
137 137 @classmethod
138 138 def delete(cls, id_):
139 139 obj = cls.query().get(id_)
140 140 Session().delete(obj)
141 141
142 142 def __repr__(self):
143 143 if hasattr(self, '__unicode__'):
144 144 # python repr needs to return str
145 145 try:
146 146 return safe_str(self.__unicode__())
147 147 except UnicodeDecodeError:
148 148 pass
149 149 return '<DB:%s>' % (self.__class__.__name__)
150 150
151 151
152 152 class RhodeCodeSetting(Base, BaseModel):
153 153 SETTINGS_TYPES = {
154 154 'str': safe_str,
155 155 'int': safe_int,
156 156 'unicode': safe_unicode,
157 157 'bool': str2bool,
158 158 'list': functools.partial(aslist, sep=',')
159 159 }
160 160 __tablename__ = 'rhodecode_settings'
161 161 __table_args__ = (
162 162 UniqueConstraint('app_settings_name'),
163 163 {'extend_existing': True, 'mysql_engine': 'InnoDB',
164 164 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
165 165 )
166 166 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
167 167 app_settings_name = Column("app_settings_name", String(255), nullable=True, unique=None, default=None)
168 168 _app_settings_value = Column("app_settings_value", String(4096), nullable=True, unique=None, default=None)
169 169 _app_settings_type = Column("app_settings_type", String(255), nullable=True, unique=None, default=None)
170 170
171 171 def __init__(self, key='', val='', type='unicode'):
172 172 self.app_settings_name = key
173 173 self.app_settings_value = val
174 174 self.app_settings_type = type
175 175
176 176 @validates('_app_settings_value')
177 177 def validate_settings_value(self, key, val):
178 178 assert type(val) == unicode
179 179 return val
180 180
181 181 @hybrid_property
182 182 def app_settings_value(self):
183 183 v = self._app_settings_value
184 184 _type = self.app_settings_type
185 185 converter = self.SETTINGS_TYPES.get(_type) or self.SETTINGS_TYPES['unicode']
186 186 return converter(v)
187 187
188 188 @app_settings_value.setter
189 189 def app_settings_value(self, val):
190 190 """
191 191 Setter that will always make sure we use unicode in app_settings_value
192 192
193 193 :param val:
194 194 """
195 195 self._app_settings_value = safe_unicode(val)
196 196
197 197 @hybrid_property
198 198 def app_settings_type(self):
199 199 return self._app_settings_type
200 200
201 201 @app_settings_type.setter
202 202 def app_settings_type(self, val):
203 203 if val not in self.SETTINGS_TYPES:
204 204 raise Exception('type must be one of %s got %s'
205 205 % (self.SETTINGS_TYPES.keys(), val))
206 206 self._app_settings_type = val
207 207
208 208 def __unicode__(self):
209 209 return u"<%s('%s:%s[%s]')>" % (
210 210 self.__class__.__name__,
211 211 self.app_settings_name, self.app_settings_value, self.app_settings_type
212 212 )
213 213
214 214
215 215 class RhodeCodeUi(Base, BaseModel):
216 216 __tablename__ = 'rhodecode_ui'
217 217 __table_args__ = (
218 218 UniqueConstraint('ui_key'),
219 219 {'extend_existing': True, 'mysql_engine': 'InnoDB',
220 220 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
221 221 )
222 222
223 223 HOOK_REPO_SIZE = 'changegroup.repo_size'
224 224 HOOK_PUSH = 'changegroup.push_logger'
225 225 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
226 226 HOOK_PULL = 'outgoing.pull_logger'
227 227 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
228 228
229 229 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
230 230 ui_section = Column("ui_section", String(255), nullable=True, unique=None, default=None)
231 231 ui_key = Column("ui_key", String(255), nullable=True, unique=None, default=None)
232 232 ui_value = Column("ui_value", String(255), nullable=True, unique=None, default=None)
233 233 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
234 234
235 235
236 236
237 237 class User(Base, BaseModel):
238 238 __tablename__ = 'users'
239 239 __table_args__ = (
240 240 UniqueConstraint('username'), UniqueConstraint('email'),
241 241 Index('u_username_idx', 'username'),
242 242 Index('u_email_idx', 'email'),
243 243 {'extend_existing': True, 'mysql_engine': 'InnoDB',
244 244 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
245 245 )
246 246 DEFAULT_USER = 'default'
247 247
248 248 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
249 249 username = Column("username", String(255), nullable=True, unique=None, default=None)
250 250 password = Column("password", String(255), nullable=True, unique=None, default=None)
251 251 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
252 252 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
253 253 name = Column("firstname", String(255), nullable=True, unique=None, default=None)
254 254 lastname = Column("lastname", String(255), nullable=True, unique=None, default=None)
255 255 _email = Column("email", String(255), nullable=True, unique=None, default=None)
256 256 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
257 257 extern_type = Column("extern_type", String(255), nullable=True, unique=None, default=None)
258 258 extern_name = Column("extern_name", String(255), nullable=True, unique=None, default=None)
259 259 api_key = Column("api_key", String(255), nullable=True, unique=None, default=None)
260 260 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
261 261 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
262 262
263 263 user_log = relationship('UserLog')
264 264 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
265 265
266 266 repositories = relationship('Repository')
267 267 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
268 268 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
269 269
270 270 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
271 271 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
272 272
273 273 group_member = relationship('UserGroupMember', cascade='all')
274 274
275 275 notifications = relationship('UserNotification', cascade='all')
276 276 # notifications assigned to this user
277 277 user_created_notifications = relationship('Notification', cascade='all')
278 278 # comments created by this user
279 279 user_comments = relationship('ChangesetComment', cascade='all')
280 280 user_emails = relationship('UserEmailMap', cascade='all')
281 281
282 282 @hybrid_property
283 283 def email(self):
284 284 return self._email
285 285
286 286 @email.setter
287 287 def email(self, val):
288 288 self._email = val.lower() if val else None
289 289
290 290 @property
291 291 def firstname(self):
292 292 # alias for future
293 293 return self.name
294 294
295 295 @property
296 296 def username_and_name(self):
297 297 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
298 298
299 299 @property
300 300 def full_name(self):
301 301 return '%s %s' % (self.firstname, self.lastname)
302 302
303 303 @property
304 304 def full_contact(self):
305 305 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
306 306
307 307 @property
308 308 def short_contact(self):
309 309 return '%s %s' % (self.firstname, self.lastname)
310 310
311 311 @property
312 312 def is_admin(self):
313 313 return self.admin
314 314
315 315 @classmethod
316 316 def get_by_username(cls, username, case_insensitive=False, cache=False):
317 317 if case_insensitive:
318 318 q = cls.query().filter(cls.username.ilike(username))
319 319 else:
320 320 q = cls.query().filter(cls.username == username)
321 321
322 322 if cache:
323 323 q = q.options(FromCache(
324 324 "sql_cache_short",
325 325 "get_user_%s" % _hash_key(username)
326 326 )
327 327 )
328 328 return q.scalar()
329 329
330 330 @classmethod
331 331 def get_by_auth_token(cls, auth_token, cache=False):
332 332 q = cls.query().filter(cls.api_key == auth_token)
333 333
334 334 if cache:
335 335 q = q.options(FromCache("sql_cache_short",
336 336 "get_auth_token_%s" % auth_token))
337 337 return q.scalar()
338 338
339 339 @classmethod
340 340 def get_by_email(cls, email, case_insensitive=False, cache=False):
341 341 if case_insensitive:
342 342 q = cls.query().filter(cls.email.ilike(email))
343 343 else:
344 344 q = cls.query().filter(cls.email == email)
345 345
346 346 if cache:
347 347 q = q.options(FromCache("sql_cache_short",
348 348 "get_email_key_%s" % email))
349 349
350 350 ret = q.scalar()
351 351 if ret is None:
352 352 q = UserEmailMap.query()
353 353 # try fetching in alternate email map
354 354 if case_insensitive:
355 355 q = q.filter(UserEmailMap.email.ilike(email))
356 356 else:
357 357 q = q.filter(UserEmailMap.email == email)
358 358 q = q.options(joinedload(UserEmailMap.user))
359 359 if cache:
360 360 q = q.options(FromCache("sql_cache_short",
361 361 "get_email_map_key_%s" % email))
362 362 ret = getattr(q.scalar(), 'user', None)
363 363
364 364 return ret
365 365
366 366 @classmethod
367 367 def get_first_admin(cls):
368 368 user = User.query().filter(User.admin == True).first()
369 369 if user is None:
370 370 raise Exception('Missing administrative account!')
371 371 return user
372 372
373 373 @classmethod
374 374 def get_default_user(cls, cache=False):
375 375 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
376 376 if user is None:
377 377 raise Exception('Missing default account!')
378 378 return user
379 379
380 380
381 381
382 382
383 383 class UserEmailMap(Base, BaseModel):
384 384 __tablename__ = 'user_email_map'
385 385 __table_args__ = (
386 386 Index('uem_email_idx', 'email'),
387 387 UniqueConstraint('email'),
388 388 {'extend_existing': True, 'mysql_engine': 'InnoDB',
389 389 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
390 390 )
391 391 __mapper_args__ = {}
392 392
393 393 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
394 394 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
395 395 _email = Column("email", String(255), nullable=True, unique=False, default=None)
396 396 user = relationship('User', lazy='joined')
397 397
398 398 @validates('_email')
399 399 def validate_email(self, key, email):
400 400 # check if this email is not main one
401 401 main_email = Session().query(User).filter(User.email == email).scalar()
402 402 if main_email is not None:
403 403 raise AttributeError('email %s is present is user table' % email)
404 404 return email
405 405
406 406 @hybrid_property
407 407 def email(self):
408 408 return self._email
409 409
410 410 @email.setter
411 411 def email(self, val):
412 412 self._email = val.lower() if val else None
413 413
414 414
415 415 class UserIpMap(Base, BaseModel):
416 416 __tablename__ = 'user_ip_map'
417 417 __table_args__ = (
418 418 UniqueConstraint('user_id', 'ip_addr'),
419 419 {'extend_existing': True, 'mysql_engine': 'InnoDB',
420 420 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
421 421 )
422 422 __mapper_args__ = {}
423 423
424 424 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
425 425 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
426 426 ip_addr = Column("ip_addr", String(255), nullable=True, unique=False, default=None)
427 427 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
428 428 user = relationship('User', lazy='joined')
429 429
430 430
431 431 class UserLog(Base, BaseModel):
432 432 __tablename__ = 'user_logs'
433 433 __table_args__ = (
434 434 {'extend_existing': True, 'mysql_engine': 'InnoDB',
435 435 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
436 436 )
437 437 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
438 438 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
439 439 username = Column("username", String(255), nullable=True, unique=None, default=None)
440 440 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
441 441 repository_name = Column("repository_name", String(255), nullable=True, unique=None, default=None)
442 442 user_ip = Column("user_ip", String(255), nullable=True, unique=None, default=None)
443 443 action = Column("action", String(1200000), nullable=True, unique=None, default=None)
444 444 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
445 445
446 446 def __unicode__(self):
447 447 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
448 448 self.repository_name,
449 449 self.action)
450 450
451 451 user = relationship('User')
452 452 repository = relationship('Repository', cascade='')
453 453
454 454
455 455 class UserGroup(Base, BaseModel):
456 456 __tablename__ = 'users_groups'
457 457 __table_args__ = (
458 458 {'extend_existing': True, 'mysql_engine': 'InnoDB',
459 459 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
460 460 )
461 461
462 462 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
463 463 users_group_name = Column("users_group_name", String(255), nullable=False, unique=True, default=None)
464 464 user_group_description = Column("user_group_description", String(10000), nullable=True, unique=None, default=None)
465 465 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
466 466 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
467 467 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
468 468 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
469 469
470 470 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
471 471 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
472 472 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
473 473 users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
474 474 user_user_group_to_perm = relationship('UserUserGroupToPerm', cascade='all')
475 475 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm ', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
476 476
477 477 user = relationship('User')
478 478
479 479 def __unicode__(self):
480 480 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
481 481 self.users_group_id,
482 482 self.users_group_name)
483 483
484 484 @classmethod
485 485 def get_by_group_name(cls, group_name, cache=False,
486 486 case_insensitive=False):
487 487 if case_insensitive:
488 488 q = cls.query().filter(cls.users_group_name.ilike(group_name))
489 489 else:
490 490 q = cls.query().filter(cls.users_group_name == group_name)
491 491 if cache:
492 492 q = q.options(FromCache(
493 493 "sql_cache_short",
494 494 "get_user_%s" % _hash_key(group_name)
495 495 )
496 496 )
497 497 return q.scalar()
498 498
499 499 @classmethod
500 500 def get(cls, user_group_id, cache=False):
501 501 user_group = cls.query()
502 502 if cache:
503 503 user_group = user_group.options(FromCache("sql_cache_short",
504 504 "get_users_group_%s" % user_group_id))
505 505 return user_group.get(user_group_id)
506 506
507 507
508 508 class UserGroupMember(Base, BaseModel):
509 509 __tablename__ = 'users_groups_members'
510 510 __table_args__ = (
511 511 {'extend_existing': True, 'mysql_engine': 'InnoDB',
512 512 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
513 513 )
514 514
515 515 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
516 516 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
517 517 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
518 518
519 519 user = relationship('User', lazy='joined')
520 520 users_group = relationship('UserGroup')
521 521
522 522 def __init__(self, gr_id='', u_id=''):
523 523 self.users_group_id = gr_id
524 524 self.user_id = u_id
525 525
526 526
527 527 class RepositoryField(Base, BaseModel):
528 528 __tablename__ = 'repositories_fields'
529 529 __table_args__ = (
530 530 UniqueConstraint('repository_id', 'field_key'), # no-multi field
531 531 {'extend_existing': True, 'mysql_engine': 'InnoDB',
532 532 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
533 533 )
534 534 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
535 535
536 536 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
537 537 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
538 538 field_key = Column("field_key", String(250))
539 539 field_label = Column("field_label", String(1024), nullable=False)
540 540 field_value = Column("field_value", String(10000), nullable=False)
541 541 field_desc = Column("field_desc", String(1024), nullable=False)
542 542 field_type = Column("field_type", String(256), nullable=False, unique=None)
543 543 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
544 544
545 545 repository = relationship('Repository')
546 546
547 547 @classmethod
548 548 def get_by_key_name(cls, key, repo):
549 549 row = cls.query()\
550 550 .filter(cls.repository == repo)\
551 551 .filter(cls.field_key == key).scalar()
552 552 return row
553 553
554 554
555 555 class Repository(Base, BaseModel):
556 556 __tablename__ = 'repositories'
557 557 __table_args__ = (
558 558 UniqueConstraint('repo_name'),
559 559 Index('r_repo_name_idx', 'repo_name'),
560 560 {'extend_existing': True, 'mysql_engine': 'InnoDB',
561 561 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
562 562 )
563 563
564 564 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
565 565 repo_name = Column("repo_name", String(255), nullable=False, unique=True, default=None)
566 566 clone_uri = Column("clone_uri", String(255), nullable=True, unique=False, default=None)
567 567 repo_type = Column("repo_type", String(255), nullable=False, unique=False, default=None)
568 568 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
569 569 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
570 570 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
571 571 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
572 572 description = Column("description", String(10000), nullable=True, unique=None, default=None)
573 573 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
574 574 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
575 575 _landing_revision = 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 #NOTE: marcink, DO NOT REMOVE THOSE
608 608 @hybrid_property
609 609 def landing_rev(self):
610 610 # always should return [rev_type, rev]
611 611 if self._landing_revision:
612 612 _rev_info = self._landing_revision.split(':')
613 613 if len(_rev_info) < 2:
614 614 _rev_info.insert(0, 'rev')
615 615 return [_rev_info[0], _rev_info[1]]
616 616 return [None, None]
617 617
618 618 @landing_rev.setter
619 619 def landing_rev(self, val):
620 620 if ':' not in val:
621 621 raise ValueError('value must be delimited with `:` and consist '
622 622 'of <rev_type>:<rev>, got %s instead' % val)
623 623 self._landing_revision = val
624 624
625 625 def __unicode__(self):
626 626 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
627 627 safe_unicode(self.repo_name))
628 628
629 629 @classmethod
630 630 def get_by_repo_name(cls, repo_name):
631 631 q = Session().query(cls).filter(cls.repo_name == repo_name)
632 632 q = q.options(joinedload(Repository.fork))\
633 633 .options(joinedload(Repository.user))\
634 634 .options(joinedload(Repository.group))
635 635 return q.scalar()
636 636
637 637
638 638 class RepoGroup(Base, BaseModel):
639 639 __tablename__ = 'groups'
640 640 __table_args__ = (
641 641 UniqueConstraint('group_name', 'group_parent_id'),
642 642 CheckConstraint('group_id != group_parent_id'),
643 643 {'extend_existing': True, 'mysql_engine': 'InnoDB',
644 644 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
645 645 )
646 646 __mapper_args__ = {'order_by': 'group_name'}
647 647
648 648 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
649 649 group_name = Column("group_name", String(255), nullable=False, unique=True, default=None)
650 650 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
651 651 group_description = Column("group_description", String(10000), nullable=True, unique=None, default=None)
652 652 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
653 653 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
654 654 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
655 655
656 656 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
657 657 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
658 658 parent_group = relationship('RepoGroup', remote_side=group_id)
659 659 user = relationship('User')
660 660
661 661 def __init__(self, group_name='', parent_group=None):
662 662 self.group_name = group_name
663 663 self.parent_group = parent_group
664 664
665 665 def __unicode__(self):
666 666 return u"<%s('id:%s:%s')>" % (self.__class__.__name__, self.group_id,
667 667 self.group_name)
668 668
669 669 @classmethod
670 670 def url_sep(cls):
671 671 return URL_SEP
672 672
673 673 @classmethod
674 674 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
675 675 if case_insensitive:
676 676 gr = cls.query()\
677 677 .filter(cls.group_name.ilike(group_name))
678 678 else:
679 679 gr = cls.query()\
680 680 .filter(cls.group_name == group_name)
681 681 if cache:
682 682 gr = gr.options(FromCache(
683 683 "sql_cache_short",
684 684 "get_group_%s" % _hash_key(group_name)
685 685 )
686 686 )
687 687 return gr.scalar()
688 688
689 689
690 690 class Permission(Base, BaseModel):
691 691 __tablename__ = 'permissions'
692 692 __table_args__ = (
693 693 Index('p_perm_name_idx', 'permission_name'),
694 694 {'extend_existing': True, 'mysql_engine': 'InnoDB',
695 695 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
696 696 )
697 697 PERMS = [
698 698 ('hg.admin', _('RhodeCode Administrator')),
699 699
700 700 ('repository.none', _('Repository no access')),
701 701 ('repository.read', _('Repository read access')),
702 702 ('repository.write', _('Repository write access')),
703 703 ('repository.admin', _('Repository admin access')),
704 704
705 705 ('group.none', _('Repository group no access')),
706 706 ('group.read', _('Repository group read access')),
707 707 ('group.write', _('Repository group write access')),
708 708 ('group.admin', _('Repository group admin access')),
709 709
710 710 ('usergroup.none', _('User group no access')),
711 711 ('usergroup.read', _('User group read access')),
712 712 ('usergroup.write', _('User group write access')),
713 713 ('usergroup.admin', _('User group admin access')),
714 714
715 715 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
716 716 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
717 717
718 718 ('hg.usergroup.create.false', _('User Group creation disabled')),
719 719 ('hg.usergroup.create.true', _('User Group creation enabled')),
720 720
721 721 ('hg.create.none', _('Repository creation disabled')),
722 722 ('hg.create.repository', _('Repository creation enabled')),
723 723
724 724 ('hg.fork.none', _('Repository forking disabled')),
725 725 ('hg.fork.repository', _('Repository forking enabled')),
726 726
727 727 ('hg.register.none', _('Registration disabled')),
728 728 ('hg.register.manual_activate', _('User Registration with manual account activation')),
729 729 ('hg.register.auto_activate', _('User Registration with automatic account activation')),
730 730
731 731 ('hg.extern_activate.manual', _('Manual activation of external account')),
732 732 ('hg.extern_activate.auto', _('Automatic activation of external account')),
733 733
734 734 ]
735 735
736 736 #definition of system default permissions for DEFAULT user
737 737 DEFAULT_USER_PERMISSIONS = [
738 738 'repository.read',
739 739 'group.read',
740 740 'usergroup.read',
741 741 'hg.create.repository',
742 742 'hg.fork.repository',
743 743 'hg.register.manual_activate',
744 744 'hg.extern_activate.auto',
745 745 ]
746 746
747 747 # defines which permissions are more important higher the more important
748 748 # Weight defines which permissions are more important.
749 749 # The higher number the more important.
750 750 PERM_WEIGHTS = {
751 751 'repository.none': 0,
752 752 'repository.read': 1,
753 753 'repository.write': 3,
754 754 'repository.admin': 4,
755 755
756 756 'group.none': 0,
757 757 'group.read': 1,
758 758 'group.write': 3,
759 759 'group.admin': 4,
760 760
761 761 'usergroup.none': 0,
762 762 'usergroup.read': 1,
763 763 'usergroup.write': 3,
764 764 'usergroup.admin': 4,
765 765 'hg.repogroup.create.false': 0,
766 766 'hg.repogroup.create.true': 1,
767 767
768 768 'hg.usergroup.create.false': 0,
769 769 'hg.usergroup.create.true': 1,
770 770
771 771 'hg.fork.none': 0,
772 772 'hg.fork.repository': 1,
773 773 'hg.create.none': 0,
774 774 'hg.create.repository': 1
775 775 }
776 776
777 777 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
778 778 permission_name = Column("permission_name", String(255), nullable=True, unique=None, default=None)
779 779 permission_longname = Column("permission_longname", String(255), nullable=True, unique=None, default=None)
780 780
781 781 def __unicode__(self):
782 782 return u"<%s('%s:%s')>" % (
783 783 self.__class__.__name__, self.permission_id, self.permission_name
784 784 )
785 785
786 786 @classmethod
787 787 def get_by_key(cls, key):
788 788 return cls.query().filter(cls.permission_name == key).scalar()
789 789
790 790
791 791 class UserRepoToPerm(Base, BaseModel):
792 792 __tablename__ = 'repo_to_perm'
793 793 __table_args__ = (
794 794 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
795 795 {'extend_existing': True, 'mysql_engine': 'InnoDB',
796 796 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
797 797 )
798 798 repo_to_perm_id = Column("repo_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 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
802 802
803 803 user = relationship('User')
804 804 repository = relationship('Repository')
805 805 permission = relationship('Permission')
806 806
807 807 def __unicode__(self):
808 808 return u'<%s => %s >' % (self.user, self.repository)
809 809
810 810
811 811 class UserUserGroupToPerm(Base, BaseModel):
812 812 __tablename__ = 'user_user_group_to_perm'
813 813 __table_args__ = (
814 814 UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
815 815 {'extend_existing': True, 'mysql_engine': 'InnoDB',
816 816 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
817 817 )
818 818 user_user_group_to_perm_id = Column("user_user_group_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 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
822 822
823 823 user = relationship('User')
824 824 user_group = relationship('UserGroup')
825 825 permission = relationship('Permission')
826 826
827 827 def __unicode__(self):
828 828 return u'<%s => %s >' % (self.user, self.user_group)
829 829
830 830
831 831 class UserToPerm(Base, BaseModel):
832 832 __tablename__ = 'user_to_perm'
833 833 __table_args__ = (
834 834 UniqueConstraint('user_id', 'permission_id'),
835 835 {'extend_existing': True, 'mysql_engine': 'InnoDB',
836 836 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
837 837 )
838 838 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
839 839 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
840 840 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
841 841
842 842 user = relationship('User')
843 843 permission = relationship('Permission', lazy='joined')
844 844
845 845 def __unicode__(self):
846 846 return u'<%s => %s >' % (self.user, self.permission)
847 847
848 848
849 849 class UserGroupRepoToPerm(Base, BaseModel):
850 850 __tablename__ = 'users_group_repo_to_perm'
851 851 __table_args__ = (
852 852 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
853 853 {'extend_existing': True, 'mysql_engine': 'InnoDB',
854 854 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
855 855 )
856 856 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
857 857 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
858 858 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
859 859 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
860 860
861 861 users_group = relationship('UserGroup')
862 862 permission = relationship('Permission')
863 863 repository = relationship('Repository')
864 864
865 865 def __unicode__(self):
866 866 return u'<UserGroupRepoToPerm:%s => %s >' % (self.users_group, self.repository)
867 867
868 868
869 869 class UserGroupUserGroupToPerm(Base, BaseModel):
870 870 __tablename__ = 'user_group_user_group_to_perm'
871 871 __table_args__ = (
872 872 UniqueConstraint('target_user_group_id', 'user_group_id', 'permission_id'),
873 873 CheckConstraint('target_user_group_id != user_group_id'),
874 874 {'extend_existing': True, 'mysql_engine': 'InnoDB',
875 875 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
876 876 )
877 877 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)
878 878 target_user_group_id = Column("target_user_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 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
881 881
882 882 target_user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
883 883 user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
884 884 permission = relationship('Permission')
885 885
886 886 def __unicode__(self):
887 887 return u'<UserGroupUserGroup:%s => %s >' % (self.target_user_group, self.user_group)
888 888
889 889
890 890 class UserGroupToPerm(Base, BaseModel):
891 891 __tablename__ = 'users_group_to_perm'
892 892 __table_args__ = (
893 893 UniqueConstraint('users_group_id', 'permission_id',),
894 894 {'extend_existing': True, 'mysql_engine': 'InnoDB',
895 895 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
896 896 )
897 897 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
898 898 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
899 899 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
900 900
901 901 users_group = relationship('UserGroup')
902 902 permission = relationship('Permission')
903 903
904 904
905 905 class UserRepoGroupToPerm(Base, BaseModel):
906 906 __tablename__ = 'user_repo_group_to_perm'
907 907 __table_args__ = (
908 908 UniqueConstraint('user_id', 'group_id', 'permission_id'),
909 909 {'extend_existing': True, 'mysql_engine': 'InnoDB',
910 910 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
911 911 )
912 912
913 913 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
914 914 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
915 915 group_id = Column("group_id", Integer(), ForeignKey('groups.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
918 918 user = relationship('User')
919 919 group = relationship('RepoGroup')
920 920 permission = relationship('Permission')
921 921
922 922
923 923 class UserGroupRepoGroupToPerm(Base, BaseModel):
924 924 __tablename__ = 'users_group_repo_group_to_perm'
925 925 __table_args__ = (
926 926 UniqueConstraint('users_group_id', 'group_id'),
927 927 {'extend_existing': True, 'mysql_engine': 'InnoDB',
928 928 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
929 929 )
930 930
931 931 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)
932 932 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
933 933 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
934 934 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
935 935
936 936 users_group = relationship('UserGroup')
937 937 permission = relationship('Permission')
938 938 group = relationship('RepoGroup')
939 939
940 940
941 941 class Statistics(Base, BaseModel):
942 942 __tablename__ = 'statistics'
943 943 __table_args__ = (
944 944 UniqueConstraint('repository_id'),
945 945 {'extend_existing': True, 'mysql_engine': 'InnoDB',
946 946 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
947 947 )
948 948 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
949 949 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
950 950 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
951 951 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
952 952 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
953 953 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
954 954
955 955 repository = relationship('Repository', single_parent=True)
956 956
957 957
958 958 class UserFollowing(Base, BaseModel):
959 959 __tablename__ = 'user_followings'
960 960 __table_args__ = (
961 961 UniqueConstraint('user_id', 'follows_repository_id'),
962 962 UniqueConstraint('user_id', 'follows_user_id'),
963 963 {'extend_existing': True, 'mysql_engine': 'InnoDB',
964 964 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
965 965 )
966 966
967 967 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
968 968 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
969 969 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
970 970 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
971 971 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
972 972
973 973 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
974 974
975 975 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
976 976 follows_repository = relationship('Repository', order_by='Repository.repo_name')
977 977
978 978
979 979 class CacheInvalidation(Base, BaseModel):
980 980 __tablename__ = 'cache_invalidation'
981 981 __table_args__ = (
982 982 UniqueConstraint('cache_key'),
983 983 Index('key_idx', 'cache_key'),
984 984 {'extend_existing': True, 'mysql_engine': 'InnoDB',
985 985 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
986 986 )
987 987 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
988 988 cache_key = Column("cache_key", String(255), nullable=True, unique=None, default=None)
989 989 cache_args = Column("cache_args", String(255), nullable=True, unique=None, default=None)
990 990 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
991 991
992 992 def __init__(self, cache_key, cache_args=''):
993 993 self.cache_key = cache_key
994 994 self.cache_args = cache_args
995 995 self.cache_active = False
996 996
997 997
998 998 class ChangesetComment(Base, BaseModel):
999 999 __tablename__ = 'changeset_comments'
1000 1000 __table_args__ = (
1001 1001 Index('cc_revision_idx', 'revision'),
1002 1002 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1003 1003 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1004 1004 )
1005 1005 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1006 1006 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1007 1007 revision = Column('revision', String(40), nullable=True)
1008 1008 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1009 1009 line_no = Column('line_no', Unicode(10), nullable=True)
1010 1010 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1011 1011 f_path = Column('f_path', Unicode(1000), nullable=True)
1012 1012 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1013 1013 text = Column('text', UnicodeText().with_variant(UnicodeText(25000), 'mysql'), nullable=False)
1014 1014 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1015 1015 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1016 1016
1017 1017 author = relationship('User', lazy='joined')
1018 1018 repo = relationship('Repository')
1019 1019 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1020 1020 pull_request = relationship('PullRequest', lazy='joined')
1021 1021
1022 1022
1023 1023 class ChangesetStatus(Base, BaseModel):
1024 1024 __tablename__ = 'changeset_statuses'
1025 1025 __table_args__ = (
1026 1026 Index('cs_revision_idx', 'revision'),
1027 1027 Index('cs_version_idx', 'version'),
1028 1028 UniqueConstraint('repo_id', 'revision', 'version'),
1029 1029 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1030 1030 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1031 1031 )
1032 1032 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1033 1033 STATUS_APPROVED = 'approved'
1034 1034 STATUS_REJECTED = 'rejected'
1035 1035 STATUS_UNDER_REVIEW = 'under_review'
1036 1036
1037 1037 STATUSES = [
1038 1038 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1039 1039 (STATUS_APPROVED, _("Approved")),
1040 1040 (STATUS_REJECTED, _("Rejected")),
1041 1041 (STATUS_UNDER_REVIEW, _("Under Review")),
1042 1042 ]
1043 1043
1044 1044 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1045 1045 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1046 1046 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1047 1047 revision = Column('revision', String(40), nullable=False)
1048 1048 status = Column('status', String(128), nullable=False, default=DEFAULT)
1049 1049 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1050 1050 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1051 1051 version = Column('version', Integer(), nullable=False, default=0)
1052 1052 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1053 1053
1054 1054 author = relationship('User', lazy='joined')
1055 1055 repo = relationship('Repository')
1056 1056 comment = relationship('ChangesetComment', lazy='joined')
1057 1057 pull_request = relationship('PullRequest', lazy='joined')
1058 1058
1059 1059
1060 1060
1061 1061 class PullRequest(Base, BaseModel):
1062 1062 __tablename__ = 'pull_requests'
1063 1063 __table_args__ = (
1064 1064 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1065 1065 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1066 1066 )
1067 1067
1068 1068 STATUS_NEW = u'new'
1069 1069 STATUS_OPEN = u'open'
1070 1070 STATUS_CLOSED = u'closed'
1071 1071
1072 1072 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1073 1073 title = Column('title', Unicode(256), nullable=True)
1074 1074 description = Column('description', UnicodeText().with_variant(UnicodeText(10240), 'mysql'), nullable=True)
1075 1075 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1076 1076 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1077 1077 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1078 1078 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1079 1079 _revisions = Column('revisions', UnicodeText().with_variant(UnicodeText(20500), 'mysql'))
1080 1080 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1081 1081 org_ref = Column('org_ref', Unicode(256), nullable=False)
1082 1082 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1083 1083 other_ref = Column('other_ref', Unicode(256), nullable=False)
1084 1084
1085 1085 author = relationship('User', lazy='joined')
1086 1086 reviewers = relationship('PullRequestReviewers',
1087 1087 cascade="all, delete, delete-orphan")
1088 1088 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1089 1089 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1090 1090 statuses = relationship('ChangesetStatus')
1091 1091 comments = relationship('ChangesetComment',
1092 1092 cascade="all, delete, delete-orphan")
1093 1093
1094 1094
1095 1095 class PullRequestReviewers(Base, BaseModel):
1096 1096 __tablename__ = 'pull_request_reviewers'
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 def __init__(self, user=None, pull_request=None):
1103 1103 self.user = user
1104 1104 self.pull_request = pull_request
1105 1105
1106 1106 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1107 1107 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1108 1108 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1109 1109
1110 1110 user = relationship('User')
1111 1111 pull_request = relationship('PullRequest')
1112 1112
1113 1113
1114 1114 class Notification(Base, BaseModel):
1115 1115 __tablename__ = 'notifications'
1116 1116 __table_args__ = (
1117 1117 Index('notification_type_idx', 'type'),
1118 1118 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1119 1119 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1120 1120 )
1121 1121
1122 1122 TYPE_CHANGESET_COMMENT = u'cs_comment'
1123 1123 TYPE_MESSAGE = u'message'
1124 1124 TYPE_MENTION = u'mention'
1125 1125 TYPE_REGISTRATION = u'registration'
1126 1126 TYPE_PULL_REQUEST = u'pull_request'
1127 1127 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1128 1128
1129 1129 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1130 1130 subject = Column('subject', Unicode(512), nullable=True)
1131 1131 body = Column('body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True)
1132 1132 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1133 1133 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1134 1134 type_ = Column('type', Unicode(256))
1135 1135
1136 1136 created_by_user = relationship('User')
1137 1137 notifications_to_users = relationship('UserNotification', lazy='joined',
1138 1138 cascade="all, delete, delete-orphan")
1139 1139
1140 1140
1141 1141 class UserNotification(Base, BaseModel):
1142 1142 __tablename__ = 'user_to_notification'
1143 1143 __table_args__ = (
1144 1144 UniqueConstraint('user_id', 'notification_id'),
1145 1145 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1146 1146 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1147 1147 )
1148 1148 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1149 1149 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1150 1150 read = Column('read', Boolean, default=False)
1151 1151 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1152 1152
1153 1153 user = relationship('User', lazy="joined")
1154 1154 notification = relationship('Notification', lazy="joined",
1155 1155 order_by=lambda: Notification.created_on.desc(),)
1156 1156
1157 1157
1158 1158 class Gist(Base, BaseModel):
1159 1159 __tablename__ = 'gists'
1160 1160 __table_args__ = (
1161 1161 Index('g_gist_access_id_idx', 'gist_access_id'),
1162 1162 Index('g_created_on_idx', 'created_on'),
1163 1163 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1164 1164 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1165 1165 )
1166 1166 GIST_PUBLIC = u'public'
1167 1167 GIST_PRIVATE = u'private'
1168 1168
1169 1169 gist_id = Column('gist_id', Integer(), primary_key=True)
1170 1170 gist_access_id = Column('gist_access_id', Unicode(250))
1171 1171 gist_description = Column('gist_description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
1172 1172 gist_owner = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=True)
1173 1173 gist_expires = Column('gist_expires', Float(53), nullable=False)
1174 1174 gist_type = Column('gist_type', Unicode(128), nullable=False)
1175 1175 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1176 1176 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1177 1177
1178 1178 owner = relationship('User')
1179 1179
1180 1180
1181 1181 class DbMigrateVersion(Base, BaseModel):
1182 1182 __tablename__ = 'db_migrate_version'
1183 1183 __table_args__ = (
1184 1184 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1185 1185 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1186 1186 )
1187 1187 repository_id = Column('repository_id', String(250), primary_key=True)
1188 1188 repository_path = Column('repository_path', Text)
1189 1189 version = Column('version', Integer)
@@ -1,1206 +1,1206 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import os
22 22 import time
23 23 import logging
24 24 import datetime
25 25 import traceback
26 26 import hashlib
27 27 import collections
28 28 import functools
29 29
30 30 from sqlalchemy import *
31 31 from sqlalchemy.ext.hybrid import hybrid_property
32 32 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
33 33 from sqlalchemy.exc import DatabaseError
34 34 from beaker.cache import cache_region, region_invalidate
35 35 from webob.exc import HTTPNotFound
36 36
37 from pylons.i18n.translation import lazy_ugettext as _
37 from rhodecode.translation import _
38 38
39 39 from rhodecode.lib.vcs import get_backend
40 40 from rhodecode.lib.vcs.utils.helpers import get_scm
41 41 from rhodecode.lib.vcs.exceptions import VCSError
42 42 from zope.cachedescriptors.property import Lazy as LazyProperty
43 43 from rhodecode.lib.vcs.backends.base import EmptyCommit
44 44
45 45 from rhodecode.lib.utils2 import str2bool, safe_str, get_commit_safe, \
46 46 safe_unicode, remove_prefix, time_to_datetime, aslist, Optional, safe_int
47 47 from rhodecode.lib.ext_json import json
48 48 from rhodecode.lib.caching_query import FromCache
49 49
50 50 from rhodecode.model.meta import Base, Session
51 51
52 52 URL_SEP = '/'
53 53 log = logging.getLogger(__name__)
54 54
55 55 #==============================================================================
56 56 # BASE CLASSES
57 57 #==============================================================================
58 58
59 59 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
60 60
61 61
62 62 class BaseModel(object):
63 63 """
64 64 Base Model for all classes
65 65 """
66 66
67 67 @classmethod
68 68 def _get_keys(cls):
69 69 """return column names for this model """
70 70 return class_mapper(cls).c.keys()
71 71
72 72 def get_dict(self):
73 73 """
74 74 return dict with keys and values corresponding
75 75 to this model data """
76 76
77 77 d = {}
78 78 for k in self._get_keys():
79 79 d[k] = getattr(self, k)
80 80
81 81 # also use __json__() if present to get additional fields
82 82 _json_attr = getattr(self, '__json__', None)
83 83 if _json_attr:
84 84 # update with attributes from __json__
85 85 if callable(_json_attr):
86 86 _json_attr = _json_attr()
87 87 for k, val in _json_attr.iteritems():
88 88 d[k] = val
89 89 return d
90 90
91 91 def get_appstruct(self):
92 92 """return list with keys and values tupples corresponding
93 93 to this model data """
94 94
95 95 l = []
96 96 for k in self._get_keys():
97 97 l.append((k, getattr(self, k),))
98 98 return l
99 99
100 100 def populate_obj(self, populate_dict):
101 101 """populate model with data from given populate_dict"""
102 102
103 103 for k in self._get_keys():
104 104 if k in populate_dict:
105 105 setattr(self, k, populate_dict[k])
106 106
107 107 @classmethod
108 108 def query(cls):
109 109 return Session().query(cls)
110 110
111 111 @classmethod
112 112 def get(cls, id_):
113 113 if id_:
114 114 return cls.query().get(id_)
115 115
116 116 @classmethod
117 117 def get_or_404(cls, id_):
118 118 try:
119 119 id_ = int(id_)
120 120 except (TypeError, ValueError):
121 121 raise HTTPNotFound
122 122
123 123 res = cls.query().get(id_)
124 124 if not res:
125 125 raise HTTPNotFound
126 126 return res
127 127
128 128 @classmethod
129 129 def getAll(cls):
130 130 # deprecated and left for backward compatibility
131 131 return cls.get_all()
132 132
133 133 @classmethod
134 134 def get_all(cls):
135 135 return cls.query().all()
136 136
137 137 @classmethod
138 138 def delete(cls, id_):
139 139 obj = cls.query().get(id_)
140 140 Session().delete(obj)
141 141
142 142 def __repr__(self):
143 143 if hasattr(self, '__unicode__'):
144 144 # python repr needs to return str
145 145 try:
146 146 return safe_str(self.__unicode__())
147 147 except UnicodeDecodeError:
148 148 pass
149 149 return '<DB:%s>' % (self.__class__.__name__)
150 150
151 151
152 152 class RhodeCodeSetting(Base, BaseModel):
153 153 __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_unicode,
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) == unicode
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_unicode(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
239 239
240 240 class User(Base, BaseModel):
241 241 __tablename__ = 'users'
242 242 __table_args__ = (
243 243 UniqueConstraint('username'), UniqueConstraint('email'),
244 244 Index('u_username_idx', 'username'),
245 245 Index('u_email_idx', 'email'),
246 246 {'extend_existing': True, 'mysql_engine': 'InnoDB',
247 247 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
248 248 )
249 249 DEFAULT_USER = 'default'
250 250 DEFAULT_GRAVATAR_URL = 'https://secure.gravatar.com/avatar/{md5email}?d=identicon&s={size}'
251 251
252 252 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
253 253 username = Column("username", String(255), nullable=True, unique=None, default=None)
254 254 password = Column("password", String(255), nullable=True, unique=None, default=None)
255 255 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
256 256 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
257 257 name = Column("firstname", String(255), nullable=True, unique=None, default=None)
258 258 lastname = Column("lastname", String(255), nullable=True, unique=None, default=None)
259 259 _email = Column("email", String(255), nullable=True, unique=None, default=None)
260 260 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
261 261 extern_type = Column("extern_type", String(255), nullable=True, unique=None, default=None)
262 262 extern_name = Column("extern_name", String(255), nullable=True, unique=None, default=None)
263 263 api_key = Column("api_key", String(255), nullable=True, unique=None, default=None)
264 264 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
265 265 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
266 266
267 267 user_log = relationship('UserLog')
268 268 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
269 269
270 270 repositories = relationship('Repository')
271 271 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
272 272 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
273 273
274 274 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
275 275 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
276 276
277 277 group_member = relationship('UserGroupMember', cascade='all')
278 278
279 279 notifications = relationship('UserNotification', cascade='all')
280 280 # notifications assigned to this user
281 281 user_created_notifications = relationship('Notification', cascade='all')
282 282 # comments created by this user
283 283 user_comments = relationship('ChangesetComment', cascade='all')
284 284 user_emails = relationship('UserEmailMap', cascade='all')
285 285
286 286 @hybrid_property
287 287 def email(self):
288 288 return self._email
289 289
290 290 @email.setter
291 291 def email(self, val):
292 292 self._email = val.lower() if val else None
293 293
294 294 @property
295 295 def firstname(self):
296 296 # alias for future
297 297 return self.name
298 298
299 299 @property
300 300 def username_and_name(self):
301 301 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
302 302
303 303 @property
304 304 def full_name(self):
305 305 return '%s %s' % (self.firstname, self.lastname)
306 306
307 307 @property
308 308 def full_contact(self):
309 309 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
310 310
311 311 @property
312 312 def short_contact(self):
313 313 return '%s %s' % (self.firstname, self.lastname)
314 314
315 315 @property
316 316 def is_admin(self):
317 317 return self.admin
318 318
319 319 @classmethod
320 320 def get_by_username(cls, username, case_insensitive=False, cache=False):
321 321 if case_insensitive:
322 322 q = cls.query().filter(cls.username.ilike(username))
323 323 else:
324 324 q = cls.query().filter(cls.username == username)
325 325
326 326 if cache:
327 327 q = q.options(FromCache(
328 328 "sql_cache_short",
329 329 "get_user_%s" % _hash_key(username)
330 330 )
331 331 )
332 332 return q.scalar()
333 333
334 334 @classmethod
335 335 def get_by_auth_token(cls, auth_token, cache=False, fallback=True):
336 336 q = cls.query().filter(cls.api_key == auth_token)
337 337
338 338 if cache:
339 339 q = q.options(FromCache("sql_cache_short",
340 340 "get_auth_token_%s" % auth_token))
341 341 res = q.scalar()
342 342
343 343 if fallback and not res:
344 344 #fallback to additional keys
345 345 _res = UserApiKeys.query()\
346 346 .filter(UserApiKeys.api_key == auth_token)\
347 347 .filter(or_(UserApiKeys.expires == -1,
348 348 UserApiKeys.expires >= time.time()))\
349 349 .first()
350 350 if _res:
351 351 res = _res.user
352 352 return res
353 353
354 354 @classmethod
355 355 def get_by_email(cls, email, case_insensitive=False, cache=False):
356 356
357 357 if case_insensitive:
358 358 q = cls.query().filter(cls.email.ilike(email))
359 359 else:
360 360 q = cls.query().filter(cls.email == email)
361 361
362 362 if cache:
363 363 q = q.options(FromCache("sql_cache_short",
364 364 "get_email_key_%s" % email))
365 365
366 366 ret = q.scalar()
367 367 if ret is None:
368 368 q = UserEmailMap.query()
369 369 # try fetching in alternate email map
370 370 if case_insensitive:
371 371 q = q.filter(UserEmailMap.email.ilike(email))
372 372 else:
373 373 q = q.filter(UserEmailMap.email == email)
374 374 q = q.options(joinedload(UserEmailMap.user))
375 375 if cache:
376 376 q = q.options(FromCache("sql_cache_short",
377 377 "get_email_map_key_%s" % email))
378 378 ret = getattr(q.scalar(), 'user', None)
379 379
380 380 return ret
381 381
382 382 @classmethod
383 383 def get_first_admin(cls):
384 384 user = User.query().filter(User.admin == True).first()
385 385 if user is None:
386 386 raise Exception('Missing administrative account!')
387 387 return user
388 388
389 389 @classmethod
390 390 def get_default_user(cls, cache=False):
391 391 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
392 392 if user is None:
393 393 raise Exception('Missing default account!')
394 394 return user
395 395
396 396
397 397 class UserApiKeys(Base, BaseModel):
398 398 __tablename__ = 'user_api_keys'
399 399 __table_args__ = (
400 400 Index('uak_api_key_idx', 'api_key'),
401 401 UniqueConstraint('api_key'),
402 402 {'extend_existing': True, 'mysql_engine': 'InnoDB',
403 403 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
404 404 )
405 405 __mapper_args__ = {}
406 406
407 407 user_api_key_id = Column("user_api_key_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
408 408 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
409 409 api_key = Column("api_key", String(255), nullable=False, unique=True)
410 410 description = Column('description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
411 411 expires = Column('expires', Float(53), nullable=False)
412 412 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
413 413
414 414 user = relationship('User', lazy='joined')
415 415
416 416
417 417 class UserEmailMap(Base, BaseModel):
418 418 __tablename__ = 'user_email_map'
419 419 __table_args__ = (
420 420 Index('uem_email_idx', 'email'),
421 421 UniqueConstraint('email'),
422 422 {'extend_existing': True, 'mysql_engine': 'InnoDB',
423 423 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
424 424 )
425 425 __mapper_args__ = {}
426 426
427 427 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
428 428 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
429 429 _email = Column("email", String(255), nullable=True, unique=False, default=None)
430 430 user = relationship('User', lazy='joined')
431 431
432 432 @validates('_email')
433 433 def validate_email(self, key, email):
434 434 # check if this email is not main one
435 435 main_email = Session().query(User).filter(User.email == email).scalar()
436 436 if main_email is not None:
437 437 raise AttributeError('email %s is present is user table' % email)
438 438 return email
439 439
440 440 @hybrid_property
441 441 def email(self):
442 442 return self._email
443 443
444 444 @email.setter
445 445 def email(self, val):
446 446 self._email = val.lower() if val else None
447 447
448 448
449 449 class UserIpMap(Base, BaseModel):
450 450 __tablename__ = 'user_ip_map'
451 451 __table_args__ = (
452 452 UniqueConstraint('user_id', 'ip_addr'),
453 453 {'extend_existing': True, 'mysql_engine': 'InnoDB',
454 454 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
455 455 )
456 456 __mapper_args__ = {}
457 457
458 458 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
459 459 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
460 460 ip_addr = Column("ip_addr", String(255), nullable=True, unique=False, default=None)
461 461 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
462 462 user = relationship('User', lazy='joined')
463 463
464 464
465 465 class UserLog(Base, BaseModel):
466 466 __tablename__ = 'user_logs'
467 467 __table_args__ = (
468 468 {'extend_existing': True, 'mysql_engine': 'InnoDB',
469 469 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
470 470 )
471 471 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
472 472 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
473 473 username = Column("username", String(255), nullable=True, unique=None, default=None)
474 474 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
475 475 repository_name = Column("repository_name", String(255), nullable=True, unique=None, default=None)
476 476 user_ip = Column("user_ip", String(255), nullable=True, unique=None, default=None)
477 477 action = Column("action", String(1200000), nullable=True, unique=None, default=None)
478 478 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
479 479
480 480 def __unicode__(self):
481 481 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
482 482 self.repository_name,
483 483 self.action)
484 484
485 485 user = relationship('User')
486 486 repository = relationship('Repository', cascade='')
487 487
488 488
489 489 class UserGroup(Base, BaseModel):
490 490 __tablename__ = 'users_groups'
491 491 __table_args__ = (
492 492 {'extend_existing': True, 'mysql_engine': 'InnoDB',
493 493 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
494 494 )
495 495
496 496 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
497 497 users_group_name = Column("users_group_name", String(255), nullable=False, unique=True, default=None)
498 498 user_group_description = Column("user_group_description", String(10000), nullable=True, unique=None, default=None)
499 499 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
500 500 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
501 501 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
502 502 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
503 503
504 504 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
505 505 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
506 506 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
507 507 users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
508 508 user_user_group_to_perm = relationship('UserUserGroupToPerm', cascade='all')
509 509 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm ', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
510 510
511 511 user = relationship('User')
512 512
513 513 def __unicode__(self):
514 514 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
515 515 self.users_group_id,
516 516 self.users_group_name)
517 517
518 518 @classmethod
519 519 def get_by_group_name(cls, group_name, cache=False,
520 520 case_insensitive=False):
521 521 if case_insensitive:
522 522 q = cls.query().filter(cls.users_group_name.ilike(group_name))
523 523 else:
524 524 q = cls.query().filter(cls.users_group_name == group_name)
525 525 if cache:
526 526 q = q.options(FromCache(
527 527 "sql_cache_short",
528 528 "get_user_%s" % _hash_key(group_name)
529 529 )
530 530 )
531 531 return q.scalar()
532 532
533 533 @classmethod
534 534 def get(cls, user_group_id, cache=False):
535 535 user_group = cls.query()
536 536 if cache:
537 537 user_group = user_group.options(FromCache("sql_cache_short",
538 538 "get_users_group_%s" % user_group_id))
539 539 return user_group.get(user_group_id)
540 540
541 541
542 542 class UserGroupMember(Base, BaseModel):
543 543 __tablename__ = 'users_groups_members'
544 544 __table_args__ = (
545 545 {'extend_existing': True, 'mysql_engine': 'InnoDB',
546 546 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
547 547 )
548 548
549 549 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
550 550 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
551 551 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
552 552
553 553 user = relationship('User', lazy='joined')
554 554 users_group = relationship('UserGroup')
555 555
556 556 def __init__(self, gr_id='', u_id=''):
557 557 self.users_group_id = gr_id
558 558 self.user_id = u_id
559 559
560 560
561 561 class RepositoryField(Base, BaseModel):
562 562 __tablename__ = 'repositories_fields'
563 563 __table_args__ = (
564 564 UniqueConstraint('repository_id', 'field_key'), # no-multi field
565 565 {'extend_existing': True, 'mysql_engine': 'InnoDB',
566 566 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
567 567 )
568 568 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
569 569
570 570 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
571 571 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
572 572 field_key = Column("field_key", String(250))
573 573 field_label = Column("field_label", String(1024), nullable=False)
574 574 field_value = Column("field_value", String(10000), nullable=False)
575 575 field_desc = Column("field_desc", String(1024), nullable=False)
576 576 field_type = Column("field_type", String(256), nullable=False, unique=None)
577 577 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
578 578
579 579 repository = relationship('Repository')
580 580
581 581 @classmethod
582 582 def get_by_key_name(cls, key, repo):
583 583 row = cls.query()\
584 584 .filter(cls.repository == repo)\
585 585 .filter(cls.field_key == key).scalar()
586 586 return row
587 587
588 588
589 589 class Repository(Base, BaseModel):
590 590 __tablename__ = 'repositories'
591 591 __table_args__ = (
592 592 UniqueConstraint('repo_name'),
593 593 Index('r_repo_name_idx', 'repo_name'),
594 594 {'extend_existing': True, 'mysql_engine': 'InnoDB',
595 595 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
596 596 )
597 597 DEFAULT_CLONE_URI = '{scheme}://{user}@{netloc}/{repo}'
598 598
599 599 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
600 600 repo_name = Column("repo_name", String(255), nullable=False, unique=True, default=None)
601 601 clone_uri = Column("clone_uri", String(255), nullable=True, unique=False, default=None)
602 602 repo_type = Column("repo_type", String(255), nullable=False, unique=False, default=None)
603 603 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
604 604 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
605 605 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
606 606 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
607 607 description = Column("description", String(10000), nullable=True, unique=None, default=None)
608 608 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
609 609 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
610 610 _landing_revision = Column("landing_revision", String(255), nullable=False, unique=False, default=None)
611 611 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
612 612 _locked = Column("locked", String(255), nullable=True, unique=False, default=None)
613 613 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
614 614
615 615 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
616 616 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
617 617
618 618 user = relationship('User')
619 619 fork = relationship('Repository', remote_side=repo_id)
620 620 group = relationship('RepoGroup')
621 621 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
622 622 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
623 623 stats = relationship('Statistics', cascade='all', uselist=False)
624 624
625 625 followers = relationship('UserFollowing',
626 626 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
627 627 cascade='all')
628 628 extra_fields = relationship('RepositoryField',
629 629 cascade="all, delete, delete-orphan")
630 630
631 631 logs = relationship('UserLog')
632 632 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
633 633
634 634 pull_requests_org = relationship('PullRequest',
635 635 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
636 636 cascade="all, delete, delete-orphan")
637 637
638 638 pull_requests_other = relationship('PullRequest',
639 639 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
640 640 cascade="all, delete, delete-orphan")
641 641
642 642 def __unicode__(self):
643 643 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
644 644 safe_unicode(self.repo_name))
645 645
646 646 @classmethod
647 647 def get_by_repo_name(cls, repo_name):
648 648 q = Session().query(cls).filter(cls.repo_name == repo_name)
649 649 q = q.options(joinedload(Repository.fork))\
650 650 .options(joinedload(Repository.user))\
651 651 .options(joinedload(Repository.group))
652 652 return q.scalar()
653 653
654 654
655 655 class RepoGroup(Base, BaseModel):
656 656 __tablename__ = 'groups'
657 657 __table_args__ = (
658 658 UniqueConstraint('group_name', 'group_parent_id'),
659 659 CheckConstraint('group_id != group_parent_id'),
660 660 {'extend_existing': True, 'mysql_engine': 'InnoDB',
661 661 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
662 662 )
663 663 __mapper_args__ = {'order_by': 'group_name'}
664 664
665 665 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
666 666 group_name = Column("group_name", String(255), nullable=False, unique=True, default=None)
667 667 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
668 668 group_description = Column("group_description", String(10000), nullable=True, unique=None, default=None)
669 669 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
670 670 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
671 671 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
672 672
673 673 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
674 674 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
675 675 parent_group = relationship('RepoGroup', remote_side=group_id)
676 676 user = relationship('User')
677 677
678 678 def __init__(self, group_name='', parent_group=None):
679 679 self.group_name = group_name
680 680 self.parent_group = parent_group
681 681
682 682 def __unicode__(self):
683 683 return u"<%s('id:%s:%s')>" % (self.__class__.__name__, self.group_id,
684 684 self.group_name)
685 685
686 686 @classmethod
687 687 def url_sep(cls):
688 688 return URL_SEP
689 689
690 690 @classmethod
691 691 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
692 692 if case_insensitive:
693 693 gr = cls.query()\
694 694 .filter(cls.group_name.ilike(group_name))
695 695 else:
696 696 gr = cls.query()\
697 697 .filter(cls.group_name == group_name)
698 698 if cache:
699 699 gr = gr.options(FromCache(
700 700 "sql_cache_short",
701 701 "get_group_%s" % _hash_key(group_name)
702 702 )
703 703 )
704 704 return gr.scalar()
705 705
706 706
707 707 class Permission(Base, BaseModel):
708 708 __tablename__ = 'permissions'
709 709 __table_args__ = (
710 710 Index('p_perm_name_idx', 'permission_name'),
711 711 {'extend_existing': True, 'mysql_engine': 'InnoDB',
712 712 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
713 713 )
714 714 PERMS = [
715 715 ('hg.admin', _('RhodeCode Administrator')),
716 716
717 717 ('repository.none', _('Repository no access')),
718 718 ('repository.read', _('Repository read access')),
719 719 ('repository.write', _('Repository write access')),
720 720 ('repository.admin', _('Repository admin access')),
721 721
722 722 ('group.none', _('Repository group no access')),
723 723 ('group.read', _('Repository group read access')),
724 724 ('group.write', _('Repository group write access')),
725 725 ('group.admin', _('Repository group admin access')),
726 726
727 727 ('usergroup.none', _('User group no access')),
728 728 ('usergroup.read', _('User group read access')),
729 729 ('usergroup.write', _('User group write access')),
730 730 ('usergroup.admin', _('User group admin access')),
731 731
732 732 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
733 733 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
734 734
735 735 ('hg.usergroup.create.false', _('User Group creation disabled')),
736 736 ('hg.usergroup.create.true', _('User Group creation enabled')),
737 737
738 738 ('hg.create.none', _('Repository creation disabled')),
739 739 ('hg.create.repository', _('Repository creation enabled')),
740 740
741 741 ('hg.fork.none', _('Repository forking disabled')),
742 742 ('hg.fork.repository', _('Repository forking enabled')),
743 743
744 744 ('hg.register.none', _('Registration disabled')),
745 745 ('hg.register.manual_activate', _('User Registration with manual account activation')),
746 746 ('hg.register.auto_activate', _('User Registration with automatic account activation')),
747 747
748 748 ('hg.extern_activate.manual', _('Manual activation of external account')),
749 749 ('hg.extern_activate.auto', _('Automatic activation of external account')),
750 750
751 751 ]
752 752
753 753 #definition of system default permissions for DEFAULT user
754 754 DEFAULT_USER_PERMISSIONS = [
755 755 'repository.read',
756 756 'group.read',
757 757 'usergroup.read',
758 758 'hg.create.repository',
759 759 'hg.fork.repository',
760 760 'hg.register.manual_activate',
761 761 'hg.extern_activate.auto',
762 762 ]
763 763
764 764 # defines which permissions are more important higher the more important
765 765 # Weight defines which permissions are more important.
766 766 # The higher number the more important.
767 767 PERM_WEIGHTS = {
768 768 'repository.none': 0,
769 769 'repository.read': 1,
770 770 'repository.write': 3,
771 771 'repository.admin': 4,
772 772
773 773 'group.none': 0,
774 774 'group.read': 1,
775 775 'group.write': 3,
776 776 'group.admin': 4,
777 777
778 778 'usergroup.none': 0,
779 779 'usergroup.read': 1,
780 780 'usergroup.write': 3,
781 781 'usergroup.admin': 4,
782 782 'hg.repogroup.create.false': 0,
783 783 'hg.repogroup.create.true': 1,
784 784
785 785 'hg.usergroup.create.false': 0,
786 786 'hg.usergroup.create.true': 1,
787 787
788 788 'hg.fork.none': 0,
789 789 'hg.fork.repository': 1,
790 790 'hg.create.none': 0,
791 791 'hg.create.repository': 1
792 792 }
793 793
794 794 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
795 795 permission_name = Column("permission_name", String(255), nullable=True, unique=None, default=None)
796 796 permission_longname = Column("permission_longname", String(255), nullable=True, unique=None, default=None)
797 797
798 798 def __unicode__(self):
799 799 return u"<%s('%s:%s')>" % (
800 800 self.__class__.__name__, self.permission_id, self.permission_name
801 801 )
802 802
803 803 @classmethod
804 804 def get_by_key(cls, key):
805 805 return cls.query().filter(cls.permission_name == key).scalar()
806 806
807 807
808 808 class UserRepoToPerm(Base, BaseModel):
809 809 __tablename__ = 'repo_to_perm'
810 810 __table_args__ = (
811 811 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
812 812 {'extend_existing': True, 'mysql_engine': 'InnoDB',
813 813 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
814 814 )
815 815 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
816 816 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
817 817 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
818 818 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
819 819
820 820 user = relationship('User')
821 821 repository = relationship('Repository')
822 822 permission = relationship('Permission')
823 823
824 824 def __unicode__(self):
825 825 return u'<%s => %s >' % (self.user, self.repository)
826 826
827 827
828 828 class UserUserGroupToPerm(Base, BaseModel):
829 829 __tablename__ = 'user_user_group_to_perm'
830 830 __table_args__ = (
831 831 UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
832 832 {'extend_existing': True, 'mysql_engine': 'InnoDB',
833 833 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
834 834 )
835 835 user_user_group_to_perm_id = Column("user_user_group_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 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
839 839
840 840 user = relationship('User')
841 841 user_group = relationship('UserGroup')
842 842 permission = relationship('Permission')
843 843
844 844 def __unicode__(self):
845 845 return u'<%s => %s >' % (self.user, self.user_group)
846 846
847 847
848 848 class UserToPerm(Base, BaseModel):
849 849 __tablename__ = 'user_to_perm'
850 850 __table_args__ = (
851 851 UniqueConstraint('user_id', 'permission_id'),
852 852 {'extend_existing': True, 'mysql_engine': 'InnoDB',
853 853 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
854 854 )
855 855 user_to_perm_id = Column("user_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
859 859 user = relationship('User')
860 860 permission = relationship('Permission', lazy='joined')
861 861
862 862 def __unicode__(self):
863 863 return u'<%s => %s >' % (self.user, self.permission)
864 864
865 865
866 866 class UserGroupRepoToPerm(Base, BaseModel):
867 867 __tablename__ = 'users_group_repo_to_perm'
868 868 __table_args__ = (
869 869 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
870 870 {'extend_existing': True, 'mysql_engine': 'InnoDB',
871 871 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
872 872 )
873 873 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
874 874 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
875 875 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
876 876 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
877 877
878 878 users_group = relationship('UserGroup')
879 879 permission = relationship('Permission')
880 880 repository = relationship('Repository')
881 881
882 882 def __unicode__(self):
883 883 return u'<UserGroupRepoToPerm:%s => %s >' % (self.users_group, self.repository)
884 884
885 885
886 886 class UserGroupUserGroupToPerm(Base, BaseModel):
887 887 __tablename__ = 'user_group_user_group_to_perm'
888 888 __table_args__ = (
889 889 UniqueConstraint('target_user_group_id', 'user_group_id', 'permission_id'),
890 890 CheckConstraint('target_user_group_id != user_group_id'),
891 891 {'extend_existing': True, 'mysql_engine': 'InnoDB',
892 892 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
893 893 )
894 894 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)
895 895 target_user_group_id = Column("target_user_group_id", Integer(), ForeignKey('users_groups.users_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 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
898 898
899 899 target_user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
900 900 user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
901 901 permission = relationship('Permission')
902 902
903 903 def __unicode__(self):
904 904 return u'<UserGroupUserGroup:%s => %s >' % (self.target_user_group, self.user_group)
905 905
906 906
907 907 class UserGroupToPerm(Base, BaseModel):
908 908 __tablename__ = 'users_group_to_perm'
909 909 __table_args__ = (
910 910 UniqueConstraint('users_group_id', 'permission_id',),
911 911 {'extend_existing': True, 'mysql_engine': 'InnoDB',
912 912 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
913 913 )
914 914 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
915 915 users_group_id = Column("users_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
918 918 users_group = relationship('UserGroup')
919 919 permission = relationship('Permission')
920 920
921 921
922 922 class UserRepoGroupToPerm(Base, BaseModel):
923 923 __tablename__ = 'user_repo_group_to_perm'
924 924 __table_args__ = (
925 925 UniqueConstraint('user_id', 'group_id', 'permission_id'),
926 926 {'extend_existing': True, 'mysql_engine': 'InnoDB',
927 927 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
928 928 )
929 929
930 930 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
931 931 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
932 932 group_id = Column("group_id", Integer(), ForeignKey('groups.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 user = relationship('User')
936 936 group = relationship('RepoGroup')
937 937 permission = relationship('Permission')
938 938
939 939
940 940 class UserGroupRepoGroupToPerm(Base, BaseModel):
941 941 __tablename__ = 'users_group_repo_group_to_perm'
942 942 __table_args__ = (
943 943 UniqueConstraint('users_group_id', 'group_id'),
944 944 {'extend_existing': True, 'mysql_engine': 'InnoDB',
945 945 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
946 946 )
947 947
948 948 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)
949 949 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
950 950 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
951 951 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
952 952
953 953 users_group = relationship('UserGroup')
954 954 permission = relationship('Permission')
955 955 group = relationship('RepoGroup')
956 956
957 957
958 958 class Statistics(Base, BaseModel):
959 959 __tablename__ = 'statistics'
960 960 __table_args__ = (
961 961 UniqueConstraint('repository_id'),
962 962 {'extend_existing': True, 'mysql_engine': 'InnoDB',
963 963 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
964 964 )
965 965 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
966 966 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
967 967 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
968 968 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
969 969 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
970 970 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
971 971
972 972 repository = relationship('Repository', single_parent=True)
973 973
974 974
975 975 class UserFollowing(Base, BaseModel):
976 976 __tablename__ = 'user_followings'
977 977 __table_args__ = (
978 978 UniqueConstraint('user_id', 'follows_repository_id'),
979 979 UniqueConstraint('user_id', 'follows_user_id'),
980 980 {'extend_existing': True, 'mysql_engine': 'InnoDB',
981 981 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
982 982 )
983 983
984 984 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
985 985 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
986 986 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
987 987 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
988 988 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
989 989
990 990 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
991 991
992 992 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
993 993 follows_repository = relationship('Repository', order_by='Repository.repo_name')
994 994
995 995
996 996 class CacheInvalidation(Base, BaseModel):
997 997 __tablename__ = 'cache_invalidation'
998 998 __table_args__ = (
999 999 UniqueConstraint('cache_key'),
1000 1000 Index('key_idx', 'cache_key'),
1001 1001 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1002 1002 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1003 1003 )
1004 1004 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1005 1005 cache_key = Column("cache_key", String(255), nullable=True, unique=None, default=None)
1006 1006 cache_args = Column("cache_args", String(255), nullable=True, unique=None, default=None)
1007 1007 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1008 1008
1009 1009 def __init__(self, cache_key, cache_args=''):
1010 1010 self.cache_key = cache_key
1011 1011 self.cache_args = cache_args
1012 1012 self.cache_active = False
1013 1013
1014 1014
1015 1015 class ChangesetComment(Base, BaseModel):
1016 1016 __tablename__ = 'changeset_comments'
1017 1017 __table_args__ = (
1018 1018 Index('cc_revision_idx', 'revision'),
1019 1019 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1020 1020 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1021 1021 )
1022 1022 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1023 1023 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1024 1024 revision = Column('revision', String(40), nullable=True)
1025 1025 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1026 1026 line_no = Column('line_no', Unicode(10), nullable=True)
1027 1027 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1028 1028 f_path = Column('f_path', Unicode(1000), nullable=True)
1029 1029 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1030 1030 text = Column('text', UnicodeText().with_variant(UnicodeText(25000), 'mysql'), nullable=False)
1031 1031 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1032 1032 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1033 1033
1034 1034 author = relationship('User', lazy='joined')
1035 1035 repo = relationship('Repository')
1036 1036 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1037 1037 pull_request = relationship('PullRequest', lazy='joined')
1038 1038
1039 1039
1040 1040 class ChangesetStatus(Base, BaseModel):
1041 1041 __tablename__ = 'changeset_statuses'
1042 1042 __table_args__ = (
1043 1043 Index('cs_revision_idx', 'revision'),
1044 1044 Index('cs_version_idx', 'version'),
1045 1045 UniqueConstraint('repo_id', 'revision', 'version'),
1046 1046 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1047 1047 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1048 1048 )
1049 1049 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1050 1050 STATUS_APPROVED = 'approved'
1051 1051 STATUS_REJECTED = 'rejected'
1052 1052 STATUS_UNDER_REVIEW = 'under_review'
1053 1053
1054 1054 STATUSES = [
1055 1055 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1056 1056 (STATUS_APPROVED, _("Approved")),
1057 1057 (STATUS_REJECTED, _("Rejected")),
1058 1058 (STATUS_UNDER_REVIEW, _("Under Review")),
1059 1059 ]
1060 1060
1061 1061 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1062 1062 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1063 1063 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1064 1064 revision = Column('revision', String(40), nullable=False)
1065 1065 status = Column('status', String(128), nullable=False, default=DEFAULT)
1066 1066 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1067 1067 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1068 1068 version = Column('version', Integer(), nullable=False, default=0)
1069 1069 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1070 1070
1071 1071 author = relationship('User', lazy='joined')
1072 1072 repo = relationship('Repository')
1073 1073 comment = relationship('ChangesetComment', lazy='joined')
1074 1074 pull_request = relationship('PullRequest', lazy='joined')
1075 1075
1076 1076
1077 1077
1078 1078 class PullRequest(Base, BaseModel):
1079 1079 __tablename__ = 'pull_requests'
1080 1080 __table_args__ = (
1081 1081 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1082 1082 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1083 1083 )
1084 1084
1085 1085 STATUS_NEW = u'new'
1086 1086 STATUS_OPEN = u'open'
1087 1087 STATUS_CLOSED = u'closed'
1088 1088
1089 1089 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1090 1090 title = Column('title', Unicode(256), nullable=True)
1091 1091 description = Column('description', UnicodeText().with_variant(UnicodeText(10240), 'mysql'), nullable=True)
1092 1092 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1093 1093 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1094 1094 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1095 1095 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1096 1096 _revisions = Column('revisions', UnicodeText().with_variant(UnicodeText(20500), 'mysql'))
1097 1097 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1098 1098 org_ref = Column('org_ref', Unicode(256), nullable=False)
1099 1099 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1100 1100 other_ref = Column('other_ref', Unicode(256), nullable=False)
1101 1101
1102 1102 author = relationship('User', lazy='joined')
1103 1103 reviewers = relationship('PullRequestReviewers',
1104 1104 cascade="all, delete, delete-orphan")
1105 1105 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1106 1106 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1107 1107 statuses = relationship('ChangesetStatus')
1108 1108 comments = relationship('ChangesetComment',
1109 1109 cascade="all, delete, delete-orphan")
1110 1110
1111 1111
1112 1112 class PullRequestReviewers(Base, BaseModel):
1113 1113 __tablename__ = 'pull_request_reviewers'
1114 1114 __table_args__ = (
1115 1115 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1116 1116 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1117 1117 )
1118 1118
1119 1119 def __init__(self, user=None, pull_request=None):
1120 1120 self.user = user
1121 1121 self.pull_request = pull_request
1122 1122
1123 1123 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1124 1124 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1125 1125 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1126 1126
1127 1127 user = relationship('User')
1128 1128 pull_request = relationship('PullRequest')
1129 1129
1130 1130
1131 1131 class Notification(Base, BaseModel):
1132 1132 __tablename__ = 'notifications'
1133 1133 __table_args__ = (
1134 1134 Index('notification_type_idx', 'type'),
1135 1135 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1136 1136 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1137 1137 )
1138 1138
1139 1139 TYPE_CHANGESET_COMMENT = u'cs_comment'
1140 1140 TYPE_MESSAGE = u'message'
1141 1141 TYPE_MENTION = u'mention'
1142 1142 TYPE_REGISTRATION = u'registration'
1143 1143 TYPE_PULL_REQUEST = u'pull_request'
1144 1144 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1145 1145
1146 1146 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1147 1147 subject = Column('subject', Unicode(512), nullable=True)
1148 1148 body = Column('body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True)
1149 1149 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1150 1150 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1151 1151 type_ = Column('type', Unicode(256))
1152 1152
1153 1153 created_by_user = relationship('User')
1154 1154 notifications_to_users = relationship('UserNotification', lazy='joined',
1155 1155 cascade="all, delete, delete-orphan")
1156 1156
1157 1157
1158 1158 class UserNotification(Base, BaseModel):
1159 1159 __tablename__ = 'user_to_notification'
1160 1160 __table_args__ = (
1161 1161 UniqueConstraint('user_id', 'notification_id'),
1162 1162 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1163 1163 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1164 1164 )
1165 1165 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1166 1166 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1167 1167 read = Column('read', Boolean, default=False)
1168 1168 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1169 1169
1170 1170 user = relationship('User', lazy="joined")
1171 1171 notification = relationship('Notification', lazy="joined",
1172 1172 order_by=lambda: Notification.created_on.desc(),)
1173 1173
1174 1174
1175 1175 class Gist(Base, BaseModel):
1176 1176 __tablename__ = 'gists'
1177 1177 __table_args__ = (
1178 1178 Index('g_gist_access_id_idx', 'gist_access_id'),
1179 1179 Index('g_created_on_idx', 'created_on'),
1180 1180 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1181 1181 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1182 1182 )
1183 1183 GIST_PUBLIC = u'public'
1184 1184 GIST_PRIVATE = u'private'
1185 1185
1186 1186 gist_id = Column('gist_id', Integer(), primary_key=True)
1187 1187 gist_access_id = Column('gist_access_id', Unicode(250))
1188 1188 gist_description = Column('gist_description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
1189 1189 gist_owner = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=True)
1190 1190 gist_expires = Column('gist_expires', Float(53), nullable=False)
1191 1191 gist_type = Column('gist_type', Unicode(128), nullable=False)
1192 1192 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1193 1193 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1194 1194
1195 1195 owner = relationship('User')
1196 1196
1197 1197
1198 1198 class DbMigrateVersion(Base, BaseModel):
1199 1199 __tablename__ = 'db_migrate_version'
1200 1200 __table_args__ = (
1201 1201 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1202 1202 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1203 1203 )
1204 1204 repository_id = Column('repository_id', String(250), primary_key=True)
1205 1205 repository_path = Column('repository_path', Text)
1206 1206 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
General Comments 0
You need to be logged in to leave comments. Login now