##// END OF EJS Templates
Fixed issues with how mysql handles float values. Caused gists with...
marcink -
r3971:7f71d22a default
parent child Browse files
Show More
@@ -1,62 +1,62
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.__init__
4 4 ~~~~~~~~~~~~~~~~~~
5 5
6 6 RhodeCode, a web based repository management based on pylons
7 7 versioning implementation: http://www.python.org/dev/peps/pep-0386/
8 8
9 9 :created_on: Apr 9, 2010
10 10 :author: marcink
11 11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 12 :license: GPLv3, see COPYING for more details.
13 13 """
14 14 # This program is free software: you can redistribute it and/or modify
15 15 # it under the terms of the GNU General Public License as published by
16 16 # the Free Software Foundation, either version 3 of the License, or
17 17 # (at your option) any later version.
18 18 #
19 19 # This program is distributed in the hope that it will be useful,
20 20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 22 # GNU General Public License for more details.
23 23 #
24 24 # You should have received a copy of the GNU General Public License
25 25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 26 import sys
27 27 import platform
28 28
29 VERSION = (1, 7, 0)
29 VERSION = (1, 7, 1)
30 30 BACKENDS = {
31 31 'hg': 'Mercurial repository',
32 32 'git': 'Git repository',
33 33 }
34 34
35 35 CELERY_ON = False
36 36 CELERY_EAGER = False
37 37
38 38 # link to config for pylons
39 39 CONFIG = {}
40 40
41 41 # Linked module for extensions
42 42 EXTENSIONS = {}
43 43
44 44 try:
45 45 from rhodecode.lib import get_current_revision
46 46 _rev = get_current_revision(quiet=True)
47 47 if _rev and len(VERSION) > 3:
48 48 VERSION += ('%s' % _rev[0],)
49 49 except ImportError:
50 50 pass
51 51
52 52 __version__ = ('.'.join((str(each) for each in VERSION[:3])) +
53 53 '.'.join(VERSION[3:]))
54 54 __dbversion__ = 13 # defines current db version for migrations
55 55 __platform__ = platform.system()
56 56 __license__ = 'GPLv3'
57 57 __py_version__ = sys.version_info
58 58 __author__ = 'Marcin Kuzminski'
59 59 __url__ = 'http://rhodecode.org'
60 60
61 61 is_windows = __platform__ in ['Windows']
62 62 is_unix = not is_windows
@@ -1,2228 +1,2228
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 time
28 28 import logging
29 29 import datetime
30 30 import traceback
31 31 import hashlib
32 32 import collections
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 from rhodecode.lib.vcs.backends.base import EmptyChangeset
48 48
49 49 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
50 50 safe_unicode, remove_suffix, remove_prefix, time_to_datetime, _set_extras
51 51 from rhodecode.lib.compat import json
52 52 from rhodecode.lib.caching_query import FromCache
53 53
54 54 from rhodecode.model.meta import Base, Session
55 55
56 56 URL_SEP = '/'
57 57 log = logging.getLogger(__name__)
58 58
59 59 #==============================================================================
60 60 # BASE CLASSES
61 61 #==============================================================================
62 62
63 63 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
64 64
65 65
66 66 class BaseModel(object):
67 67 """
68 68 Base Model for all classess
69 69 """
70 70
71 71 @classmethod
72 72 def _get_keys(cls):
73 73 """return column names for this model """
74 74 return class_mapper(cls).c.keys()
75 75
76 76 def get_dict(self):
77 77 """
78 78 return dict with keys and values corresponding
79 79 to this model data """
80 80
81 81 d = {}
82 82 for k in self._get_keys():
83 83 d[k] = getattr(self, k)
84 84
85 85 # also use __json__() if present to get additional fields
86 86 _json_attr = getattr(self, '__json__', None)
87 87 if _json_attr:
88 88 # update with attributes from __json__
89 89 if callable(_json_attr):
90 90 _json_attr = _json_attr()
91 91 for k, val in _json_attr.iteritems():
92 92 d[k] = val
93 93 return d
94 94
95 95 def get_appstruct(self):
96 96 """return list with keys and values tupples corresponding
97 97 to this model data """
98 98
99 99 l = []
100 100 for k in self._get_keys():
101 101 l.append((k, getattr(self, k),))
102 102 return l
103 103
104 104 def populate_obj(self, populate_dict):
105 105 """populate model with data from given populate_dict"""
106 106
107 107 for k in self._get_keys():
108 108 if k in populate_dict:
109 109 setattr(self, k, populate_dict[k])
110 110
111 111 @classmethod
112 112 def query(cls):
113 113 return Session().query(cls)
114 114
115 115 @classmethod
116 116 def get(cls, id_):
117 117 if id_:
118 118 return cls.query().get(id_)
119 119
120 120 @classmethod
121 121 def get_or_404(cls, id_):
122 122 try:
123 123 id_ = int(id_)
124 124 except (TypeError, ValueError):
125 125 raise HTTPNotFound
126 126
127 127 res = cls.query().get(id_)
128 128 if not res:
129 129 raise HTTPNotFound
130 130 return res
131 131
132 132 @classmethod
133 133 def getAll(cls):
134 134 # deprecated and left for backward compatibility
135 135 return cls.get_all()
136 136
137 137 @classmethod
138 138 def get_all(cls):
139 139 return cls.query().all()
140 140
141 141 @classmethod
142 142 def delete(cls, id_):
143 143 obj = cls.query().get(id_)
144 144 Session().delete(obj)
145 145
146 146 def __repr__(self):
147 147 if hasattr(self, '__unicode__'):
148 148 # python repr needs to return str
149 149 return safe_str(self.__unicode__())
150 150 return '<DB:%s>' % (self.__class__.__name__)
151 151
152 152
153 153 class RhodeCodeSetting(Base, BaseModel):
154 154 __tablename__ = 'rhodecode_settings'
155 155 __table_args__ = (
156 156 UniqueConstraint('app_settings_name'),
157 157 {'extend_existing': True, 'mysql_engine': 'InnoDB',
158 158 'mysql_charset': 'utf8'}
159 159 )
160 160 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
161 161 app_settings_name = Column("app_settings_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
162 162 _app_settings_value = Column("app_settings_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
163 163
164 164 def __init__(self, k='', v=''):
165 165 self.app_settings_name = k
166 166 self.app_settings_value = v
167 167
168 168 @validates('_app_settings_value')
169 169 def validate_settings_value(self, key, val):
170 170 assert type(val) == unicode
171 171 return val
172 172
173 173 @hybrid_property
174 174 def app_settings_value(self):
175 175 v = self._app_settings_value
176 176 if self.app_settings_name in ["ldap_active",
177 177 "default_repo_enable_statistics",
178 178 "default_repo_enable_locking",
179 179 "default_repo_private",
180 180 "default_repo_enable_downloads"]:
181 181 v = str2bool(v)
182 182 return v
183 183
184 184 @app_settings_value.setter
185 185 def app_settings_value(self, val):
186 186 """
187 187 Setter that will always make sure we use unicode in app_settings_value
188 188
189 189 :param val:
190 190 """
191 191 self._app_settings_value = safe_unicode(val)
192 192
193 193 def __unicode__(self):
194 194 return u"<%s('%s:%s')>" % (
195 195 self.__class__.__name__,
196 196 self.app_settings_name, self.app_settings_value
197 197 )
198 198
199 199 @classmethod
200 200 def get_by_name(cls, key):
201 201 return cls.query()\
202 202 .filter(cls.app_settings_name == key).scalar()
203 203
204 204 @classmethod
205 205 def get_by_name_or_create(cls, key):
206 206 res = cls.get_by_name(key)
207 207 if not res:
208 208 res = cls(key)
209 209 return res
210 210
211 211 @classmethod
212 212 def get_app_settings(cls, cache=False):
213 213
214 214 ret = cls.query()
215 215
216 216 if cache:
217 217 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
218 218
219 219 if not ret:
220 220 raise Exception('Could not get application settings !')
221 221 settings = {}
222 222 for each in ret:
223 223 settings['rhodecode_' + each.app_settings_name] = \
224 224 each.app_settings_value
225 225
226 226 return settings
227 227
228 228 @classmethod
229 229 def get_ldap_settings(cls, cache=False):
230 230 ret = cls.query()\
231 231 .filter(cls.app_settings_name.startswith('ldap_')).all()
232 232 fd = {}
233 233 for row in ret:
234 234 fd.update({row.app_settings_name: row.app_settings_value})
235 235
236 236 return fd
237 237
238 238 @classmethod
239 239 def get_default_repo_settings(cls, cache=False, strip_prefix=False):
240 240 ret = cls.query()\
241 241 .filter(cls.app_settings_name.startswith('default_')).all()
242 242 fd = {}
243 243 for row in ret:
244 244 key = row.app_settings_name
245 245 if strip_prefix:
246 246 key = remove_prefix(key, prefix='default_')
247 247 fd.update({key: row.app_settings_value})
248 248
249 249 return fd
250 250
251 251
252 252 class RhodeCodeUi(Base, BaseModel):
253 253 __tablename__ = 'rhodecode_ui'
254 254 __table_args__ = (
255 255 UniqueConstraint('ui_key'),
256 256 {'extend_existing': True, 'mysql_engine': 'InnoDB',
257 257 'mysql_charset': 'utf8'}
258 258 )
259 259
260 260 HOOK_UPDATE = 'changegroup.update'
261 261 HOOK_REPO_SIZE = 'changegroup.repo_size'
262 262 HOOK_PUSH = 'changegroup.push_logger'
263 263 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
264 264 HOOK_PULL = 'outgoing.pull_logger'
265 265 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
266 266
267 267 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
268 268 ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
269 269 ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
270 270 ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
271 271 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
272 272
273 273 # def __init__(self, section='', key='', value=''):
274 274 # self.ui_section = section
275 275 # self.ui_key = key
276 276 # self.ui_value = value
277 277
278 278 @classmethod
279 279 def get_by_key(cls, key):
280 280 return cls.query().filter(cls.ui_key == key).scalar()
281 281
282 282 @classmethod
283 283 def get_builtin_hooks(cls):
284 284 q = cls.query()
285 285 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
286 286 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
287 287 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
288 288 return q.all()
289 289
290 290 @classmethod
291 291 def get_custom_hooks(cls):
292 292 q = cls.query()
293 293 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
294 294 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
295 295 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
296 296 q = q.filter(cls.ui_section == 'hooks')
297 297 return q.all()
298 298
299 299 @classmethod
300 300 def get_repos_location(cls):
301 301 return cls.get_by_key('/').ui_value
302 302
303 303 @classmethod
304 304 def create_or_update_hook(cls, key, val):
305 305 new_ui = cls.get_by_key(key) or cls()
306 306 new_ui.ui_section = 'hooks'
307 307 new_ui.ui_active = True
308 308 new_ui.ui_key = key
309 309 new_ui.ui_value = val
310 310
311 311 Session().add(new_ui)
312 312
313 313 def __repr__(self):
314 314 return '<DB:%s[%s:%s]>' % (self.__class__.__name__, self.ui_key,
315 315 self.ui_value)
316 316
317 317
318 318 class User(Base, BaseModel):
319 319 __tablename__ = 'users'
320 320 __table_args__ = (
321 321 UniqueConstraint('username'), UniqueConstraint('email'),
322 322 Index('u_username_idx', 'username'),
323 323 Index('u_email_idx', 'email'),
324 324 {'extend_existing': True, 'mysql_engine': 'InnoDB',
325 325 'mysql_charset': 'utf8'}
326 326 )
327 327 DEFAULT_USER = 'default'
328 328
329 329 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
330 330 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
331 331 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
332 332 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
333 333 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
334 334 name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
335 335 lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
336 336 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
337 337 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
338 338 ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
339 339 api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
340 340 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
341 341
342 342 user_log = relationship('UserLog')
343 343 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
344 344
345 345 repositories = relationship('Repository')
346 346 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
347 347 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
348 348
349 349 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
350 350 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
351 351
352 352 group_member = relationship('UserGroupMember', cascade='all')
353 353
354 354 notifications = relationship('UserNotification', cascade='all')
355 355 # notifications assigned to this user
356 356 user_created_notifications = relationship('Notification', cascade='all')
357 357 # comments created by this user
358 358 user_comments = relationship('ChangesetComment', cascade='all')
359 359 #extra emails for this user
360 360 user_emails = relationship('UserEmailMap', cascade='all')
361 361
362 362 @hybrid_property
363 363 def email(self):
364 364 return self._email
365 365
366 366 @email.setter
367 367 def email(self, val):
368 368 self._email = val.lower() if val else None
369 369
370 370 @property
371 371 def firstname(self):
372 372 # alias for future
373 373 return self.name
374 374
375 375 @property
376 376 def emails(self):
377 377 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
378 378 return [self.email] + [x.email for x in other]
379 379
380 380 @property
381 381 def ip_addresses(self):
382 382 ret = UserIpMap.query().filter(UserIpMap.user == self).all()
383 383 return [x.ip_addr for x in ret]
384 384
385 385 @property
386 386 def username_and_name(self):
387 387 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
388 388
389 389 @property
390 390 def full_name(self):
391 391 return '%s %s' % (self.firstname, self.lastname)
392 392
393 393 @property
394 394 def full_name_or_username(self):
395 395 return ('%s %s' % (self.firstname, self.lastname)
396 396 if (self.firstname and self.lastname) else self.username)
397 397
398 398 @property
399 399 def full_contact(self):
400 400 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
401 401
402 402 @property
403 403 def short_contact(self):
404 404 return '%s %s' % (self.firstname, self.lastname)
405 405
406 406 @property
407 407 def is_admin(self):
408 408 return self.admin
409 409
410 410 @property
411 411 def AuthUser(self):
412 412 """
413 413 Returns instance of AuthUser for this user
414 414 """
415 415 from rhodecode.lib.auth import AuthUser
416 416 return AuthUser(user_id=self.user_id, api_key=self.api_key,
417 417 username=self.username)
418 418
419 419 def __unicode__(self):
420 420 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
421 421 self.user_id, self.username)
422 422
423 423 @classmethod
424 424 def get_by_username(cls, username, case_insensitive=False, cache=False):
425 425 if case_insensitive:
426 426 q = cls.query().filter(cls.username.ilike(username))
427 427 else:
428 428 q = cls.query().filter(cls.username == username)
429 429
430 430 if cache:
431 431 q = q.options(FromCache(
432 432 "sql_cache_short",
433 433 "get_user_%s" % _hash_key(username)
434 434 )
435 435 )
436 436 return q.scalar()
437 437
438 438 @classmethod
439 439 def get_by_api_key(cls, api_key, cache=False):
440 440 q = cls.query().filter(cls.api_key == api_key)
441 441
442 442 if cache:
443 443 q = q.options(FromCache("sql_cache_short",
444 444 "get_api_key_%s" % api_key))
445 445 return q.scalar()
446 446
447 447 @classmethod
448 448 def get_by_email(cls, email, case_insensitive=False, cache=False):
449 449 if case_insensitive:
450 450 q = cls.query().filter(cls.email.ilike(email))
451 451 else:
452 452 q = cls.query().filter(cls.email == email)
453 453
454 454 if cache:
455 455 q = q.options(FromCache("sql_cache_short",
456 456 "get_email_key_%s" % email))
457 457
458 458 ret = q.scalar()
459 459 if ret is None:
460 460 q = UserEmailMap.query()
461 461 # try fetching in alternate email map
462 462 if case_insensitive:
463 463 q = q.filter(UserEmailMap.email.ilike(email))
464 464 else:
465 465 q = q.filter(UserEmailMap.email == email)
466 466 q = q.options(joinedload(UserEmailMap.user))
467 467 if cache:
468 468 q = q.options(FromCache("sql_cache_short",
469 469 "get_email_map_key_%s" % email))
470 470 ret = getattr(q.scalar(), 'user', None)
471 471
472 472 return ret
473 473
474 474 @classmethod
475 475 def get_from_cs_author(cls, author):
476 476 """
477 477 Tries to get User objects out of commit author string
478 478
479 479 :param author:
480 480 """
481 481 from rhodecode.lib.helpers import email, author_name
482 482 # Valid email in the attribute passed, see if they're in the system
483 483 _email = email(author)
484 484 if _email:
485 485 user = cls.get_by_email(_email, case_insensitive=True)
486 486 if user:
487 487 return user
488 488 # Maybe we can match by username?
489 489 _author = author_name(author)
490 490 user = cls.get_by_username(_author, case_insensitive=True)
491 491 if user:
492 492 return user
493 493
494 494 def update_lastlogin(self):
495 495 """Update user lastlogin"""
496 496 self.last_login = datetime.datetime.now()
497 497 Session().add(self)
498 498 log.debug('updated user %s lastlogin' % self.username)
499 499
500 500 @classmethod
501 501 def get_first_admin(cls):
502 502 user = User.query().filter(User.admin == True).first()
503 503 if user is None:
504 504 raise Exception('Missing administrative account!')
505 505 return user
506 506
507 507 @classmethod
508 508 def get_default_user(cls, cache=False):
509 509 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
510 510 if user is None:
511 511 raise Exception('Missing default account!')
512 512 return user
513 513
514 514 def get_api_data(self):
515 515 """
516 516 Common function for generating user related data for API
517 517 """
518 518 user = self
519 519 data = dict(
520 520 user_id=user.user_id,
521 521 username=user.username,
522 522 firstname=user.name,
523 523 lastname=user.lastname,
524 524 email=user.email,
525 525 emails=user.emails,
526 526 api_key=user.api_key,
527 527 active=user.active,
528 528 admin=user.admin,
529 529 ldap_dn=user.ldap_dn,
530 530 last_login=user.last_login,
531 531 ip_addresses=user.ip_addresses
532 532 )
533 533 return data
534 534
535 535 def __json__(self):
536 536 data = dict(
537 537 full_name=self.full_name,
538 538 full_name_or_username=self.full_name_or_username,
539 539 short_contact=self.short_contact,
540 540 full_contact=self.full_contact
541 541 )
542 542 data.update(self.get_api_data())
543 543 return data
544 544
545 545
546 546 class UserEmailMap(Base, BaseModel):
547 547 __tablename__ = 'user_email_map'
548 548 __table_args__ = (
549 549 Index('uem_email_idx', 'email'),
550 550 UniqueConstraint('email'),
551 551 {'extend_existing': True, 'mysql_engine': 'InnoDB',
552 552 'mysql_charset': 'utf8'}
553 553 )
554 554 __mapper_args__ = {}
555 555
556 556 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
557 557 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
558 558 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
559 559 user = relationship('User', lazy='joined')
560 560
561 561 @validates('_email')
562 562 def validate_email(self, key, email):
563 563 # check if this email is not main one
564 564 main_email = Session().query(User).filter(User.email == email).scalar()
565 565 if main_email is not None:
566 566 raise AttributeError('email %s is present is user table' % email)
567 567 return email
568 568
569 569 @hybrid_property
570 570 def email(self):
571 571 return self._email
572 572
573 573 @email.setter
574 574 def email(self, val):
575 575 self._email = val.lower() if val else None
576 576
577 577
578 578 class UserIpMap(Base, BaseModel):
579 579 __tablename__ = 'user_ip_map'
580 580 __table_args__ = (
581 581 UniqueConstraint('user_id', 'ip_addr'),
582 582 {'extend_existing': True, 'mysql_engine': 'InnoDB',
583 583 'mysql_charset': 'utf8'}
584 584 )
585 585 __mapper_args__ = {}
586 586
587 587 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
588 588 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
589 589 ip_addr = Column("ip_addr", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
590 590 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
591 591 user = relationship('User', lazy='joined')
592 592
593 593 @classmethod
594 594 def _get_ip_range(cls, ip_addr):
595 595 from rhodecode.lib import ipaddr
596 596 net = ipaddr.IPNetwork(address=ip_addr)
597 597 return [str(net.network), str(net.broadcast)]
598 598
599 599 def __json__(self):
600 600 return dict(
601 601 ip_addr=self.ip_addr,
602 602 ip_range=self._get_ip_range(self.ip_addr)
603 603 )
604 604
605 605
606 606 class UserLog(Base, BaseModel):
607 607 __tablename__ = 'user_logs'
608 608 __table_args__ = (
609 609 {'extend_existing': True, 'mysql_engine': 'InnoDB',
610 610 'mysql_charset': 'utf8'},
611 611 )
612 612 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
613 613 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
614 614 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
615 615 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
616 616 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
617 617 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
618 618 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
619 619 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
620 620
621 621 def __unicode__(self):
622 622 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
623 623 self.repository_name,
624 624 self.action)
625 625
626 626 @property
627 627 def action_as_day(self):
628 628 return datetime.date(*self.action_date.timetuple()[:3])
629 629
630 630 user = relationship('User')
631 631 repository = relationship('Repository', cascade='')
632 632
633 633
634 634 class UserGroup(Base, BaseModel):
635 635 __tablename__ = 'users_groups'
636 636 __table_args__ = (
637 637 {'extend_existing': True, 'mysql_engine': 'InnoDB',
638 638 'mysql_charset': 'utf8'},
639 639 )
640 640
641 641 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
642 642 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
643 643 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
644 644 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
645 645 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
646 646
647 647 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
648 648 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
649 649 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
650 650 users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
651 651 user_user_group_to_perm = relationship('UserUserGroupToPerm ', cascade='all')
652 652 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm ', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
653 653
654 654 user = relationship('User')
655 655
656 656 def __unicode__(self):
657 657 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
658 658 self.users_group_id,
659 659 self.users_group_name)
660 660
661 661 @classmethod
662 662 def get_by_group_name(cls, group_name, cache=False,
663 663 case_insensitive=False):
664 664 if case_insensitive:
665 665 q = cls.query().filter(cls.users_group_name.ilike(group_name))
666 666 else:
667 667 q = cls.query().filter(cls.users_group_name == group_name)
668 668 if cache:
669 669 q = q.options(FromCache(
670 670 "sql_cache_short",
671 671 "get_user_%s" % _hash_key(group_name)
672 672 )
673 673 )
674 674 return q.scalar()
675 675
676 676 @classmethod
677 677 def get(cls, users_group_id, cache=False):
678 678 users_group = cls.query()
679 679 if cache:
680 680 users_group = users_group.options(FromCache("sql_cache_short",
681 681 "get_users_group_%s" % users_group_id))
682 682 return users_group.get(users_group_id)
683 683
684 684 def get_api_data(self):
685 685 users_group = self
686 686
687 687 data = dict(
688 688 users_group_id=users_group.users_group_id,
689 689 group_name=users_group.users_group_name,
690 690 active=users_group.users_group_active,
691 691 )
692 692
693 693 return data
694 694
695 695
696 696 class UserGroupMember(Base, BaseModel):
697 697 __tablename__ = 'users_groups_members'
698 698 __table_args__ = (
699 699 {'extend_existing': True, 'mysql_engine': 'InnoDB',
700 700 'mysql_charset': 'utf8'},
701 701 )
702 702
703 703 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
704 704 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
705 705 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
706 706
707 707 user = relationship('User', lazy='joined')
708 708 users_group = relationship('UserGroup')
709 709
710 710 def __init__(self, gr_id='', u_id=''):
711 711 self.users_group_id = gr_id
712 712 self.user_id = u_id
713 713
714 714
715 715 class RepositoryField(Base, BaseModel):
716 716 __tablename__ = 'repositories_fields'
717 717 __table_args__ = (
718 718 UniqueConstraint('repository_id', 'field_key'), # no-multi field
719 719 {'extend_existing': True, 'mysql_engine': 'InnoDB',
720 720 'mysql_charset': 'utf8'},
721 721 )
722 722 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
723 723
724 724 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
725 725 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
726 726 field_key = Column("field_key", String(250, convert_unicode=False, assert_unicode=None))
727 727 field_label = Column("field_label", String(1024, convert_unicode=False, assert_unicode=None), nullable=False)
728 728 field_value = Column("field_value", String(10000, convert_unicode=False, assert_unicode=None), nullable=False)
729 729 field_desc = Column("field_desc", String(1024, convert_unicode=False, assert_unicode=None), nullable=False)
730 730 field_type = Column("field_type", String(256), nullable=False, unique=None)
731 731 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
732 732
733 733 repository = relationship('Repository')
734 734
735 735 @property
736 736 def field_key_prefixed(self):
737 737 return 'ex_%s' % self.field_key
738 738
739 739 @classmethod
740 740 def un_prefix_key(cls, key):
741 741 if key.startswith(cls.PREFIX):
742 742 return key[len(cls.PREFIX):]
743 743 return key
744 744
745 745 @classmethod
746 746 def get_by_key_name(cls, key, repo):
747 747 row = cls.query()\
748 748 .filter(cls.repository == repo)\
749 749 .filter(cls.field_key == key).scalar()
750 750 return row
751 751
752 752
753 753 class Repository(Base, BaseModel):
754 754 __tablename__ = 'repositories'
755 755 __table_args__ = (
756 756 UniqueConstraint('repo_name'),
757 757 Index('r_repo_name_idx', 'repo_name'),
758 758 {'extend_existing': True, 'mysql_engine': 'InnoDB',
759 759 'mysql_charset': 'utf8'},
760 760 )
761 761
762 762 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
763 763 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
764 764 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
765 765 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
766 766 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
767 767 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
768 768 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
769 769 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
770 770 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
771 771 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
772 772 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
773 773 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
774 774 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
775 775 _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
776 776 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
777 777
778 778 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
779 779 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
780 780
781 781 user = relationship('User')
782 782 fork = relationship('Repository', remote_side=repo_id)
783 783 group = relationship('RepoGroup')
784 784 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
785 785 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
786 786 stats = relationship('Statistics', cascade='all', uselist=False)
787 787
788 788 followers = relationship('UserFollowing',
789 789 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
790 790 cascade='all')
791 791 extra_fields = relationship('RepositoryField',
792 792 cascade="all, delete, delete-orphan")
793 793
794 794 logs = relationship('UserLog')
795 795 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
796 796
797 797 pull_requests_org = relationship('PullRequest',
798 798 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
799 799 cascade="all, delete, delete-orphan")
800 800
801 801 pull_requests_other = relationship('PullRequest',
802 802 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
803 803 cascade="all, delete, delete-orphan")
804 804
805 805 def __unicode__(self):
806 806 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
807 807 self.repo_name)
808 808
809 809 @hybrid_property
810 810 def locked(self):
811 811 # always should return [user_id, timelocked]
812 812 if self._locked:
813 813 _lock_info = self._locked.split(':')
814 814 return int(_lock_info[0]), _lock_info[1]
815 815 return [None, None]
816 816
817 817 @locked.setter
818 818 def locked(self, val):
819 819 if val and isinstance(val, (list, tuple)):
820 820 self._locked = ':'.join(map(str, val))
821 821 else:
822 822 self._locked = None
823 823
824 824 @hybrid_property
825 825 def changeset_cache(self):
826 826 from rhodecode.lib.vcs.backends.base import EmptyChangeset
827 827 dummy = EmptyChangeset().__json__()
828 828 if not self._changeset_cache:
829 829 return dummy
830 830 try:
831 831 return json.loads(self._changeset_cache)
832 832 except TypeError:
833 833 return dummy
834 834
835 835 @changeset_cache.setter
836 836 def changeset_cache(self, val):
837 837 try:
838 838 self._changeset_cache = json.dumps(val)
839 839 except Exception:
840 840 log.error(traceback.format_exc())
841 841
842 842 @classmethod
843 843 def url_sep(cls):
844 844 return URL_SEP
845 845
846 846 @classmethod
847 847 def normalize_repo_name(cls, repo_name):
848 848 """
849 849 Normalizes os specific repo_name to the format internally stored inside
850 850 dabatabase using URL_SEP
851 851
852 852 :param cls:
853 853 :param repo_name:
854 854 """
855 855 return cls.url_sep().join(repo_name.split(os.sep))
856 856
857 857 @classmethod
858 858 def get_by_repo_name(cls, repo_name):
859 859 q = Session().query(cls).filter(cls.repo_name == repo_name)
860 860 q = q.options(joinedload(Repository.fork))\
861 861 .options(joinedload(Repository.user))\
862 862 .options(joinedload(Repository.group))
863 863 return q.scalar()
864 864
865 865 @classmethod
866 866 def get_by_full_path(cls, repo_full_path):
867 867 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
868 868 repo_name = cls.normalize_repo_name(repo_name)
869 869 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
870 870
871 871 @classmethod
872 872 def get_repo_forks(cls, repo_id):
873 873 return cls.query().filter(Repository.fork_id == repo_id)
874 874
875 875 @classmethod
876 876 def base_path(cls):
877 877 """
878 878 Returns base path when all repos are stored
879 879
880 880 :param cls:
881 881 """
882 882 q = Session().query(RhodeCodeUi)\
883 883 .filter(RhodeCodeUi.ui_key == cls.url_sep())
884 884 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
885 885 return q.one().ui_value
886 886
887 887 @property
888 888 def forks(self):
889 889 """
890 890 Return forks of this repo
891 891 """
892 892 return Repository.get_repo_forks(self.repo_id)
893 893
894 894 @property
895 895 def parent(self):
896 896 """
897 897 Returns fork parent
898 898 """
899 899 return self.fork
900 900
901 901 @property
902 902 def just_name(self):
903 903 return self.repo_name.split(Repository.url_sep())[-1]
904 904
905 905 @property
906 906 def groups_with_parents(self):
907 907 groups = []
908 908 if self.group is None:
909 909 return groups
910 910
911 911 cur_gr = self.group
912 912 groups.insert(0, cur_gr)
913 913 while 1:
914 914 gr = getattr(cur_gr, 'parent_group', None)
915 915 cur_gr = cur_gr.parent_group
916 916 if gr is None:
917 917 break
918 918 groups.insert(0, gr)
919 919
920 920 return groups
921 921
922 922 @property
923 923 def groups_and_repo(self):
924 924 return self.groups_with_parents, self.just_name, self.repo_name
925 925
926 926 @LazyProperty
927 927 def repo_path(self):
928 928 """
929 929 Returns base full path for that repository means where it actually
930 930 exists on a filesystem
931 931 """
932 932 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
933 933 Repository.url_sep())
934 934 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
935 935 return q.one().ui_value
936 936
937 937 @property
938 938 def repo_full_path(self):
939 939 p = [self.repo_path]
940 940 # we need to split the name by / since this is how we store the
941 941 # names in the database, but that eventually needs to be converted
942 942 # into a valid system path
943 943 p += self.repo_name.split(Repository.url_sep())
944 944 return os.path.join(*map(safe_unicode, p))
945 945
946 946 @property
947 947 def cache_keys(self):
948 948 """
949 949 Returns associated cache keys for that repo
950 950 """
951 951 return CacheInvalidation.query()\
952 952 .filter(CacheInvalidation.cache_args == self.repo_name)\
953 953 .order_by(CacheInvalidation.cache_key)\
954 954 .all()
955 955
956 956 def get_new_name(self, repo_name):
957 957 """
958 958 returns new full repository name based on assigned group and new new
959 959
960 960 :param group_name:
961 961 """
962 962 path_prefix = self.group.full_path_splitted if self.group else []
963 963 return Repository.url_sep().join(path_prefix + [repo_name])
964 964
965 965 @property
966 966 def _ui(self):
967 967 """
968 968 Creates an db based ui object for this repository
969 969 """
970 970 from rhodecode.lib.utils import make_ui
971 971 return make_ui('db', clear_session=False)
972 972
973 973 @classmethod
974 974 def is_valid(cls, repo_name):
975 975 """
976 976 returns True if given repo name is a valid filesystem repository
977 977
978 978 :param cls:
979 979 :param repo_name:
980 980 """
981 981 from rhodecode.lib.utils import is_valid_repo
982 982
983 983 return is_valid_repo(repo_name, cls.base_path())
984 984
985 985 def get_api_data(self):
986 986 """
987 987 Common function for generating repo api data
988 988
989 989 """
990 990 repo = self
991 991 data = dict(
992 992 repo_id=repo.repo_id,
993 993 repo_name=repo.repo_name,
994 994 repo_type=repo.repo_type,
995 995 clone_uri=repo.clone_uri,
996 996 private=repo.private,
997 997 created_on=repo.created_on,
998 998 description=repo.description,
999 999 landing_rev=repo.landing_rev,
1000 1000 owner=repo.user.username,
1001 1001 fork_of=repo.fork.repo_name if repo.fork else None,
1002 1002 enable_statistics=repo.enable_statistics,
1003 1003 enable_locking=repo.enable_locking,
1004 1004 enable_downloads=repo.enable_downloads,
1005 1005 last_changeset=repo.changeset_cache,
1006 1006 locked_by=User.get(self.locked[0]).get_api_data() \
1007 1007 if self.locked[0] else None,
1008 1008 locked_date=time_to_datetime(self.locked[1]) \
1009 1009 if self.locked[1] else None
1010 1010 )
1011 1011 rc_config = RhodeCodeSetting.get_app_settings()
1012 1012 repository_fields = str2bool(rc_config.get('rhodecode_repository_fields'))
1013 1013 if repository_fields:
1014 1014 for f in self.extra_fields:
1015 1015 data[f.field_key_prefixed] = f.field_value
1016 1016
1017 1017 return data
1018 1018
1019 1019 @classmethod
1020 1020 def lock(cls, repo, user_id, lock_time=None):
1021 1021 if not lock_time:
1022 1022 lock_time = time.time()
1023 1023 repo.locked = [user_id, lock_time]
1024 1024 Session().add(repo)
1025 1025 Session().commit()
1026 1026
1027 1027 @classmethod
1028 1028 def unlock(cls, repo):
1029 1029 repo.locked = None
1030 1030 Session().add(repo)
1031 1031 Session().commit()
1032 1032
1033 1033 @classmethod
1034 1034 def getlock(cls, repo):
1035 1035 return repo.locked
1036 1036
1037 1037 @property
1038 1038 def last_db_change(self):
1039 1039 return self.updated_on
1040 1040
1041 1041 def clone_url(self, **override):
1042 1042 from pylons import url
1043 1043 from urlparse import urlparse
1044 1044 import urllib
1045 1045 parsed_url = urlparse(url('home', qualified=True))
1046 1046 default_clone_uri = '%(scheme)s://%(user)s%(pass)s%(netloc)s%(prefix)s%(path)s'
1047 1047 decoded_path = safe_unicode(urllib.unquote(parsed_url.path))
1048 1048 args = {
1049 1049 'user': '',
1050 1050 'pass': '',
1051 1051 'scheme': parsed_url.scheme,
1052 1052 'netloc': parsed_url.netloc,
1053 1053 'prefix': decoded_path,
1054 1054 'path': self.repo_name
1055 1055 }
1056 1056
1057 1057 args.update(override)
1058 1058 return default_clone_uri % args
1059 1059
1060 1060 #==========================================================================
1061 1061 # SCM PROPERTIES
1062 1062 #==========================================================================
1063 1063
1064 1064 def get_changeset(self, rev=None):
1065 1065 return get_changeset_safe(self.scm_instance, rev)
1066 1066
1067 1067 def get_landing_changeset(self):
1068 1068 """
1069 1069 Returns landing changeset, or if that doesn't exist returns the tip
1070 1070 """
1071 1071 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
1072 1072 return cs
1073 1073
1074 1074 def update_changeset_cache(self, cs_cache=None):
1075 1075 """
1076 1076 Update cache of last changeset for repository, keys should be::
1077 1077
1078 1078 short_id
1079 1079 raw_id
1080 1080 revision
1081 1081 message
1082 1082 date
1083 1083 author
1084 1084
1085 1085 :param cs_cache:
1086 1086 """
1087 1087 from rhodecode.lib.vcs.backends.base import BaseChangeset
1088 1088 if cs_cache is None:
1089 1089 cs_cache = EmptyChangeset()
1090 1090 # use no-cache version here
1091 1091 scm_repo = self.scm_instance_no_cache()
1092 1092 if scm_repo:
1093 1093 cs_cache = scm_repo.get_changeset()
1094 1094
1095 1095 if isinstance(cs_cache, BaseChangeset):
1096 1096 cs_cache = cs_cache.__json__()
1097 1097
1098 1098 if (cs_cache != self.changeset_cache or not self.changeset_cache):
1099 1099 _default = datetime.datetime.fromtimestamp(0)
1100 1100 last_change = cs_cache.get('date') or _default
1101 1101 log.debug('updated repo %s with new cs cache %s'
1102 1102 % (self.repo_name, cs_cache))
1103 1103 self.updated_on = last_change
1104 1104 self.changeset_cache = cs_cache
1105 1105 Session().add(self)
1106 1106 Session().commit()
1107 1107 else:
1108 1108 log.debug('Skipping repo:%s already with latest changes'
1109 1109 % self.repo_name)
1110 1110
1111 1111 @property
1112 1112 def tip(self):
1113 1113 return self.get_changeset('tip')
1114 1114
1115 1115 @property
1116 1116 def author(self):
1117 1117 return self.tip.author
1118 1118
1119 1119 @property
1120 1120 def last_change(self):
1121 1121 return self.scm_instance.last_change
1122 1122
1123 1123 def get_comments(self, revisions=None):
1124 1124 """
1125 1125 Returns comments for this repository grouped by revisions
1126 1126
1127 1127 :param revisions: filter query by revisions only
1128 1128 """
1129 1129 cmts = ChangesetComment.query()\
1130 1130 .filter(ChangesetComment.repo == self)
1131 1131 if revisions:
1132 1132 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
1133 1133 grouped = collections.defaultdict(list)
1134 1134 for cmt in cmts.all():
1135 1135 grouped[cmt.revision].append(cmt)
1136 1136 return grouped
1137 1137
1138 1138 def statuses(self, revisions=None):
1139 1139 """
1140 1140 Returns statuses for this repository
1141 1141
1142 1142 :param revisions: list of revisions to get statuses for
1143 1143 """
1144 1144
1145 1145 statuses = ChangesetStatus.query()\
1146 1146 .filter(ChangesetStatus.repo == self)\
1147 1147 .filter(ChangesetStatus.version == 0)
1148 1148 if revisions:
1149 1149 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
1150 1150 grouped = {}
1151 1151
1152 1152 #maybe we have open new pullrequest without a status ?
1153 1153 stat = ChangesetStatus.STATUS_UNDER_REVIEW
1154 1154 status_lbl = ChangesetStatus.get_status_lbl(stat)
1155 1155 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
1156 1156 for rev in pr.revisions:
1157 1157 pr_id = pr.pull_request_id
1158 1158 pr_repo = pr.other_repo.repo_name
1159 1159 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
1160 1160
1161 1161 for stat in statuses.all():
1162 1162 pr_id = pr_repo = None
1163 1163 if stat.pull_request:
1164 1164 pr_id = stat.pull_request.pull_request_id
1165 1165 pr_repo = stat.pull_request.other_repo.repo_name
1166 1166 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
1167 1167 pr_id, pr_repo]
1168 1168 return grouped
1169 1169
1170 1170 def _repo_size(self):
1171 1171 from rhodecode.lib import helpers as h
1172 1172 log.debug('calculating repository size...')
1173 1173 return h.format_byte_size(self.scm_instance.size)
1174 1174
1175 1175 #==========================================================================
1176 1176 # SCM CACHE INSTANCE
1177 1177 #==========================================================================
1178 1178
1179 1179 def set_invalidate(self):
1180 1180 """
1181 1181 Mark caches of this repo as invalid.
1182 1182 """
1183 1183 CacheInvalidation.set_invalidate(self.repo_name)
1184 1184
1185 1185 def scm_instance_no_cache(self):
1186 1186 return self.__get_instance()
1187 1187
1188 1188 @property
1189 1189 def scm_instance(self):
1190 1190 import rhodecode
1191 1191 full_cache = str2bool(rhodecode.CONFIG.get('vcs_full_cache'))
1192 1192 if full_cache:
1193 1193 return self.scm_instance_cached()
1194 1194 return self.__get_instance()
1195 1195
1196 1196 def scm_instance_cached(self, valid_cache_keys=None):
1197 1197 @cache_region('long_term')
1198 1198 def _c(repo_name):
1199 1199 return self.__get_instance()
1200 1200 rn = self.repo_name
1201 1201
1202 1202 valid = CacheInvalidation.test_and_set_valid(rn, None, valid_cache_keys=valid_cache_keys)
1203 1203 if not valid:
1204 1204 log.debug('Cache for %s invalidated, getting new object' % (rn))
1205 1205 region_invalidate(_c, None, rn)
1206 1206 else:
1207 1207 log.debug('Getting obj for %s from cache' % (rn))
1208 1208 return _c(rn)
1209 1209
1210 1210 def __get_instance(self):
1211 1211 repo_full_path = self.repo_full_path
1212 1212 try:
1213 1213 alias = get_scm(repo_full_path)[0]
1214 1214 log.debug('Creating instance of %s repository from %s'
1215 1215 % (alias, repo_full_path))
1216 1216 backend = get_backend(alias)
1217 1217 except VCSError:
1218 1218 log.error(traceback.format_exc())
1219 1219 log.error('Perhaps this repository is in db and not in '
1220 1220 'filesystem run rescan repositories with '
1221 1221 '"destroy old data " option from admin panel')
1222 1222 return
1223 1223
1224 1224 if alias == 'hg':
1225 1225
1226 1226 repo = backend(safe_str(repo_full_path), create=False,
1227 1227 baseui=self._ui)
1228 1228 # skip hidden web repository
1229 1229 if repo._get_hidden():
1230 1230 return
1231 1231 else:
1232 1232 repo = backend(repo_full_path, create=False)
1233 1233
1234 1234 return repo
1235 1235
1236 1236
1237 1237 class RepoGroup(Base, BaseModel):
1238 1238 __tablename__ = 'groups'
1239 1239 __table_args__ = (
1240 1240 UniqueConstraint('group_name', 'group_parent_id'),
1241 1241 CheckConstraint('group_id != group_parent_id'),
1242 1242 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1243 1243 'mysql_charset': 'utf8'},
1244 1244 )
1245 1245 __mapper_args__ = {'order_by': 'group_name'}
1246 1246
1247 1247 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1248 1248 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
1249 1249 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
1250 1250 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1251 1251 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
1252 1252 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
1253 1253
1254 1254 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
1255 1255 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
1256 1256 parent_group = relationship('RepoGroup', remote_side=group_id)
1257 1257 user = relationship('User')
1258 1258
1259 1259 def __init__(self, group_name='', parent_group=None):
1260 1260 self.group_name = group_name
1261 1261 self.parent_group = parent_group
1262 1262
1263 1263 def __unicode__(self):
1264 1264 return u"<%s('id:%s:%s')>" % (self.__class__.__name__, self.group_id,
1265 1265 self.group_name)
1266 1266
1267 1267 @classmethod
1268 1268 def groups_choices(cls, groups=None, show_empty_group=True):
1269 1269 from webhelpers.html import literal as _literal
1270 1270 if not groups:
1271 1271 groups = cls.query().all()
1272 1272
1273 1273 repo_groups = []
1274 1274 if show_empty_group:
1275 1275 repo_groups = [('-1', u'-- %s --' % _('top level'))]
1276 1276 sep = ' &raquo; '
1277 1277 _name = lambda k: _literal(sep.join(k))
1278 1278
1279 1279 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
1280 1280 for x in groups])
1281 1281
1282 1282 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
1283 1283 return repo_groups
1284 1284
1285 1285 @classmethod
1286 1286 def url_sep(cls):
1287 1287 return URL_SEP
1288 1288
1289 1289 @classmethod
1290 1290 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
1291 1291 if case_insensitive:
1292 1292 gr = cls.query()\
1293 1293 .filter(cls.group_name.ilike(group_name))
1294 1294 else:
1295 1295 gr = cls.query()\
1296 1296 .filter(cls.group_name == group_name)
1297 1297 if cache:
1298 1298 gr = gr.options(FromCache(
1299 1299 "sql_cache_short",
1300 1300 "get_group_%s" % _hash_key(group_name)
1301 1301 )
1302 1302 )
1303 1303 return gr.scalar()
1304 1304
1305 1305 @property
1306 1306 def parents(self):
1307 1307 parents_recursion_limit = 5
1308 1308 groups = []
1309 1309 if self.parent_group is None:
1310 1310 return groups
1311 1311 cur_gr = self.parent_group
1312 1312 groups.insert(0, cur_gr)
1313 1313 cnt = 0
1314 1314 while 1:
1315 1315 cnt += 1
1316 1316 gr = getattr(cur_gr, 'parent_group', None)
1317 1317 cur_gr = cur_gr.parent_group
1318 1318 if gr is None:
1319 1319 break
1320 1320 if cnt == parents_recursion_limit:
1321 1321 # this will prevent accidental infinit loops
1322 1322 log.error('group nested more than %s' %
1323 1323 parents_recursion_limit)
1324 1324 break
1325 1325
1326 1326 groups.insert(0, gr)
1327 1327 return groups
1328 1328
1329 1329 @property
1330 1330 def children(self):
1331 1331 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1332 1332
1333 1333 @property
1334 1334 def name(self):
1335 1335 return self.group_name.split(RepoGroup.url_sep())[-1]
1336 1336
1337 1337 @property
1338 1338 def full_path(self):
1339 1339 return self.group_name
1340 1340
1341 1341 @property
1342 1342 def full_path_splitted(self):
1343 1343 return self.group_name.split(RepoGroup.url_sep())
1344 1344
1345 1345 @property
1346 1346 def repositories(self):
1347 1347 return Repository.query()\
1348 1348 .filter(Repository.group == self)\
1349 1349 .order_by(Repository.repo_name)
1350 1350
1351 1351 @property
1352 1352 def repositories_recursive_count(self):
1353 1353 cnt = self.repositories.count()
1354 1354
1355 1355 def children_count(group):
1356 1356 cnt = 0
1357 1357 for child in group.children:
1358 1358 cnt += child.repositories.count()
1359 1359 cnt += children_count(child)
1360 1360 return cnt
1361 1361
1362 1362 return cnt + children_count(self)
1363 1363
1364 1364 def _recursive_objects(self, include_repos=True):
1365 1365 all_ = []
1366 1366
1367 1367 def _get_members(root_gr):
1368 1368 if include_repos:
1369 1369 for r in root_gr.repositories:
1370 1370 all_.append(r)
1371 1371 childs = root_gr.children.all()
1372 1372 if childs:
1373 1373 for gr in childs:
1374 1374 all_.append(gr)
1375 1375 _get_members(gr)
1376 1376
1377 1377 _get_members(self)
1378 1378 return [self] + all_
1379 1379
1380 1380 def recursive_groups_and_repos(self):
1381 1381 """
1382 1382 Recursive return all groups, with repositories in those groups
1383 1383 """
1384 1384 return self._recursive_objects()
1385 1385
1386 1386 def recursive_groups(self):
1387 1387 """
1388 1388 Returns all children groups for this group including children of children
1389 1389 """
1390 1390 return self._recursive_objects(include_repos=False)
1391 1391
1392 1392 def get_new_name(self, group_name):
1393 1393 """
1394 1394 returns new full group name based on parent and new name
1395 1395
1396 1396 :param group_name:
1397 1397 """
1398 1398 path_prefix = (self.parent_group.full_path_splitted if
1399 1399 self.parent_group else [])
1400 1400 return RepoGroup.url_sep().join(path_prefix + [group_name])
1401 1401
1402 1402
1403 1403 class Permission(Base, BaseModel):
1404 1404 __tablename__ = 'permissions'
1405 1405 __table_args__ = (
1406 1406 Index('p_perm_name_idx', 'permission_name'),
1407 1407 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1408 1408 'mysql_charset': 'utf8'},
1409 1409 )
1410 1410 PERMS = [
1411 1411 ('hg.admin', _('RhodeCode Administrator')),
1412 1412
1413 1413 ('repository.none', _('Repository no access')),
1414 1414 ('repository.read', _('Repository read access')),
1415 1415 ('repository.write', _('Repository write access')),
1416 1416 ('repository.admin', _('Repository admin access')),
1417 1417
1418 1418 ('group.none', _('Repository group no access')),
1419 1419 ('group.read', _('Repository group read access')),
1420 1420 ('group.write', _('Repository group write access')),
1421 1421 ('group.admin', _('Repository group admin access')),
1422 1422
1423 1423 ('usergroup.none', _('User group no access')),
1424 1424 ('usergroup.read', _('User group read access')),
1425 1425 ('usergroup.write', _('User group write access')),
1426 1426 ('usergroup.admin', _('User group admin access')),
1427 1427
1428 1428 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
1429 1429 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
1430 1430
1431 1431 ('hg.usergroup.create.false', _('User Group creation disabled')),
1432 1432 ('hg.usergroup.create.true', _('User Group creation enabled')),
1433 1433
1434 1434 ('hg.create.none', _('Repository creation disabled')),
1435 1435 ('hg.create.repository', _('Repository creation enabled')),
1436 1436
1437 1437 ('hg.fork.none', _('Repository forking disabled')),
1438 1438 ('hg.fork.repository', _('Repository forking enabled')),
1439 1439
1440 1440 ('hg.register.none', _('Registration disabled')),
1441 1441 ('hg.register.manual_activate', _('User Registration with manual account activation')),
1442 1442 ('hg.register.auto_activate', _('User Registration with automatic account activation')),
1443 1443
1444 1444 ('hg.extern_activate.manual', _('Manual activation of external account')),
1445 1445 ('hg.extern_activate.auto', _('Automatic activation of external account')),
1446 1446
1447 1447 ]
1448 1448
1449 1449 #definition of system default permissions for DEFAULT user
1450 1450 DEFAULT_USER_PERMISSIONS = [
1451 1451 'repository.read',
1452 1452 'group.read',
1453 1453 'usergroup.read',
1454 1454 'hg.create.repository',
1455 1455 'hg.fork.repository',
1456 1456 'hg.register.manual_activate',
1457 1457 'hg.extern_activate.auto',
1458 1458 ]
1459 1459
1460 1460 # defines which permissions are more important higher the more important
1461 1461 # Weight defines which permissions are more important.
1462 1462 # The higher number the more important.
1463 1463 PERM_WEIGHTS = {
1464 1464 'repository.none': 0,
1465 1465 'repository.read': 1,
1466 1466 'repository.write': 3,
1467 1467 'repository.admin': 4,
1468 1468
1469 1469 'group.none': 0,
1470 1470 'group.read': 1,
1471 1471 'group.write': 3,
1472 1472 'group.admin': 4,
1473 1473
1474 1474 'usergroup.none': 0,
1475 1475 'usergroup.read': 1,
1476 1476 'usergroup.write': 3,
1477 1477 'usergroup.admin': 4,
1478 1478 'hg.repogroup.create.false': 0,
1479 1479 'hg.repogroup.create.true': 1,
1480 1480
1481 1481 'hg.usergroup.create.false': 0,
1482 1482 'hg.usergroup.create.true': 1,
1483 1483
1484 1484 'hg.fork.none': 0,
1485 1485 'hg.fork.repository': 1,
1486 1486 'hg.create.none': 0,
1487 1487 'hg.create.repository': 1
1488 1488 }
1489 1489
1490 1490 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1491 1491 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1492 1492 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1493 1493
1494 1494 def __unicode__(self):
1495 1495 return u"<%s('%s:%s')>" % (
1496 1496 self.__class__.__name__, self.permission_id, self.permission_name
1497 1497 )
1498 1498
1499 1499 @classmethod
1500 1500 def get_by_key(cls, key):
1501 1501 return cls.query().filter(cls.permission_name == key).scalar()
1502 1502
1503 1503 @classmethod
1504 1504 def get_default_perms(cls, default_user_id):
1505 1505 q = Session().query(UserRepoToPerm, Repository, cls)\
1506 1506 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1507 1507 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1508 1508 .filter(UserRepoToPerm.user_id == default_user_id)
1509 1509
1510 1510 return q.all()
1511 1511
1512 1512 @classmethod
1513 1513 def get_default_group_perms(cls, default_user_id):
1514 1514 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1515 1515 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1516 1516 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1517 1517 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1518 1518
1519 1519 return q.all()
1520 1520
1521 1521 @classmethod
1522 1522 def get_default_user_group_perms(cls, default_user_id):
1523 1523 q = Session().query(UserUserGroupToPerm, UserGroup, cls)\
1524 1524 .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id))\
1525 1525 .join((cls, UserUserGroupToPerm.permission_id == cls.permission_id))\
1526 1526 .filter(UserUserGroupToPerm.user_id == default_user_id)
1527 1527
1528 1528 return q.all()
1529 1529
1530 1530
1531 1531 class UserRepoToPerm(Base, BaseModel):
1532 1532 __tablename__ = 'repo_to_perm'
1533 1533 __table_args__ = (
1534 1534 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1535 1535 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1536 1536 'mysql_charset': 'utf8'}
1537 1537 )
1538 1538 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1539 1539 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1540 1540 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1541 1541 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1542 1542
1543 1543 user = relationship('User')
1544 1544 repository = relationship('Repository')
1545 1545 permission = relationship('Permission')
1546 1546
1547 1547 @classmethod
1548 1548 def create(cls, user, repository, permission):
1549 1549 n = cls()
1550 1550 n.user = user
1551 1551 n.repository = repository
1552 1552 n.permission = permission
1553 1553 Session().add(n)
1554 1554 return n
1555 1555
1556 1556 def __unicode__(self):
1557 1557 return u'<%s => %s >' % (self.user, self.repository)
1558 1558
1559 1559
1560 1560 class UserUserGroupToPerm(Base, BaseModel):
1561 1561 __tablename__ = 'user_user_group_to_perm'
1562 1562 __table_args__ = (
1563 1563 UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
1564 1564 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1565 1565 'mysql_charset': 'utf8'}
1566 1566 )
1567 1567 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1568 1568 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1569 1569 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1570 1570 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1571 1571
1572 1572 user = relationship('User')
1573 1573 user_group = relationship('UserGroup')
1574 1574 permission = relationship('Permission')
1575 1575
1576 1576 @classmethod
1577 1577 def create(cls, user, user_group, permission):
1578 1578 n = cls()
1579 1579 n.user = user
1580 1580 n.user_group = user_group
1581 1581 n.permission = permission
1582 1582 Session().add(n)
1583 1583 return n
1584 1584
1585 1585 def __unicode__(self):
1586 1586 return u'<%s => %s >' % (self.user, self.user_group)
1587 1587
1588 1588
1589 1589 class UserToPerm(Base, BaseModel):
1590 1590 __tablename__ = 'user_to_perm'
1591 1591 __table_args__ = (
1592 1592 UniqueConstraint('user_id', 'permission_id'),
1593 1593 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1594 1594 'mysql_charset': 'utf8'}
1595 1595 )
1596 1596 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1597 1597 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1598 1598 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1599 1599
1600 1600 user = relationship('User')
1601 1601 permission = relationship('Permission', lazy='joined')
1602 1602
1603 1603 def __unicode__(self):
1604 1604 return u'<%s => %s >' % (self.user, self.permission)
1605 1605
1606 1606
1607 1607 class UserGroupRepoToPerm(Base, BaseModel):
1608 1608 __tablename__ = 'users_group_repo_to_perm'
1609 1609 __table_args__ = (
1610 1610 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1611 1611 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1612 1612 'mysql_charset': 'utf8'}
1613 1613 )
1614 1614 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1615 1615 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1616 1616 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1617 1617 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1618 1618
1619 1619 users_group = relationship('UserGroup')
1620 1620 permission = relationship('Permission')
1621 1621 repository = relationship('Repository')
1622 1622
1623 1623 @classmethod
1624 1624 def create(cls, users_group, repository, permission):
1625 1625 n = cls()
1626 1626 n.users_group = users_group
1627 1627 n.repository = repository
1628 1628 n.permission = permission
1629 1629 Session().add(n)
1630 1630 return n
1631 1631
1632 1632 def __unicode__(self):
1633 1633 return u'<UserGroupRepoToPerm:%s => %s >' % (self.users_group, self.repository)
1634 1634
1635 1635
1636 1636 class UserGroupUserGroupToPerm(Base, BaseModel):
1637 1637 __tablename__ = 'user_group_user_group_to_perm'
1638 1638 __table_args__ = (
1639 1639 UniqueConstraint('target_user_group_id', 'user_group_id', 'permission_id'),
1640 1640 CheckConstraint('target_user_group_id != user_group_id'),
1641 1641 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1642 1642 'mysql_charset': 'utf8'}
1643 1643 )
1644 1644 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)
1645 1645 target_user_group_id = Column("target_user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1646 1646 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1647 1647 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1648 1648
1649 1649 target_user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
1650 1650 user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
1651 1651 permission = relationship('Permission')
1652 1652
1653 1653 @classmethod
1654 1654 def create(cls, target_user_group, user_group, permission):
1655 1655 n = cls()
1656 1656 n.target_user_group = target_user_group
1657 1657 n.user_group = user_group
1658 1658 n.permission = permission
1659 1659 Session().add(n)
1660 1660 return n
1661 1661
1662 1662 def __unicode__(self):
1663 1663 return u'<UserGroupUserGroup:%s => %s >' % (self.target_user_group, self.user_group)
1664 1664
1665 1665
1666 1666 class UserGroupToPerm(Base, BaseModel):
1667 1667 __tablename__ = 'users_group_to_perm'
1668 1668 __table_args__ = (
1669 1669 UniqueConstraint('users_group_id', 'permission_id',),
1670 1670 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1671 1671 'mysql_charset': 'utf8'}
1672 1672 )
1673 1673 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1674 1674 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1675 1675 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1676 1676
1677 1677 users_group = relationship('UserGroup')
1678 1678 permission = relationship('Permission')
1679 1679
1680 1680
1681 1681 class UserRepoGroupToPerm(Base, BaseModel):
1682 1682 __tablename__ = 'user_repo_group_to_perm'
1683 1683 __table_args__ = (
1684 1684 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1685 1685 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1686 1686 'mysql_charset': 'utf8'}
1687 1687 )
1688 1688
1689 1689 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1690 1690 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1691 1691 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1692 1692 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1693 1693
1694 1694 user = relationship('User')
1695 1695 group = relationship('RepoGroup')
1696 1696 permission = relationship('Permission')
1697 1697
1698 1698
1699 1699 class UserGroupRepoGroupToPerm(Base, BaseModel):
1700 1700 __tablename__ = 'users_group_repo_group_to_perm'
1701 1701 __table_args__ = (
1702 1702 UniqueConstraint('users_group_id', 'group_id'),
1703 1703 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1704 1704 'mysql_charset': 'utf8'}
1705 1705 )
1706 1706
1707 1707 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)
1708 1708 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1709 1709 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1710 1710 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1711 1711
1712 1712 users_group = relationship('UserGroup')
1713 1713 permission = relationship('Permission')
1714 1714 group = relationship('RepoGroup')
1715 1715
1716 1716
1717 1717 class Statistics(Base, BaseModel):
1718 1718 __tablename__ = 'statistics'
1719 1719 __table_args__ = (
1720 1720 UniqueConstraint('repository_id'),
1721 1721 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1722 1722 'mysql_charset': 'utf8'}
1723 1723 )
1724 1724 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1725 1725 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1726 1726 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1727 1727 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1728 1728 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1729 1729 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1730 1730
1731 1731 repository = relationship('Repository', single_parent=True)
1732 1732
1733 1733
1734 1734 class UserFollowing(Base, BaseModel):
1735 1735 __tablename__ = 'user_followings'
1736 1736 __table_args__ = (
1737 1737 UniqueConstraint('user_id', 'follows_repository_id'),
1738 1738 UniqueConstraint('user_id', 'follows_user_id'),
1739 1739 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1740 1740 'mysql_charset': 'utf8'}
1741 1741 )
1742 1742
1743 1743 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1744 1744 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1745 1745 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1746 1746 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1747 1747 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1748 1748
1749 1749 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1750 1750
1751 1751 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1752 1752 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1753 1753
1754 1754 @classmethod
1755 1755 def get_repo_followers(cls, repo_id):
1756 1756 return cls.query().filter(cls.follows_repo_id == repo_id)
1757 1757
1758 1758
1759 1759 class CacheInvalidation(Base, BaseModel):
1760 1760 __tablename__ = 'cache_invalidation'
1761 1761 __table_args__ = (
1762 1762 UniqueConstraint('cache_key'),
1763 1763 Index('key_idx', 'cache_key'),
1764 1764 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1765 1765 'mysql_charset': 'utf8'},
1766 1766 )
1767 1767 # cache_id, not used
1768 1768 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1769 1769 # cache_key as created by _get_cache_key
1770 1770 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1771 1771 # cache_args is a repo_name
1772 1772 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1773 1773 # instance sets cache_active True when it is caching,
1774 1774 # other instances set cache_active to False to indicate that this cache is invalid
1775 1775 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1776 1776
1777 1777 def __init__(self, cache_key, repo_name=''):
1778 1778 self.cache_key = cache_key
1779 1779 self.cache_args = repo_name
1780 1780 self.cache_active = False
1781 1781
1782 1782 def __unicode__(self):
1783 1783 return u"<%s('%s:%s[%s]')>" % (self.__class__.__name__,
1784 1784 self.cache_id, self.cache_key, self.cache_active)
1785 1785
1786 1786 def _cache_key_partition(self):
1787 1787 prefix, repo_name, suffix = self.cache_key.partition(self.cache_args)
1788 1788 return prefix, repo_name, suffix
1789 1789
1790 1790 def get_prefix(self):
1791 1791 """
1792 1792 get prefix that might have been used in _get_cache_key to
1793 1793 generate self.cache_key. Only used for informational purposes
1794 1794 in repo_edit.html.
1795 1795 """
1796 1796 # prefix, repo_name, suffix
1797 1797 return self._cache_key_partition()[0]
1798 1798
1799 1799 def get_suffix(self):
1800 1800 """
1801 1801 get suffix that might have been used in _get_cache_key to
1802 1802 generate self.cache_key. Only used for informational purposes
1803 1803 in repo_edit.html.
1804 1804 """
1805 1805 # prefix, repo_name, suffix
1806 1806 return self._cache_key_partition()[2]
1807 1807
1808 1808 @classmethod
1809 1809 def clear_cache(cls):
1810 1810 """
1811 1811 Delete all cache keys from database.
1812 1812 Should only be run when all instances are down and all entries thus stale.
1813 1813 """
1814 1814 cls.query().delete()
1815 1815 Session().commit()
1816 1816
1817 1817 @classmethod
1818 1818 def _get_cache_key(cls, key):
1819 1819 """
1820 1820 Wrapper for generating a unique cache key for this instance and "key".
1821 1821 key must / will start with a repo_name which will be stored in .cache_args .
1822 1822 """
1823 1823 import rhodecode
1824 1824 prefix = rhodecode.CONFIG.get('instance_id', '')
1825 1825 return "%s%s" % (prefix, key)
1826 1826
1827 1827 @classmethod
1828 1828 def set_invalidate(cls, repo_name):
1829 1829 """
1830 1830 Mark all caches of a repo as invalid in the database.
1831 1831 """
1832 1832 inv_objs = Session().query(cls).filter(cls.cache_args == repo_name).all()
1833 1833
1834 1834 try:
1835 1835 for inv_obj in inv_objs:
1836 1836 log.debug('marking %s key for invalidation based on repo_name=%s'
1837 1837 % (inv_obj, safe_str(repo_name)))
1838 1838 inv_obj.cache_active = False
1839 1839 Session().add(inv_obj)
1840 1840 Session().commit()
1841 1841 except Exception:
1842 1842 log.error(traceback.format_exc())
1843 1843 Session().rollback()
1844 1844
1845 1845 @classmethod
1846 1846 def test_and_set_valid(cls, repo_name, kind, valid_cache_keys=None):
1847 1847 """
1848 1848 Mark this cache key as active and currently cached.
1849 1849 Return True if the existing cache registration still was valid.
1850 1850 Return False to indicate that it had been invalidated and caches should be refreshed.
1851 1851 """
1852 1852
1853 1853 key = (repo_name + '_' + kind) if kind else repo_name
1854 1854 cache_key = cls._get_cache_key(key)
1855 1855
1856 1856 if valid_cache_keys and cache_key in valid_cache_keys:
1857 1857 return True
1858 1858
1859 1859 try:
1860 1860 inv_obj = cls.query().filter(cls.cache_key == cache_key).scalar()
1861 1861 if not inv_obj:
1862 1862 inv_obj = CacheInvalidation(cache_key, repo_name)
1863 1863 was_valid = inv_obj.cache_active
1864 1864 inv_obj.cache_active = True
1865 1865 Session().add(inv_obj)
1866 1866 Session().commit()
1867 1867 return was_valid
1868 1868 except Exception:
1869 1869 log.error(traceback.format_exc())
1870 1870 Session().rollback()
1871 1871 return False
1872 1872
1873 1873 @classmethod
1874 1874 def get_valid_cache_keys(cls):
1875 1875 """
1876 1876 Return opaque object with information of which caches still are valid
1877 1877 and can be used without checking for invalidation.
1878 1878 """
1879 1879 return set(inv_obj.cache_key for inv_obj in cls.query().filter(cls.cache_active).all())
1880 1880
1881 1881
1882 1882 class ChangesetComment(Base, BaseModel):
1883 1883 __tablename__ = 'changeset_comments'
1884 1884 __table_args__ = (
1885 1885 Index('cc_revision_idx', 'revision'),
1886 1886 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1887 1887 'mysql_charset': 'utf8'},
1888 1888 )
1889 1889 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1890 1890 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1891 1891 revision = Column('revision', String(40), nullable=True)
1892 1892 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1893 1893 line_no = Column('line_no', Unicode(10), nullable=True)
1894 1894 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1895 1895 f_path = Column('f_path', Unicode(1000), nullable=True)
1896 1896 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1897 1897 text = Column('text', UnicodeText(25000), nullable=False)
1898 1898 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1899 1899 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1900 1900
1901 1901 author = relationship('User', lazy='joined')
1902 1902 repo = relationship('Repository')
1903 1903 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1904 1904 pull_request = relationship('PullRequest', lazy='joined')
1905 1905
1906 1906 @classmethod
1907 1907 def get_users(cls, revision=None, pull_request_id=None):
1908 1908 """
1909 1909 Returns user associated with this ChangesetComment. ie those
1910 1910 who actually commented
1911 1911
1912 1912 :param cls:
1913 1913 :param revision:
1914 1914 """
1915 1915 q = Session().query(User)\
1916 1916 .join(ChangesetComment.author)
1917 1917 if revision:
1918 1918 q = q.filter(cls.revision == revision)
1919 1919 elif pull_request_id:
1920 1920 q = q.filter(cls.pull_request_id == pull_request_id)
1921 1921 return q.all()
1922 1922
1923 1923
1924 1924 class ChangesetStatus(Base, BaseModel):
1925 1925 __tablename__ = 'changeset_statuses'
1926 1926 __table_args__ = (
1927 1927 Index('cs_revision_idx', 'revision'),
1928 1928 Index('cs_version_idx', 'version'),
1929 1929 UniqueConstraint('repo_id', 'revision', 'version'),
1930 1930 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1931 1931 'mysql_charset': 'utf8'}
1932 1932 )
1933 1933 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1934 1934 STATUS_APPROVED = 'approved'
1935 1935 STATUS_REJECTED = 'rejected'
1936 1936 STATUS_UNDER_REVIEW = 'under_review'
1937 1937
1938 1938 STATUSES = [
1939 1939 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1940 1940 (STATUS_APPROVED, _("Approved")),
1941 1941 (STATUS_REJECTED, _("Rejected")),
1942 1942 (STATUS_UNDER_REVIEW, _("Under Review")),
1943 1943 ]
1944 1944
1945 1945 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1946 1946 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1947 1947 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1948 1948 revision = Column('revision', String(40), nullable=False)
1949 1949 status = Column('status', String(128), nullable=False, default=DEFAULT)
1950 1950 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1951 1951 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1952 1952 version = Column('version', Integer(), nullable=False, default=0)
1953 1953 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1954 1954
1955 1955 author = relationship('User', lazy='joined')
1956 1956 repo = relationship('Repository')
1957 1957 comment = relationship('ChangesetComment', lazy='joined')
1958 1958 pull_request = relationship('PullRequest', lazy='joined')
1959 1959
1960 1960 def __unicode__(self):
1961 1961 return u"<%s('%s:%s')>" % (
1962 1962 self.__class__.__name__,
1963 1963 self.status, self.author
1964 1964 )
1965 1965
1966 1966 @classmethod
1967 1967 def get_status_lbl(cls, value):
1968 1968 return dict(cls.STATUSES).get(value)
1969 1969
1970 1970 @property
1971 1971 def status_lbl(self):
1972 1972 return ChangesetStatus.get_status_lbl(self.status)
1973 1973
1974 1974
1975 1975 class PullRequest(Base, BaseModel):
1976 1976 __tablename__ = 'pull_requests'
1977 1977 __table_args__ = (
1978 1978 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1979 1979 'mysql_charset': 'utf8'},
1980 1980 )
1981 1981
1982 1982 STATUS_NEW = u'new'
1983 1983 STATUS_OPEN = u'open'
1984 1984 STATUS_CLOSED = u'closed'
1985 1985
1986 1986 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1987 1987 title = Column('title', Unicode(256), nullable=True)
1988 1988 description = Column('description', UnicodeText(10240), nullable=True)
1989 1989 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1990 1990 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1991 1991 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1992 1992 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1993 1993 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1994 1994 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1995 1995 org_ref = Column('org_ref', Unicode(256), nullable=False)
1996 1996 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1997 1997 other_ref = Column('other_ref', Unicode(256), nullable=False)
1998 1998
1999 1999 @hybrid_property
2000 2000 def revisions(self):
2001 2001 return self._revisions.split(':')
2002 2002
2003 2003 @revisions.setter
2004 2004 def revisions(self, val):
2005 2005 self._revisions = ':'.join(val)
2006 2006
2007 2007 @property
2008 2008 def org_ref_parts(self):
2009 2009 return self.org_ref.split(':')
2010 2010
2011 2011 @property
2012 2012 def other_ref_parts(self):
2013 2013 return self.other_ref.split(':')
2014 2014
2015 2015 author = relationship('User', lazy='joined')
2016 2016 reviewers = relationship('PullRequestReviewers',
2017 2017 cascade="all, delete, delete-orphan")
2018 2018 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
2019 2019 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
2020 2020 statuses = relationship('ChangesetStatus')
2021 2021 comments = relationship('ChangesetComment',
2022 2022 cascade="all, delete, delete-orphan")
2023 2023
2024 2024 def is_closed(self):
2025 2025 return self.status == self.STATUS_CLOSED
2026 2026
2027 2027 @property
2028 2028 def last_review_status(self):
2029 2029 return self.statuses[-1].status if self.statuses else ''
2030 2030
2031 2031 def __json__(self):
2032 2032 return dict(
2033 2033 revisions=self.revisions
2034 2034 )
2035 2035
2036 2036
2037 2037 class PullRequestReviewers(Base, BaseModel):
2038 2038 __tablename__ = 'pull_request_reviewers'
2039 2039 __table_args__ = (
2040 2040 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2041 2041 'mysql_charset': 'utf8'},
2042 2042 )
2043 2043
2044 2044 def __init__(self, user=None, pull_request=None):
2045 2045 self.user = user
2046 2046 self.pull_request = pull_request
2047 2047
2048 2048 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
2049 2049 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
2050 2050 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
2051 2051
2052 2052 user = relationship('User')
2053 2053 pull_request = relationship('PullRequest')
2054 2054
2055 2055
2056 2056 class Notification(Base, BaseModel):
2057 2057 __tablename__ = 'notifications'
2058 2058 __table_args__ = (
2059 2059 Index('notification_type_idx', 'type'),
2060 2060 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2061 2061 'mysql_charset': 'utf8'},
2062 2062 )
2063 2063
2064 2064 TYPE_CHANGESET_COMMENT = u'cs_comment'
2065 2065 TYPE_MESSAGE = u'message'
2066 2066 TYPE_MENTION = u'mention'
2067 2067 TYPE_REGISTRATION = u'registration'
2068 2068 TYPE_PULL_REQUEST = u'pull_request'
2069 2069 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
2070 2070
2071 2071 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
2072 2072 subject = Column('subject', Unicode(512), nullable=True)
2073 2073 body = Column('body', UnicodeText(50000), nullable=True)
2074 2074 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
2075 2075 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
2076 2076 type_ = Column('type', Unicode(256))
2077 2077
2078 2078 created_by_user = relationship('User')
2079 2079 notifications_to_users = relationship('UserNotification', lazy='joined',
2080 2080 cascade="all, delete, delete-orphan")
2081 2081
2082 2082 @property
2083 2083 def recipients(self):
2084 2084 return [x.user for x in UserNotification.query()\
2085 2085 .filter(UserNotification.notification == self)\
2086 2086 .order_by(UserNotification.user_id.asc()).all()]
2087 2087
2088 2088 @classmethod
2089 2089 def create(cls, created_by, subject, body, recipients, type_=None):
2090 2090 if type_ is None:
2091 2091 type_ = Notification.TYPE_MESSAGE
2092 2092
2093 2093 notification = cls()
2094 2094 notification.created_by_user = created_by
2095 2095 notification.subject = subject
2096 2096 notification.body = body
2097 2097 notification.type_ = type_
2098 2098 notification.created_on = datetime.datetime.now()
2099 2099
2100 2100 for u in recipients:
2101 2101 assoc = UserNotification()
2102 2102 assoc.notification = notification
2103 2103 u.notifications.append(assoc)
2104 2104 Session().add(notification)
2105 2105 return notification
2106 2106
2107 2107 @property
2108 2108 def description(self):
2109 2109 from rhodecode.model.notification import NotificationModel
2110 2110 return NotificationModel().make_description(self)
2111 2111
2112 2112
2113 2113 class UserNotification(Base, BaseModel):
2114 2114 __tablename__ = 'user_to_notification'
2115 2115 __table_args__ = (
2116 2116 UniqueConstraint('user_id', 'notification_id'),
2117 2117 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2118 2118 'mysql_charset': 'utf8'}
2119 2119 )
2120 2120 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
2121 2121 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
2122 2122 read = Column('read', Boolean, default=False)
2123 2123 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
2124 2124
2125 2125 user = relationship('User', lazy="joined")
2126 2126 notification = relationship('Notification', lazy="joined",
2127 2127 order_by=lambda: Notification.created_on.desc(),)
2128 2128
2129 2129 def mark_as_read(self):
2130 2130 self.read = True
2131 2131 Session().add(self)
2132 2132
2133 2133
2134 2134 class Gist(Base, BaseModel):
2135 2135 __tablename__ = 'gists'
2136 2136 __table_args__ = (
2137 2137 Index('g_gist_access_id_idx', 'gist_access_id'),
2138 2138 Index('g_created_on_idx', 'created_on'),
2139 2139 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2140 2140 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2141 2141 )
2142 2142 GIST_PUBLIC = u'public'
2143 2143 GIST_PRIVATE = u'private'
2144 2144
2145 2145 gist_id = Column('gist_id', Integer(), primary_key=True)
2146 2146 gist_access_id = Column('gist_access_id', Unicode(250))
2147 2147 gist_description = Column('gist_description', UnicodeText(1024))
2148 2148 gist_owner = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=True)
2149 gist_expires = Column('gist_expires', Float(), nullable=False)
2149 gist_expires = Column('gist_expires', Float(53), nullable=False)
2150 2150 gist_type = Column('gist_type', Unicode(128), nullable=False)
2151 2151 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
2152 2152 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
2153 2153
2154 2154 owner = relationship('User')
2155 2155
2156 2156 @classmethod
2157 2157 def get_or_404(cls, id_):
2158 2158 res = cls.query().filter(cls.gist_access_id == id_).scalar()
2159 2159 if not res:
2160 2160 raise HTTPNotFound
2161 2161 return res
2162 2162
2163 2163 @classmethod
2164 2164 def get_by_access_id(cls, gist_access_id):
2165 2165 return cls.query().filter(cls.gist_access_id == gist_access_id).scalar()
2166 2166
2167 2167 def gist_url(self):
2168 2168 import rhodecode
2169 2169 alias_url = rhodecode.CONFIG.get('gist_alias_url')
2170 2170 if alias_url:
2171 2171 return alias_url.replace('{gistid}', self.gist_access_id)
2172 2172
2173 2173 from pylons import url
2174 2174 return url('gist', gist_id=self.gist_access_id, qualified=True)
2175 2175
2176 2176 @classmethod
2177 2177 def base_path(cls):
2178 2178 """
2179 2179 Returns base path when all gists are stored
2180 2180
2181 2181 :param cls:
2182 2182 """
2183 2183 from rhodecode.model.gist import GIST_STORE_LOC
2184 2184 q = Session().query(RhodeCodeUi)\
2185 2185 .filter(RhodeCodeUi.ui_key == URL_SEP)
2186 2186 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
2187 2187 return os.path.join(q.one().ui_value, GIST_STORE_LOC)
2188 2188
2189 2189 def get_api_data(self):
2190 2190 """
2191 2191 Common function for generating gist related data for API
2192 2192 """
2193 2193 gist = self
2194 2194 data = dict(
2195 2195 gist_id=gist.gist_id,
2196 2196 type=gist.gist_type,
2197 2197 access_id=gist.gist_access_id,
2198 2198 description=gist.gist_description,
2199 2199 url=gist.gist_url(),
2200 2200 expires=gist.gist_expires,
2201 2201 created_on=gist.created_on,
2202 2202 )
2203 2203 return data
2204 2204
2205 2205 def __json__(self):
2206 2206 data = dict(
2207 2207 )
2208 2208 data.update(self.get_api_data())
2209 2209 return data
2210 2210 ## SCM functions
2211 2211
2212 2212 @property
2213 2213 def scm_instance(self):
2214 2214 from rhodecode.lib.vcs import get_repo
2215 2215 base_path = self.base_path()
2216 2216 return get_repo(os.path.join(*map(safe_str,
2217 2217 [base_path, self.gist_access_id])))
2218 2218
2219 2219
2220 2220 class DbMigrateVersion(Base, BaseModel):
2221 2221 __tablename__ = 'db_migrate_version'
2222 2222 __table_args__ = (
2223 2223 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2224 2224 'mysql_charset': 'utf8'},
2225 2225 )
2226 2226 repository_id = Column('repository_id', String(250), primary_key=True)
2227 2227 repository_path = Column('repository_path', Text)
2228 2228 version = Column('version', Integer)
General Comments 0
You need to be logged in to leave comments. Login now