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