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