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