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