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