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