##// END OF EJS Templates
fixes #245 Rescan of the repositories on Windows
marcink -
r1554:e7c6341a beta
parent child Browse files
Show More
@@ -1,1040 +1,1046 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) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import os
27 27 import logging
28 28 import datetime
29 29 import traceback
30 30 from datetime import date
31 31
32 32 from sqlalchemy import *
33 33 from sqlalchemy.exc import DatabaseError
34 34 from sqlalchemy.ext.hybrid import hybrid_property
35 35 from sqlalchemy.orm import relationship, backref, joinedload, class_mapper
36 36 from sqlalchemy.orm.interfaces import MapperExtension
37 37
38 38 from beaker.cache import cache_region, region_invalidate
39 39
40 40 from vcs import get_backend
41 41 from vcs.utils.helpers import get_scm
42 42 from vcs.exceptions import VCSError
43 43 from vcs.utils.lazy import LazyProperty
44 44
45 45 from rhodecode.lib import str2bool, safe_str, get_changeset_safe, \
46 46 generate_api_key, safe_unicode
47 47 from rhodecode.lib.exceptions import UsersGroupsAssignedException
48 48 from rhodecode.lib.compat import json
49 49
50 50 from rhodecode.model.meta import Base, Session
51 51 from rhodecode.model.caching_query import FromCache
52 52
53 53
54 54 log = logging.getLogger(__name__)
55 55
56 56 #==============================================================================
57 57 # BASE CLASSES
58 58 #==============================================================================
59 59
60 60 class ModelSerializer(json.JSONEncoder):
61 61 """
62 62 Simple Serializer for JSON,
63 63
64 64 usage::
65 65
66 66 to make object customized for serialization implement a __json__
67 67 method that will return a dict for serialization into json
68 68
69 69 example::
70 70
71 71 class Task(object):
72 72
73 73 def __init__(self, name, value):
74 74 self.name = name
75 75 self.value = value
76 76
77 77 def __json__(self):
78 78 return dict(name=self.name,
79 79 value=self.value)
80 80
81 81 """
82 82
83 83 def default(self, obj):
84 84
85 85 if hasattr(obj, '__json__'):
86 86 return obj.__json__()
87 87 else:
88 88 return json.JSONEncoder.default(self, obj)
89 89
90 90 class BaseModel(object):
91 91 """Base Model for all classess
92 92
93 93 """
94 94
95 95 @classmethod
96 96 def _get_keys(cls):
97 97 """return column names for this model """
98 98 return class_mapper(cls).c.keys()
99 99
100 100 def get_dict(self):
101 101 """return dict with keys and values corresponding
102 102 to this model data """
103 103
104 104 d = {}
105 105 for k in self._get_keys():
106 106 d[k] = getattr(self, k)
107 107 return d
108 108
109 109 def get_appstruct(self):
110 110 """return list with keys and values tupples corresponding
111 111 to this model data """
112 112
113 113 l = []
114 114 for k in self._get_keys():
115 115 l.append((k, getattr(self, k),))
116 116 return l
117 117
118 118 def populate_obj(self, populate_dict):
119 119 """populate model with data from given populate_dict"""
120 120
121 121 for k in self._get_keys():
122 122 if k in populate_dict:
123 123 setattr(self, k, populate_dict[k])
124 124
125 125 @classmethod
126 126 def query(cls):
127 127 return Session.query(cls)
128 128
129 129 @classmethod
130 130 def get(cls, id_):
131 131 if id_:
132 132 return Session.query(cls).get(id_)
133 133
134 134 @classmethod
135 135 def delete(cls, id_):
136 136 obj = Session.query(cls).get(id_)
137 137 Session.delete(obj)
138 138 Session.commit()
139 139
140 140
141 141 class RhodeCodeSettings(Base, BaseModel):
142 142 __tablename__ = 'rhodecode_settings'
143 143 __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
144 144 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
145 145 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
146 146 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
147 147
148 148 def __init__(self, k='', v=''):
149 149 self.app_settings_name = k
150 150 self.app_settings_value = v
151 151
152 152
153 153 @hybrid_property
154 154 def app_settings_value(self):
155 155 v = self._app_settings_value
156 156 if v == 'ldap_active':
157 157 v = str2bool(v)
158 158 return v
159 159
160 160 @app_settings_value.setter
161 161 def app_settings_value(self,val):
162 162 """
163 163 Setter that will always make sure we use unicode in app_settings_value
164 164
165 165 :param val:
166 166 """
167 167 self._app_settings_value = safe_unicode(val)
168 168
169 169 def __repr__(self):
170 170 return "<%s('%s:%s')>" % (self.__class__.__name__,
171 171 self.app_settings_name, self.app_settings_value)
172 172
173 173
174 174 @classmethod
175 175 def get_by_name(cls, ldap_key):
176 176 return Session.query(cls)\
177 177 .filter(cls.app_settings_name == ldap_key).scalar()
178 178
179 179 @classmethod
180 180 def get_app_settings(cls, cache=False):
181 181
182 182 ret = Session.query(cls)
183 183
184 184 if cache:
185 185 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
186 186
187 187 if not ret:
188 188 raise Exception('Could not get application settings !')
189 189 settings = {}
190 190 for each in ret:
191 191 settings['rhodecode_' + each.app_settings_name] = \
192 192 each.app_settings_value
193 193
194 194 return settings
195 195
196 196 @classmethod
197 197 def get_ldap_settings(cls, cache=False):
198 198 ret = Session.query(cls)\
199 199 .filter(cls.app_settings_name.startswith('ldap_')).all()
200 200 fd = {}
201 201 for row in ret:
202 202 fd.update({row.app_settings_name:row.app_settings_value})
203 203
204 204 return fd
205 205
206 206
207 207 class RhodeCodeUi(Base, BaseModel):
208 208 __tablename__ = 'rhodecode_ui'
209 209 __table_args__ = (UniqueConstraint('ui_key'), {'extend_existing':True})
210 210
211 211 HOOK_UPDATE = 'changegroup.update'
212 212 HOOK_REPO_SIZE = 'changegroup.repo_size'
213 213 HOOK_PUSH = 'pretxnchangegroup.push_logger'
214 214 HOOK_PULL = 'preoutgoing.pull_logger'
215 215
216 216 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
217 217 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
218 218 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
219 219 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
220 220 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
221 221
222 222
223 223 @classmethod
224 224 def get_by_key(cls, key):
225 225 return Session.query(cls).filter(cls.ui_key == key)
226 226
227 227
228 228 @classmethod
229 229 def get_builtin_hooks(cls):
230 230 q = cls.query()
231 231 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
232 232 cls.HOOK_REPO_SIZE,
233 233 cls.HOOK_PUSH, cls.HOOK_PULL]))
234 234 return q.all()
235 235
236 236 @classmethod
237 237 def get_custom_hooks(cls):
238 238 q = cls.query()
239 239 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
240 240 cls.HOOK_REPO_SIZE,
241 241 cls.HOOK_PUSH, cls.HOOK_PULL]))
242 242 q = q.filter(cls.ui_section == 'hooks')
243 243 return q.all()
244 244
245 245 @classmethod
246 246 def create_or_update_hook(cls, key, val):
247 247 new_ui = cls.get_by_key(key).scalar() or cls()
248 248 new_ui.ui_section = 'hooks'
249 249 new_ui.ui_active = True
250 250 new_ui.ui_key = key
251 251 new_ui.ui_value = val
252 252
253 253 Session.add(new_ui)
254 254 Session.commit()
255 255
256 256
257 257 class User(Base, BaseModel):
258 258 __tablename__ = 'users'
259 259 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True})
260 260 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
261 261 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
262 262 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
263 263 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
264 264 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
265 265 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
266 266 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
267 267 email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
268 268 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
269 269 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
270 270 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
271 271
272 272 user_log = relationship('UserLog', cascade='all')
273 273 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
274 274
275 275 repositories = relationship('Repository')
276 276 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
277 277 repo_to_perm = relationship('RepoToPerm', primaryjoin='RepoToPerm.user_id==User.user_id', cascade='all')
278 278
279 279 group_member = relationship('UsersGroupMember', cascade='all')
280 280
281 281 @property
282 282 def full_contact(self):
283 283 return '%s %s <%s>' % (self.name, self.lastname, self.email)
284 284
285 285 @property
286 286 def short_contact(self):
287 287 return '%s %s' % (self.name, self.lastname)
288 288
289 289 @property
290 290 def is_admin(self):
291 291 return self.admin
292 292
293 293 def __repr__(self):
294 294 try:
295 295 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
296 296 self.user_id, self.username)
297 297 except:
298 298 return self.__class__.__name__
299 299
300 300 @classmethod
301 301 def get_by_username(cls, username, case_insensitive=False):
302 302 if case_insensitive:
303 303 return Session.query(cls).filter(cls.username.ilike(username)).scalar()
304 304 else:
305 305 return Session.query(cls).filter(cls.username == username).scalar()
306 306
307 307 @classmethod
308 308 def get_by_api_key(cls, api_key):
309 309 return Session.query(cls).filter(cls.api_key == api_key).one()
310 310
311 311 def update_lastlogin(self):
312 312 """Update user lastlogin"""
313 313
314 314 self.last_login = datetime.datetime.now()
315 315 Session.add(self)
316 316 Session.commit()
317 317 log.debug('updated user %s lastlogin', self.username)
318 318
319 319 @classmethod
320 320 def create(cls, form_data):
321 321 from rhodecode.lib.auth import get_crypt_password
322 322
323 323 try:
324 324 new_user = cls()
325 325 for k, v in form_data.items():
326 326 if k == 'password':
327 327 v = get_crypt_password(v)
328 328 setattr(new_user, k, v)
329 329
330 330 new_user.api_key = generate_api_key(form_data['username'])
331 331 Session.add(new_user)
332 332 Session.commit()
333 333 return new_user
334 334 except:
335 335 log.error(traceback.format_exc())
336 336 Session.rollback()
337 337 raise
338 338
339 339 class UserLog(Base, BaseModel):
340 340 __tablename__ = 'user_logs'
341 341 __table_args__ = {'extend_existing':True}
342 342 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
343 343 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
344 344 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
345 345 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
346 346 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
347 347 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
348 348 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
349 349
350 350 @property
351 351 def action_as_day(self):
352 352 return date(*self.action_date.timetuple()[:3])
353 353
354 354 user = relationship('User')
355 355 repository = relationship('Repository')
356 356
357 357
358 358 class UsersGroup(Base, BaseModel):
359 359 __tablename__ = 'users_groups'
360 360 __table_args__ = {'extend_existing':True}
361 361
362 362 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
363 363 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
364 364 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
365 365
366 366 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
367 367
368 368 def __repr__(self):
369 369 return '<userGroup(%s)>' % (self.users_group_name)
370 370
371 371 @classmethod
372 372 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
373 373 if case_insensitive:
374 374 gr = Session.query(cls)\
375 375 .filter(cls.users_group_name.ilike(group_name))
376 376 else:
377 377 gr = Session.query(UsersGroup)\
378 378 .filter(UsersGroup.users_group_name == group_name)
379 379 if cache:
380 380 gr = gr.options(FromCache("sql_cache_short",
381 381 "get_user_%s" % group_name))
382 382 return gr.scalar()
383 383
384 384
385 385 @classmethod
386 386 def get(cls, users_group_id, cache=False):
387 387 users_group = Session.query(cls)
388 388 if cache:
389 389 users_group = users_group.options(FromCache("sql_cache_short",
390 390 "get_users_group_%s" % users_group_id))
391 391 return users_group.get(users_group_id)
392 392
393 393 @classmethod
394 394 def create(cls, form_data):
395 395 try:
396 396 new_users_group = cls()
397 397 for k, v in form_data.items():
398 398 setattr(new_users_group, k, v)
399 399
400 400 Session.add(new_users_group)
401 401 Session.commit()
402 402 return new_users_group
403 403 except:
404 404 log.error(traceback.format_exc())
405 405 Session.rollback()
406 406 raise
407 407
408 408 @classmethod
409 409 def update(cls, users_group_id, form_data):
410 410
411 411 try:
412 412 users_group = cls.get(users_group_id, cache=False)
413 413
414 414 for k, v in form_data.items():
415 415 if k == 'users_group_members':
416 416 users_group.members = []
417 417 Session.flush()
418 418 members_list = []
419 419 if v:
420 420 for u_id in set(v):
421 421 members_list.append(UsersGroupMember(
422 422 users_group_id,
423 423 u_id))
424 424 setattr(users_group, 'members', members_list)
425 425 setattr(users_group, k, v)
426 426
427 427 Session.add(users_group)
428 428 Session.commit()
429 429 except:
430 430 log.error(traceback.format_exc())
431 431 Session.rollback()
432 432 raise
433 433
434 434 @classmethod
435 435 def delete(cls, users_group_id):
436 436 try:
437 437
438 438 # check if this group is not assigned to repo
439 439 assigned_groups = UsersGroupRepoToPerm.query()\
440 440 .filter(UsersGroupRepoToPerm.users_group_id ==
441 441 users_group_id).all()
442 442
443 443 if assigned_groups:
444 444 raise UsersGroupsAssignedException('Group assigned to %s' %
445 445 assigned_groups)
446 446
447 447 users_group = cls.get(users_group_id, cache=False)
448 448 Session.delete(users_group)
449 449 Session.commit()
450 450 except:
451 451 log.error(traceback.format_exc())
452 452 Session.rollback()
453 453 raise
454 454
455 455
456 456 class UsersGroupMember(Base, BaseModel):
457 457 __tablename__ = 'users_groups_members'
458 458 __table_args__ = {'extend_existing':True}
459 459
460 460 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
461 461 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
462 462 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
463 463
464 464 user = relationship('User', lazy='joined')
465 465 users_group = relationship('UsersGroup')
466 466
467 467 def __init__(self, gr_id='', u_id=''):
468 468 self.users_group_id = gr_id
469 469 self.user_id = u_id
470 470
471 471 class Repository(Base, BaseModel):
472 472 __tablename__ = 'repositories'
473 473 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
474 474
475 475 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
476 476 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
477 477 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
478 478 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
479 479 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
480 480 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
481 481 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
482 482 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
483 483 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
484 484 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
485 485
486 486 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
487 487 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
488 488
489 489
490 490 user = relationship('User')
491 491 fork = relationship('Repository', remote_side=repo_id)
492 492 group = relationship('Group')
493 493 repo_to_perm = relationship('RepoToPerm', cascade='all', order_by='RepoToPerm.repo_to_perm_id')
494 494 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
495 495 stats = relationship('Statistics', cascade='all', uselist=False)
496 496
497 497 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
498 498
499 499 logs = relationship('UserLog', cascade='all')
500 500
501 501 def __repr__(self):
502 502 return "<%s('%s:%s')>" % (self.__class__.__name__,
503 503 self.repo_id, self.repo_name)
504 504
505 505 @classmethod
506 def url_sep(cls):
507 return '/'
508
509 @classmethod
506 510 def get_by_repo_name(cls, repo_name):
507 511 q = Session.query(cls).filter(cls.repo_name == repo_name)
508 512
509 513 q = q.options(joinedload(Repository.fork))\
510 514 .options(joinedload(Repository.user))\
511 515 .options(joinedload(Repository.group))\
512 516
513 517 return q.one()
514 518
515 519 @classmethod
516 520 def get_repo_forks(cls, repo_id):
517 521 return Session.query(cls).filter(Repository.fork_id == repo_id)
518 522
519 523 @classmethod
520 524 def base_path(cls):
521 525 """
522 526 Returns base path when all repos are stored
523 527
524 528 :param cls:
525 529 """
526 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/')
530 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
531 cls.url_sep())
527 532 q.options(FromCache("sql_cache_short", "repository_repo_path"))
528 533 return q.one().ui_value
529 534
530 535 @property
531 536 def just_name(self):
532 537 return self.repo_name.split(os.sep)[-1]
533 538
534 539 @property
535 540 def groups_with_parents(self):
536 541 groups = []
537 542 if self.group is None:
538 543 return groups
539 544
540 545 cur_gr = self.group
541 546 groups.insert(0, cur_gr)
542 547 while 1:
543 548 gr = getattr(cur_gr, 'parent_group', None)
544 549 cur_gr = cur_gr.parent_group
545 550 if gr is None:
546 551 break
547 552 groups.insert(0, gr)
548 553
549 554 return groups
550 555
551 556 @property
552 557 def groups_and_repo(self):
553 558 return self.groups_with_parents, self.just_name
554 559
555 560 @LazyProperty
556 561 def repo_path(self):
557 562 """
558 563 Returns base full path for that repository means where it actually
559 564 exists on a filesystem
560 565 """
561 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/')
566 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
567 Repository.url_sep())
562 568 q.options(FromCache("sql_cache_short", "repository_repo_path"))
563 569 return q.one().ui_value
564 570
565 571 @property
566 572 def repo_full_path(self):
567 573 p = [self.repo_path]
568 574 # we need to split the name by / since this is how we store the
569 575 # names in the database, but that eventually needs to be converted
570 576 # into a valid system path
571 p += self.repo_name.split('/')
577 p += self.repo_name.split(Repository.url_sep())
572 578 return os.path.join(*p)
573 579
574 580 def get_new_name(self, repo_name):
575 581 """
576 582 returns new full repository name based on assigned group and new new
577 583
578 584 :param group_name:
579 585 """
580 586 path_prefix = self.group.full_path_splitted if self.group else []
581 return '/'.join(path_prefix + [repo_name])
587 return Repository.url_sep().join(path_prefix + [repo_name])
582 588
583 589 @property
584 590 def _ui(self):
585 591 """
586 592 Creates an db based ui object for this repository
587 593 """
588 594 from mercurial import ui
589 595 from mercurial import config
590 596 baseui = ui.ui()
591 597
592 598 #clean the baseui object
593 599 baseui._ocfg = config.config()
594 600 baseui._ucfg = config.config()
595 601 baseui._tcfg = config.config()
596 602
597 603
598 604 ret = Session.query(RhodeCodeUi)\
599 605 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
600 606
601 607 hg_ui = ret
602 608 for ui_ in hg_ui:
603 609 if ui_.ui_active:
604 610 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
605 611 ui_.ui_key, ui_.ui_value)
606 612 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
607 613
608 614 return baseui
609 615
610 616 @classmethod
611 617 def is_valid(cls, repo_name):
612 618 """
613 619 returns True if given repo name is a valid filesystem repository
614 620
615 621 @param cls:
616 622 @param repo_name:
617 623 """
618 624 from rhodecode.lib.utils import is_valid_repo
619 625
620 626 return is_valid_repo(repo_name, cls.base_path())
621 627
622 628
623 629 #==========================================================================
624 630 # SCM PROPERTIES
625 631 #==========================================================================
626 632
627 633 def get_changeset(self, rev):
628 634 return get_changeset_safe(self.scm_instance, rev)
629 635
630 636 @property
631 637 def tip(self):
632 638 return self.get_changeset('tip')
633 639
634 640 @property
635 641 def author(self):
636 642 return self.tip.author
637 643
638 644 @property
639 645 def last_change(self):
640 646 return self.scm_instance.last_change
641 647
642 648 #==========================================================================
643 649 # SCM CACHE INSTANCE
644 650 #==========================================================================
645 651
646 652 @property
647 653 def invalidate(self):
648 654 """
649 655 Returns Invalidation object if this repo should be invalidated
650 656 None otherwise. `cache_active = False` means that this cache
651 657 state is not valid and needs to be invalidated
652 658 """
653 659 return Session.query(CacheInvalidation)\
654 660 .filter(CacheInvalidation.cache_key == self.repo_name)\
655 661 .filter(CacheInvalidation.cache_active == False)\
656 662 .scalar()
657 663
658 664 def set_invalidate(self):
659 665 """
660 666 set a cache for invalidation for this instance
661 667 """
662 668 inv = Session.query(CacheInvalidation)\
663 669 .filter(CacheInvalidation.cache_key == self.repo_name)\
664 670 .scalar()
665 671
666 672 if inv is None:
667 673 inv = CacheInvalidation(self.repo_name)
668 674 inv.cache_active = True
669 675 Session.add(inv)
670 676 Session.commit()
671 677
672 678 @LazyProperty
673 679 def scm_instance(self):
674 680 return self.__get_instance()
675 681
676 682 @property
677 683 def scm_instance_cached(self):
678 684 @cache_region('long_term')
679 685 def _c(repo_name):
680 686 return self.__get_instance()
681 687
682 688 # TODO: remove this trick when beaker 1.6 is released
683 689 # and have fixed this issue with not supporting unicode keys
684 690 rn = safe_str(self.repo_name)
685 691
686 692 inv = self.invalidate
687 693 if inv is not None:
688 694 region_invalidate(_c, None, rn)
689 695 # update our cache
690 696 inv.cache_active = True
691 697 Session.add(inv)
692 698 Session.commit()
693 699
694 700 return _c(rn)
695 701
696 702 def __get_instance(self):
697 703
698 704 repo_full_path = self.repo_full_path
699 705
700 706 try:
701 707 alias = get_scm(repo_full_path)[0]
702 708 log.debug('Creating instance of %s repository', alias)
703 709 backend = get_backend(alias)
704 710 except VCSError:
705 711 log.error(traceback.format_exc())
706 712 log.error('Perhaps this repository is in db and not in '
707 713 'filesystem run rescan repositories with '
708 714 '"destroy old data " option from admin panel')
709 715 return
710 716
711 717 if alias == 'hg':
712 718
713 719 repo = backend(safe_str(repo_full_path), create=False,
714 720 baseui=self._ui)
715 721 #skip hidden web repository
716 722 if repo._get_hidden():
717 723 return
718 724 else:
719 725 repo = backend(repo_full_path, create=False)
720 726
721 727 return repo
722 728
723 729
724 730 class Group(Base, BaseModel):
725 731 __tablename__ = 'groups'
726 732 __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'),
727 733 CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},)
728 734 __mapper_args__ = {'order_by':'group_name'}
729 735
730 736 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
731 737 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
732 738 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
733 739 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
734 740
735 741 parent_group = relationship('Group', remote_side=group_id)
736 742
737 743
738 744 def __init__(self, group_name='', parent_group=None):
739 745 self.group_name = group_name
740 746 self.parent_group = parent_group
741 747
742 748 def __repr__(self):
743 749 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
744 750 self.group_name)
745 751
746 752 @classmethod
747 753 def groups_choices(cls):
748 754 from webhelpers.html import literal as _literal
749 755 repo_groups = [('', '')]
750 756 sep = ' &raquo; '
751 757 _name = lambda k: _literal(sep.join(k))
752 758
753 759 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
754 760 for x in cls.query().all()])
755 761
756 762 repo_groups = sorted(repo_groups,key=lambda t: t[1].split(sep)[0])
757 763 return repo_groups
758 764
759 765 @classmethod
760 766 def url_sep(cls):
761 767 return '/'
762 768
763 769 @classmethod
764 770 def get_by_group_name(cls, group_name):
765 771 return cls.query().filter(cls.group_name == group_name).scalar()
766 772
767 773 @property
768 774 def parents(self):
769 775 parents_recursion_limit = 5
770 776 groups = []
771 777 if self.parent_group is None:
772 778 return groups
773 779 cur_gr = self.parent_group
774 780 groups.insert(0, cur_gr)
775 781 cnt = 0
776 782 while 1:
777 783 cnt += 1
778 784 gr = getattr(cur_gr, 'parent_group', None)
779 785 cur_gr = cur_gr.parent_group
780 786 if gr is None:
781 787 break
782 788 if cnt == parents_recursion_limit:
783 789 # this will prevent accidental infinit loops
784 790 log.error('group nested more than %s' %
785 791 parents_recursion_limit)
786 792 break
787 793
788 794 groups.insert(0, gr)
789 795 return groups
790 796
791 797 @property
792 798 def children(self):
793 799 return Session.query(Group).filter(Group.parent_group == self)
794 800
795 801 @property
796 802 def name(self):
797 803 return self.group_name.split(Group.url_sep())[-1]
798 804
799 805 @property
800 806 def full_path(self):
801 807 return self.group_name
802 808
803 809 @property
804 810 def full_path_splitted(self):
805 811 return self.group_name.split(Group.url_sep())
806 812
807 813 @property
808 814 def repositories(self):
809 815 return Session.query(Repository).filter(Repository.group == self)
810 816
811 817 @property
812 818 def repositories_recursive_count(self):
813 819 cnt = self.repositories.count()
814 820
815 821 def children_count(group):
816 822 cnt = 0
817 823 for child in group.children:
818 824 cnt += child.repositories.count()
819 825 cnt += children_count(child)
820 826 return cnt
821 827
822 828 return cnt + children_count(self)
823 829
824 830
825 831 def get_new_name(self, group_name):
826 832 """
827 833 returns new full group name based on parent and new name
828 834
829 835 :param group_name:
830 836 """
831 837 path_prefix = self.parent_group.full_path_splitted if self.parent_group else []
832 838 return Group.url_sep().join(path_prefix + [group_name])
833 839
834 840
835 841 class Permission(Base, BaseModel):
836 842 __tablename__ = 'permissions'
837 843 __table_args__ = {'extend_existing':True}
838 844 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
839 845 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
840 846 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
841 847
842 848 def __repr__(self):
843 849 return "<%s('%s:%s')>" % (self.__class__.__name__,
844 850 self.permission_id, self.permission_name)
845 851
846 852 @classmethod
847 853 def get_by_key(cls, key):
848 854 return Session.query(cls).filter(cls.permission_name == key).scalar()
849 855
850 856 class RepoToPerm(Base, BaseModel):
851 857 __tablename__ = 'repo_to_perm'
852 858 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True})
853 859 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
854 860 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
855 861 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
856 862 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
857 863
858 864 user = relationship('User')
859 865 permission = relationship('Permission')
860 866 repository = relationship('Repository')
861 867
862 868 class UserToPerm(Base, BaseModel):
863 869 __tablename__ = 'user_to_perm'
864 870 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
865 871 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
866 872 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
867 873 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
868 874
869 875 user = relationship('User')
870 876 permission = relationship('Permission')
871 877
872 878 @classmethod
873 879 def has_perm(cls, user_id, perm):
874 880 if not isinstance(perm, Permission):
875 881 raise Exception('perm needs to be an instance of Permission class')
876 882
877 883 return Session.query(cls).filter(cls.user_id == user_id)\
878 884 .filter(cls.permission == perm).scalar() is not None
879 885
880 886 @classmethod
881 887 def grant_perm(cls, user_id, perm):
882 888 if not isinstance(perm, Permission):
883 889 raise Exception('perm needs to be an instance of Permission class')
884 890
885 891 new = cls()
886 892 new.user_id = user_id
887 893 new.permission = perm
888 894 try:
889 895 Session.add(new)
890 896 Session.commit()
891 897 except:
892 898 Session.rollback()
893 899
894 900
895 901 @classmethod
896 902 def revoke_perm(cls, user_id, perm):
897 903 if not isinstance(perm, Permission):
898 904 raise Exception('perm needs to be an instance of Permission class')
899 905
900 906 try:
901 907 Session.query(cls).filter(cls.user_id == user_id)\
902 908 .filter(cls.permission == perm).delete()
903 909 Session.commit()
904 910 except:
905 911 Session.rollback()
906 912
907 913 class UsersGroupRepoToPerm(Base, BaseModel):
908 914 __tablename__ = 'users_group_repo_to_perm'
909 915 __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True})
910 916 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
911 917 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
912 918 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
913 919 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
914 920
915 921 users_group = relationship('UsersGroup')
916 922 permission = relationship('Permission')
917 923 repository = relationship('Repository')
918 924
919 925 def __repr__(self):
920 926 return '<userGroup:%s => %s >' % (self.users_group, self.repository)
921 927
922 928 class UsersGroupToPerm(Base, BaseModel):
923 929 __tablename__ = 'users_group_to_perm'
924 930 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
925 931 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
926 932 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
927 933
928 934 users_group = relationship('UsersGroup')
929 935 permission = relationship('Permission')
930 936
931 937
932 938 @classmethod
933 939 def has_perm(cls, users_group_id, perm):
934 940 if not isinstance(perm, Permission):
935 941 raise Exception('perm needs to be an instance of Permission class')
936 942
937 943 return Session.query(cls).filter(cls.users_group_id ==
938 944 users_group_id)\
939 945 .filter(cls.permission == perm)\
940 946 .scalar() is not None
941 947
942 948 @classmethod
943 949 def grant_perm(cls, users_group_id, perm):
944 950 if not isinstance(perm, Permission):
945 951 raise Exception('perm needs to be an instance of Permission class')
946 952
947 953 new = cls()
948 954 new.users_group_id = users_group_id
949 955 new.permission = perm
950 956 try:
951 957 Session.add(new)
952 958 Session.commit()
953 959 except:
954 960 Session.rollback()
955 961
956 962
957 963 @classmethod
958 964 def revoke_perm(cls, users_group_id, perm):
959 965 if not isinstance(perm, Permission):
960 966 raise Exception('perm needs to be an instance of Permission class')
961 967
962 968 try:
963 969 Session.query(cls).filter(cls.users_group_id == users_group_id)\
964 970 .filter(cls.permission == perm).delete()
965 971 Session.commit()
966 972 except:
967 973 Session.rollback()
968 974
969 975
970 976 class GroupToPerm(Base, BaseModel):
971 977 __tablename__ = 'group_to_perm'
972 978 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
973 979
974 980 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
975 981 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
976 982 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
977 983 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
978 984
979 985 user = relationship('User')
980 986 permission = relationship('Permission')
981 987 group = relationship('Group')
982 988
983 989 class Statistics(Base, BaseModel):
984 990 __tablename__ = 'statistics'
985 991 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True})
986 992 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
987 993 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
988 994 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
989 995 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
990 996 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
991 997 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
992 998
993 999 repository = relationship('Repository', single_parent=True)
994 1000
995 1001 class UserFollowing(Base, BaseModel):
996 1002 __tablename__ = 'user_followings'
997 1003 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
998 1004 UniqueConstraint('user_id', 'follows_user_id')
999 1005 , {'extend_existing':True})
1000 1006
1001 1007 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1002 1008 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1003 1009 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1004 1010 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1005 1011 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1006 1012
1007 1013 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1008 1014
1009 1015 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1010 1016 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1011 1017
1012 1018
1013 1019 @classmethod
1014 1020 def get_repo_followers(cls, repo_id):
1015 1021 return Session.query(cls).filter(cls.follows_repo_id == repo_id)
1016 1022
1017 1023 class CacheInvalidation(Base, BaseModel):
1018 1024 __tablename__ = 'cache_invalidation'
1019 1025 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True})
1020 1026 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1021 1027 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1022 1028 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1023 1029 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1024 1030
1025 1031
1026 1032 def __init__(self, cache_key, cache_args=''):
1027 1033 self.cache_key = cache_key
1028 1034 self.cache_args = cache_args
1029 1035 self.cache_active = False
1030 1036
1031 1037 def __repr__(self):
1032 1038 return "<%s('%s:%s')>" % (self.__class__.__name__,
1033 1039 self.cache_id, self.cache_key)
1034 1040
1035 1041 class DbMigrateVersion(Base, BaseModel):
1036 1042 __tablename__ = 'db_migrate_version'
1037 1043 __table_args__ = {'extend_existing':True}
1038 1044 repository_id = Column('repository_id', String(250), primary_key=True)
1039 1045 repository_path = Column('repository_path', Text)
1040 1046 version = Column('version', Integer)
@@ -1,408 +1,414 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.scm
4 4 ~~~~~~~~~~~~~~~~~~~
5 5
6 6 Scm model for RhodeCode
7 7
8 8 :created_on: Apr 9, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 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 import os
25 26 import time
26 27 import traceback
27 28 import logging
28 29
29 30 from sqlalchemy.exc import DatabaseError
30 31
31 32 from vcs import get_backend
32 33 from vcs.exceptions import RepositoryError
33 34 from vcs.utils.lazy import LazyProperty
34 35 from vcs.nodes import FileNode
35 36
36 37 from rhodecode import BACKENDS
37 38 from rhodecode.lib import helpers as h
38 39 from rhodecode.lib import safe_str
39 40 from rhodecode.lib.auth import HasRepoPermissionAny
40 41 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
41 42 action_logger, EmptyChangeset
42 43 from rhodecode.model import BaseModel
43 44 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
44 45 UserFollowing, UserLog, User
45 46
46 47 log = logging.getLogger(__name__)
47 48
48 49
49 50 class UserTemp(object):
50 51 def __init__(self, user_id):
51 52 self.user_id = user_id
52 53
53 54 def __repr__(self):
54 55 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
55 56
56 57
57 58 class RepoTemp(object):
58 59 def __init__(self, repo_id):
59 60 self.repo_id = repo_id
60 61
61 62 def __repr__(self):
62 63 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
63 64
64 65 class CachedRepoList(object):
65 66
66 67 def __init__(self, db_repo_list, repos_path, order_by=None):
67 68 self.db_repo_list = db_repo_list
68 69 self.repos_path = repos_path
69 70 self.order_by = order_by
70 71 self.reversed = (order_by or '').startswith('-')
71 72
72 73 def __len__(self):
73 74 return len(self.db_repo_list)
74 75
75 76 def __repr__(self):
76 77 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
77 78
78 79 def __iter__(self):
79 80 for dbr in self.db_repo_list:
80 81
81 82 scmr = dbr.scm_instance_cached
82 83
83 84 # check permission at this level
84 85 if not HasRepoPermissionAny('repository.read', 'repository.write',
85 86 'repository.admin')(dbr.repo_name,
86 87 'get repo check'):
87 88 continue
88 89
89 90 if scmr is None:
90 91 log.error('%s this repository is present in database but it '
91 92 'cannot be created as an scm instance',
92 93 dbr.repo_name)
93 94 continue
94 95
95 96 last_change = scmr.last_change
96 97 tip = h.get_changeset_safe(scmr, 'tip')
97 98
98 99 tmp_d = {}
99 100 tmp_d['name'] = dbr.repo_name
100 101 tmp_d['name_sort'] = tmp_d['name'].lower()
101 102 tmp_d['description'] = dbr.description
102 103 tmp_d['description_sort'] = tmp_d['description']
103 104 tmp_d['last_change'] = last_change
104 105 tmp_d['last_change_sort'] = time.mktime(last_change \
105 106 .timetuple())
106 107 tmp_d['tip'] = tip.raw_id
107 108 tmp_d['tip_sort'] = tip.revision
108 109 tmp_d['rev'] = tip.revision
109 110 tmp_d['contact'] = dbr.user.full_contact
110 111 tmp_d['contact_sort'] = tmp_d['contact']
111 112 tmp_d['owner_sort'] = tmp_d['contact']
112 113 tmp_d['repo_archives'] = list(scmr._get_archives())
113 114 tmp_d['last_msg'] = tip.message
114 115 tmp_d['author'] = tip.author
115 116 tmp_d['dbrepo'] = dbr.get_dict()
116 117 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork \
117 118 else {}
118 119 yield tmp_d
119 120
120 121 class ScmModel(BaseModel):
121 122 """Generic Scm Model
122 123 """
123 124
124 125 @LazyProperty
125 126 def repos_path(self):
126 127 """Get's the repositories root path from database
127 128 """
128 129
129 130 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
130 131
131 132 return q.ui_value
132 133
133 134 def repo_scan(self, repos_path=None):
134 135 """Listing of repositories in given path. This path should not be a
135 136 repository itself. Return a dictionary of repository objects
136 137
137 138 :param repos_path: path to directory containing repositories
138 139 """
139 140
140 141 log.info('scanning for repositories in %s', repos_path)
141 142
142 143 if repos_path is None:
143 144 repos_path = self.repos_path
144 145
145 146 baseui = make_ui('db')
146 147 repos_list = {}
147 148
148 149 for name, path in get_filesystem_repos(repos_path, recursive=True):
150
151 # name need to be decomposed and put back together using the /
152 # since this is internal storage separator for rhodecode
153 name = Repository.url_sep().join(name.split(os.sep))
154
149 155 try:
150 156 if name in repos_list:
151 157 raise RepositoryError('Duplicate repository name %s '
152 158 'found in %s' % (name, path))
153 159 else:
154 160
155 161 klass = get_backend(path[0])
156 162
157 163 if path[0] == 'hg' and path[0] in BACKENDS.keys():
158 164
159 165 # for mercurial we need to have an str path
160 166 repos_list[name] = klass(safe_str(path[1]),
161 167 baseui=baseui)
162 168
163 169 if path[0] == 'git' and path[0] in BACKENDS.keys():
164 170 repos_list[name] = klass(path[1])
165 171 except OSError:
166 172 continue
167 173
168 174 return repos_list
169 175
170 176 def get_repos(self, all_repos=None, sort_key=None):
171 177 """
172 178 Get all repos from db and for each repo create it's
173 179 backend instance and fill that backed with information from database
174 180
175 181 :param all_repos: list of repository names as strings
176 182 give specific repositories list, good for filtering
177 183 """
178 184 if all_repos is None:
179 185 all_repos = self.sa.query(Repository)\
180 186 .filter(Repository.group_id == None)\
181 187 .order_by(Repository.repo_name).all()
182 188
183 189 repo_iter = CachedRepoList(all_repos, repos_path=self.repos_path,
184 190 order_by=sort_key)
185 191
186 192 return repo_iter
187 193
188 194 def mark_for_invalidation(self, repo_name):
189 195 """Puts cache invalidation task into db for
190 196 further global cache invalidation
191 197
192 198 :param repo_name: this repo that should invalidation take place
193 199 """
194 200
195 201 log.debug('marking %s for invalidation', repo_name)
196 202 cache = self.sa.query(CacheInvalidation)\
197 203 .filter(CacheInvalidation.cache_key == repo_name).scalar()
198 204
199 205 if cache:
200 206 # mark this cache as inactive
201 207 cache.cache_active = False
202 208 else:
203 209 log.debug('cache key not found in invalidation db -> creating one')
204 210 cache = CacheInvalidation(repo_name)
205 211
206 212 try:
207 213 self.sa.add(cache)
208 214 self.sa.commit()
209 215 except (DatabaseError,):
210 216 log.error(traceback.format_exc())
211 217 self.sa.rollback()
212 218
213 219 def toggle_following_repo(self, follow_repo_id, user_id):
214 220
215 221 f = self.sa.query(UserFollowing)\
216 222 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
217 223 .filter(UserFollowing.user_id == user_id).scalar()
218 224
219 225 if f is not None:
220 226
221 227 try:
222 228 self.sa.delete(f)
223 229 self.sa.commit()
224 230 action_logger(UserTemp(user_id),
225 231 'stopped_following_repo',
226 232 RepoTemp(follow_repo_id))
227 233 return
228 234 except:
229 235 log.error(traceback.format_exc())
230 236 self.sa.rollback()
231 237 raise
232 238
233 239 try:
234 240 f = UserFollowing()
235 241 f.user_id = user_id
236 242 f.follows_repo_id = follow_repo_id
237 243 self.sa.add(f)
238 244 self.sa.commit()
239 245 action_logger(UserTemp(user_id),
240 246 'started_following_repo',
241 247 RepoTemp(follow_repo_id))
242 248 except:
243 249 log.error(traceback.format_exc())
244 250 self.sa.rollback()
245 251 raise
246 252
247 253 def toggle_following_user(self, follow_user_id, user_id):
248 254 f = self.sa.query(UserFollowing)\
249 255 .filter(UserFollowing.follows_user_id == follow_user_id)\
250 256 .filter(UserFollowing.user_id == user_id).scalar()
251 257
252 258 if f is not None:
253 259 try:
254 260 self.sa.delete(f)
255 261 self.sa.commit()
256 262 return
257 263 except:
258 264 log.error(traceback.format_exc())
259 265 self.sa.rollback()
260 266 raise
261 267
262 268 try:
263 269 f = UserFollowing()
264 270 f.user_id = user_id
265 271 f.follows_user_id = follow_user_id
266 272 self.sa.add(f)
267 273 self.sa.commit()
268 274 except:
269 275 log.error(traceback.format_exc())
270 276 self.sa.rollback()
271 277 raise
272 278
273 279 def is_following_repo(self, repo_name, user_id, cache=False):
274 280 r = self.sa.query(Repository)\
275 281 .filter(Repository.repo_name == repo_name).scalar()
276 282
277 283 f = self.sa.query(UserFollowing)\
278 284 .filter(UserFollowing.follows_repository == r)\
279 285 .filter(UserFollowing.user_id == user_id).scalar()
280 286
281 287 return f is not None
282 288
283 289 def is_following_user(self, username, user_id, cache=False):
284 290 u = User.get_by_username(username)
285 291
286 292 f = self.sa.query(UserFollowing)\
287 293 .filter(UserFollowing.follows_user == u)\
288 294 .filter(UserFollowing.user_id == user_id).scalar()
289 295
290 296 return f is not None
291 297
292 298 def get_followers(self, repo_id):
293 299 if not isinstance(repo_id, int):
294 300 repo_id = getattr(Repository.get_by_repo_name(repo_id), 'repo_id')
295 301
296 302 return self.sa.query(UserFollowing)\
297 303 .filter(UserFollowing.follows_repo_id == repo_id).count()
298 304
299 305 def get_forks(self, repo_id):
300 306 if not isinstance(repo_id, int):
301 307 repo_id = getattr(Repository.get_by_repo_name(repo_id), 'repo_id')
302 308
303 309 return self.sa.query(Repository)\
304 310 .filter(Repository.fork_id == repo_id).count()
305 311
306 312 def pull_changes(self, repo_name, username):
307 313 dbrepo = Repository.get_by_repo_name(repo_name)
308 314 clone_uri = dbrepo.clone_uri
309 315 if not clone_uri:
310 316 raise Exception("This repository doesn't have a clone uri")
311 317
312 318 repo = dbrepo.scm_instance
313 319 try:
314 320 extras = {'ip': '',
315 321 'username': username,
316 322 'action': 'push_remote',
317 323 'repository': repo_name}
318 324
319 325 #inject ui extra param to log this action via push logger
320 326 for k, v in extras.items():
321 327 repo._repo.ui.setconfig('rhodecode_extras', k, v)
322 328
323 329 repo.pull(clone_uri)
324 330 self.mark_for_invalidation(repo_name)
325 331 except:
326 332 log.error(traceback.format_exc())
327 333 raise
328 334
329 335 def commit_change(self, repo, repo_name, cs, user, author, message, content,
330 336 f_path):
331 337
332 338 if repo.alias == 'hg':
333 339 from vcs.backends.hg import MercurialInMemoryChangeset as IMC
334 340 elif repo.alias == 'git':
335 341 from vcs.backends.git import GitInMemoryChangeset as IMC
336 342
337 343 # decoding here will force that we have proper encoded values
338 344 # in any other case this will throw exceptions and deny commit
339 345 content = safe_str(content)
340 346 message = safe_str(message)
341 347 path = safe_str(f_path)
342 348 author = safe_str(author)
343 349 m = IMC(repo)
344 350 m.change(FileNode(path, content))
345 351 tip = m.commit(message=message,
346 352 author=author,
347 353 parents=[cs], branch=cs.branch)
348 354
349 355 new_cs = tip.short_id
350 356 action = 'push_local:%s' % new_cs
351 357
352 358 action_logger(user, action, repo_name)
353 359
354 360 self.mark_for_invalidation(repo_name)
355 361
356 362 def create_node(self, repo, repo_name, cs, user, author, message, content,
357 363 f_path):
358 364 if repo.alias == 'hg':
359 365 from vcs.backends.hg import MercurialInMemoryChangeset as IMC
360 366 elif repo.alias == 'git':
361 367 from vcs.backends.git import GitInMemoryChangeset as IMC
362 368 # decoding here will force that we have proper encoded values
363 369 # in any other case this will throw exceptions and deny commit
364 370
365 371 if isinstance(content, (basestring,)):
366 372 content = safe_str(content)
367 373 elif isinstance(content, file):
368 374 content = content.read()
369 375
370 376 message = safe_str(message)
371 377 path = safe_str(f_path)
372 378 author = safe_str(author)
373 379 m = IMC(repo)
374 380
375 381 if isinstance(cs, EmptyChangeset):
376 382 # Emptychangeset means we we're editing empty repository
377 383 parents = None
378 384 else:
379 385 parents = [cs]
380 386
381 387 m.add(FileNode(path, content=content))
382 388 tip = m.commit(message=message,
383 389 author=author,
384 390 parents=parents, branch=cs.branch)
385 391 new_cs = tip.short_id
386 392 action = 'push_local:%s' % new_cs
387 393
388 394 action_logger(user, action, repo_name)
389 395
390 396 self.mark_for_invalidation(repo_name)
391 397
392 398
393 399 def get_unread_journal(self):
394 400 return self.sa.query(UserLog).count()
395 401
396 402 def _should_invalidate(self, repo_name):
397 403 """Looks up database for invalidation signals for this repo_name
398 404
399 405 :param repo_name:
400 406 """
401 407
402 408 ret = self.sa.query(CacheInvalidation)\
403 409 .filter(CacheInvalidation.cache_key == repo_name)\
404 410 .filter(CacheInvalidation.cache_active == False)\
405 411 .scalar()
406 412
407 413 return ret
408 414
General Comments 0
You need to be logged in to leave comments. Login now