##// END OF EJS Templates
fixed issue with cascade deleting of following entries
marcink -
r3067:9b0636e9 beta
parent child Browse files
Show More
@@ -1,1832 +1,1834 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.db
3 rhodecode.model.db
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 Database Models for RhodeCode
6 Database Models for RhodeCode
7
7
8 :created_on: Apr 08, 2010
8 :created_on: Apr 08, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import logging
27 import logging
28 import datetime
28 import datetime
29 import traceback
29 import traceback
30 import hashlib
30 import hashlib
31 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')
340
339 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')
340 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')
341
343
342 group_member = relationship('UsersGroupMember', cascade='all')
344 group_member = relationship('UsersGroupMember', cascade='all')
343
345
344 notifications = relationship('UserNotification', cascade='all')
346 notifications = relationship('UserNotification', cascade='all')
345 # notifications assigned to this user
347 # notifications assigned to this user
346 user_created_notifications = relationship('Notification', cascade='all')
348 user_created_notifications = relationship('Notification', cascade='all')
347 # comments created by this user
349 # comments created by this user
348 user_comments = relationship('ChangesetComment', cascade='all')
350 user_comments = relationship('ChangesetComment', cascade='all')
349 #extra emails for this user
351 #extra emails for this user
350 user_emails = relationship('UserEmailMap', cascade='all')
352 user_emails = relationship('UserEmailMap', cascade='all')
351
353
352 @hybrid_property
354 @hybrid_property
353 def email(self):
355 def email(self):
354 return self._email
356 return self._email
355
357
356 @email.setter
358 @email.setter
357 def email(self, val):
359 def email(self, val):
358 self._email = val.lower() if val else None
360 self._email = val.lower() if val else None
359
361
360 @property
362 @property
361 def firstname(self):
363 def firstname(self):
362 # alias for future
364 # alias for future
363 return self.name
365 return self.name
364
366
365 @property
367 @property
366 def emails(self):
368 def emails(self):
367 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
369 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
368 return [self.email] + [x.email for x in other]
370 return [self.email] + [x.email for x in other]
369
371
370 @property
372 @property
371 def username_and_name(self):
373 def username_and_name(self):
372 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
374 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
373
375
374 @property
376 @property
375 def full_name(self):
377 def full_name(self):
376 return '%s %s' % (self.firstname, self.lastname)
378 return '%s %s' % (self.firstname, self.lastname)
377
379
378 @property
380 @property
379 def full_name_or_username(self):
381 def full_name_or_username(self):
380 return ('%s %s' % (self.firstname, self.lastname)
382 return ('%s %s' % (self.firstname, self.lastname)
381 if (self.firstname and self.lastname) else self.username)
383 if (self.firstname and self.lastname) else self.username)
382
384
383 @property
385 @property
384 def full_contact(self):
386 def full_contact(self):
385 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
387 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
386
388
387 @property
389 @property
388 def short_contact(self):
390 def short_contact(self):
389 return '%s %s' % (self.firstname, self.lastname)
391 return '%s %s' % (self.firstname, self.lastname)
390
392
391 @property
393 @property
392 def is_admin(self):
394 def is_admin(self):
393 return self.admin
395 return self.admin
394
396
395 def __unicode__(self):
397 def __unicode__(self):
396 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
398 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
397 self.user_id, self.username)
399 self.user_id, self.username)
398
400
399 @classmethod
401 @classmethod
400 def get_by_username(cls, username, case_insensitive=False, cache=False):
402 def get_by_username(cls, username, case_insensitive=False, cache=False):
401 if case_insensitive:
403 if case_insensitive:
402 q = cls.query().filter(cls.username.ilike(username))
404 q = cls.query().filter(cls.username.ilike(username))
403 else:
405 else:
404 q = cls.query().filter(cls.username == username)
406 q = cls.query().filter(cls.username == username)
405
407
406 if cache:
408 if cache:
407 q = q.options(FromCache(
409 q = q.options(FromCache(
408 "sql_cache_short",
410 "sql_cache_short",
409 "get_user_%s" % _hash_key(username)
411 "get_user_%s" % _hash_key(username)
410 )
412 )
411 )
413 )
412 return q.scalar()
414 return q.scalar()
413
415
414 @classmethod
416 @classmethod
415 def get_by_api_key(cls, api_key, cache=False):
417 def get_by_api_key(cls, api_key, cache=False):
416 q = cls.query().filter(cls.api_key == api_key)
418 q = cls.query().filter(cls.api_key == api_key)
417
419
418 if cache:
420 if cache:
419 q = q.options(FromCache("sql_cache_short",
421 q = q.options(FromCache("sql_cache_short",
420 "get_api_key_%s" % api_key))
422 "get_api_key_%s" % api_key))
421 return q.scalar()
423 return q.scalar()
422
424
423 @classmethod
425 @classmethod
424 def get_by_email(cls, email, case_insensitive=False, cache=False):
426 def get_by_email(cls, email, case_insensitive=False, cache=False):
425 if case_insensitive:
427 if case_insensitive:
426 q = cls.query().filter(cls.email.ilike(email))
428 q = cls.query().filter(cls.email.ilike(email))
427 else:
429 else:
428 q = cls.query().filter(cls.email == email)
430 q = cls.query().filter(cls.email == email)
429
431
430 if cache:
432 if cache:
431 q = q.options(FromCache("sql_cache_short",
433 q = q.options(FromCache("sql_cache_short",
432 "get_email_key_%s" % email))
434 "get_email_key_%s" % email))
433
435
434 ret = q.scalar()
436 ret = q.scalar()
435 if ret is None:
437 if ret is None:
436 q = UserEmailMap.query()
438 q = UserEmailMap.query()
437 # try fetching in alternate email map
439 # try fetching in alternate email map
438 if case_insensitive:
440 if case_insensitive:
439 q = q.filter(UserEmailMap.email.ilike(email))
441 q = q.filter(UserEmailMap.email.ilike(email))
440 else:
442 else:
441 q = q.filter(UserEmailMap.email == email)
443 q = q.filter(UserEmailMap.email == email)
442 q = q.options(joinedload(UserEmailMap.user))
444 q = q.options(joinedload(UserEmailMap.user))
443 if cache:
445 if cache:
444 q = q.options(FromCache("sql_cache_short",
446 q = q.options(FromCache("sql_cache_short",
445 "get_email_map_key_%s" % email))
447 "get_email_map_key_%s" % email))
446 ret = getattr(q.scalar(), 'user', None)
448 ret = getattr(q.scalar(), 'user', None)
447
449
448 return ret
450 return ret
449
451
450 def update_lastlogin(self):
452 def update_lastlogin(self):
451 """Update user lastlogin"""
453 """Update user lastlogin"""
452 self.last_login = datetime.datetime.now()
454 self.last_login = datetime.datetime.now()
453 Session().add(self)
455 Session().add(self)
454 log.debug('updated user %s lastlogin' % self.username)
456 log.debug('updated user %s lastlogin' % self.username)
455
457
456 def get_api_data(self):
458 def get_api_data(self):
457 """
459 """
458 Common function for generating user related data for API
460 Common function for generating user related data for API
459 """
461 """
460 user = self
462 user = self
461 data = dict(
463 data = dict(
462 user_id=user.user_id,
464 user_id=user.user_id,
463 username=user.username,
465 username=user.username,
464 firstname=user.name,
466 firstname=user.name,
465 lastname=user.lastname,
467 lastname=user.lastname,
466 email=user.email,
468 email=user.email,
467 emails=user.emails,
469 emails=user.emails,
468 api_key=user.api_key,
470 api_key=user.api_key,
469 active=user.active,
471 active=user.active,
470 admin=user.admin,
472 admin=user.admin,
471 ldap_dn=user.ldap_dn,
473 ldap_dn=user.ldap_dn,
472 last_login=user.last_login,
474 last_login=user.last_login,
473 )
475 )
474 return data
476 return data
475
477
476 def __json__(self):
478 def __json__(self):
477 data = dict(
479 data = dict(
478 full_name=self.full_name,
480 full_name=self.full_name,
479 full_name_or_username=self.full_name_or_username,
481 full_name_or_username=self.full_name_or_username,
480 short_contact=self.short_contact,
482 short_contact=self.short_contact,
481 full_contact=self.full_contact
483 full_contact=self.full_contact
482 )
484 )
483 data.update(self.get_api_data())
485 data.update(self.get_api_data())
484 return data
486 return data
485
487
486
488
487 class UserEmailMap(Base, BaseModel):
489 class UserEmailMap(Base, BaseModel):
488 __tablename__ = 'user_email_map'
490 __tablename__ = 'user_email_map'
489 __table_args__ = (
491 __table_args__ = (
490 Index('uem_email_idx', 'email'),
492 Index('uem_email_idx', 'email'),
491 UniqueConstraint('email'),
493 UniqueConstraint('email'),
492 {'extend_existing': True, 'mysql_engine': 'InnoDB',
494 {'extend_existing': True, 'mysql_engine': 'InnoDB',
493 'mysql_charset': 'utf8'}
495 'mysql_charset': 'utf8'}
494 )
496 )
495 __mapper_args__ = {}
497 __mapper_args__ = {}
496
498
497 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
499 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
498 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
500 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
499 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
501 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
500 user = relationship('User', lazy='joined')
502 user = relationship('User', lazy='joined')
501
503
502 @validates('_email')
504 @validates('_email')
503 def validate_email(self, key, email):
505 def validate_email(self, key, email):
504 # check if this email is not main one
506 # check if this email is not main one
505 main_email = Session().query(User).filter(User.email == email).scalar()
507 main_email = Session().query(User).filter(User.email == email).scalar()
506 if main_email is not None:
508 if main_email is not None:
507 raise AttributeError('email %s is present is user table' % email)
509 raise AttributeError('email %s is present is user table' % email)
508 return email
510 return email
509
511
510 @hybrid_property
512 @hybrid_property
511 def email(self):
513 def email(self):
512 return self._email
514 return self._email
513
515
514 @email.setter
516 @email.setter
515 def email(self, val):
517 def email(self, val):
516 self._email = val.lower() if val else None
518 self._email = val.lower() if val else None
517
519
518
520
519 class UserLog(Base, BaseModel):
521 class UserLog(Base, BaseModel):
520 __tablename__ = 'user_logs'
522 __tablename__ = 'user_logs'
521 __table_args__ = (
523 __table_args__ = (
522 {'extend_existing': True, 'mysql_engine': 'InnoDB',
524 {'extend_existing': True, 'mysql_engine': 'InnoDB',
523 'mysql_charset': 'utf8'},
525 'mysql_charset': 'utf8'},
524 )
526 )
525 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
527 user_log_id = Column("user_log_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)
528 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
527 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
529 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
528 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
530 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
529 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
531 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
530 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
532 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
531 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
533 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
532 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
534 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
533
535
534 @property
536 @property
535 def action_as_day(self):
537 def action_as_day(self):
536 return datetime.date(*self.action_date.timetuple()[:3])
538 return datetime.date(*self.action_date.timetuple()[:3])
537
539
538 user = relationship('User')
540 user = relationship('User')
539 repository = relationship('Repository', cascade='')
541 repository = relationship('Repository', cascade='')
540
542
541
543
542 class UsersGroup(Base, BaseModel):
544 class UsersGroup(Base, BaseModel):
543 __tablename__ = 'users_groups'
545 __tablename__ = 'users_groups'
544 __table_args__ = (
546 __table_args__ = (
545 {'extend_existing': True, 'mysql_engine': 'InnoDB',
547 {'extend_existing': True, 'mysql_engine': 'InnoDB',
546 'mysql_charset': 'utf8'},
548 'mysql_charset': 'utf8'},
547 )
549 )
548
550
549 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
551 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
550 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
552 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
551 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
553 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
552 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
554 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
553
555
554 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
556 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
555 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
557 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
556 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
558 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
557
559
558 def __unicode__(self):
560 def __unicode__(self):
559 return u'<userGroup(%s)>' % (self.users_group_name)
561 return u'<userGroup(%s)>' % (self.users_group_name)
560
562
561 @classmethod
563 @classmethod
562 def get_by_group_name(cls, group_name, cache=False,
564 def get_by_group_name(cls, group_name, cache=False,
563 case_insensitive=False):
565 case_insensitive=False):
564 if case_insensitive:
566 if case_insensitive:
565 q = cls.query().filter(cls.users_group_name.ilike(group_name))
567 q = cls.query().filter(cls.users_group_name.ilike(group_name))
566 else:
568 else:
567 q = cls.query().filter(cls.users_group_name == group_name)
569 q = cls.query().filter(cls.users_group_name == group_name)
568 if cache:
570 if cache:
569 q = q.options(FromCache(
571 q = q.options(FromCache(
570 "sql_cache_short",
572 "sql_cache_short",
571 "get_user_%s" % _hash_key(group_name)
573 "get_user_%s" % _hash_key(group_name)
572 )
574 )
573 )
575 )
574 return q.scalar()
576 return q.scalar()
575
577
576 @classmethod
578 @classmethod
577 def get(cls, users_group_id, cache=False):
579 def get(cls, users_group_id, cache=False):
578 users_group = cls.query()
580 users_group = cls.query()
579 if cache:
581 if cache:
580 users_group = users_group.options(FromCache("sql_cache_short",
582 users_group = users_group.options(FromCache("sql_cache_short",
581 "get_users_group_%s" % users_group_id))
583 "get_users_group_%s" % users_group_id))
582 return users_group.get(users_group_id)
584 return users_group.get(users_group_id)
583
585
584 def get_api_data(self):
586 def get_api_data(self):
585 users_group = self
587 users_group = self
586
588
587 data = dict(
589 data = dict(
588 users_group_id=users_group.users_group_id,
590 users_group_id=users_group.users_group_id,
589 group_name=users_group.users_group_name,
591 group_name=users_group.users_group_name,
590 active=users_group.users_group_active,
592 active=users_group.users_group_active,
591 )
593 )
592
594
593 return data
595 return data
594
596
595
597
596 class UsersGroupMember(Base, BaseModel):
598 class UsersGroupMember(Base, BaseModel):
597 __tablename__ = 'users_groups_members'
599 __tablename__ = 'users_groups_members'
598 __table_args__ = (
600 __table_args__ = (
599 {'extend_existing': True, 'mysql_engine': 'InnoDB',
601 {'extend_existing': True, 'mysql_engine': 'InnoDB',
600 'mysql_charset': 'utf8'},
602 'mysql_charset': 'utf8'},
601 )
603 )
602
604
603 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
605 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
604 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
606 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
605 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
607 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
606
608
607 user = relationship('User', lazy='joined')
609 user = relationship('User', lazy='joined')
608 users_group = relationship('UsersGroup')
610 users_group = relationship('UsersGroup')
609
611
610 def __init__(self, gr_id='', u_id=''):
612 def __init__(self, gr_id='', u_id=''):
611 self.users_group_id = gr_id
613 self.users_group_id = gr_id
612 self.user_id = u_id
614 self.user_id = u_id
613
615
614
616
615 class Repository(Base, BaseModel):
617 class Repository(Base, BaseModel):
616 __tablename__ = 'repositories'
618 __tablename__ = 'repositories'
617 __table_args__ = (
619 __table_args__ = (
618 UniqueConstraint('repo_name'),
620 UniqueConstraint('repo_name'),
619 Index('r_repo_name_idx', 'repo_name'),
621 Index('r_repo_name_idx', 'repo_name'),
620 {'extend_existing': True, 'mysql_engine': 'InnoDB',
622 {'extend_existing': True, 'mysql_engine': 'InnoDB',
621 'mysql_charset': 'utf8'},
623 'mysql_charset': 'utf8'},
622 )
624 )
623
625
624 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
626 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
625 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
627 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
626 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
628 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
627 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
629 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
628 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
630 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
629 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
631 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
630 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
632 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
631 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
633 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
632 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
634 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
633 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
635 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
634 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
636 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
635 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
637 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
636 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
638 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
637 _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
639 _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
638
640
639 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
641 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
640 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
642 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
641
643
642 user = relationship('User')
644 user = relationship('User')
643 fork = relationship('Repository', remote_side=repo_id)
645 fork = relationship('Repository', remote_side=repo_id)
644 group = relationship('RepoGroup')
646 group = relationship('RepoGroup')
645 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
647 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
646 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
648 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
647 stats = relationship('Statistics', cascade='all', uselist=False)
649 stats = relationship('Statistics', cascade='all', uselist=False)
648
650
649 followers = relationship('UserFollowing',
651 followers = relationship('UserFollowing',
650 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
652 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
651 cascade='all')
653 cascade='all')
652
654
653 logs = relationship('UserLog')
655 logs = relationship('UserLog')
654 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
656 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
655
657
656 pull_requests_org = relationship('PullRequest',
658 pull_requests_org = relationship('PullRequest',
657 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
659 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
658 cascade="all, delete, delete-orphan")
660 cascade="all, delete, delete-orphan")
659
661
660 pull_requests_other = relationship('PullRequest',
662 pull_requests_other = relationship('PullRequest',
661 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
663 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
662 cascade="all, delete, delete-orphan")
664 cascade="all, delete, delete-orphan")
663
665
664 def __unicode__(self):
666 def __unicode__(self):
665 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
667 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
666 self.repo_name)
668 self.repo_name)
667
669
668 @hybrid_property
670 @hybrid_property
669 def locked(self):
671 def locked(self):
670 # always should return [user_id, timelocked]
672 # always should return [user_id, timelocked]
671 if self._locked:
673 if self._locked:
672 _lock_info = self._locked.split(':')
674 _lock_info = self._locked.split(':')
673 return int(_lock_info[0]), _lock_info[1]
675 return int(_lock_info[0]), _lock_info[1]
674 return [None, None]
676 return [None, None]
675
677
676 @locked.setter
678 @locked.setter
677 def locked(self, val):
679 def locked(self, val):
678 if val and isinstance(val, (list, tuple)):
680 if val and isinstance(val, (list, tuple)):
679 self._locked = ':'.join(map(str, val))
681 self._locked = ':'.join(map(str, val))
680 else:
682 else:
681 self._locked = None
683 self._locked = None
682
684
683 @classmethod
685 @classmethod
684 def url_sep(cls):
686 def url_sep(cls):
685 return URL_SEP
687 return URL_SEP
686
688
687 @classmethod
689 @classmethod
688 def get_by_repo_name(cls, repo_name):
690 def get_by_repo_name(cls, repo_name):
689 q = Session().query(cls).filter(cls.repo_name == repo_name)
691 q = Session().query(cls).filter(cls.repo_name == repo_name)
690 q = q.options(joinedload(Repository.fork))\
692 q = q.options(joinedload(Repository.fork))\
691 .options(joinedload(Repository.user))\
693 .options(joinedload(Repository.user))\
692 .options(joinedload(Repository.group))
694 .options(joinedload(Repository.group))
693 return q.scalar()
695 return q.scalar()
694
696
695 @classmethod
697 @classmethod
696 def get_by_full_path(cls, repo_full_path):
698 def get_by_full_path(cls, repo_full_path):
697 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
699 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
698 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
700 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
699
701
700 @classmethod
702 @classmethod
701 def get_repo_forks(cls, repo_id):
703 def get_repo_forks(cls, repo_id):
702 return cls.query().filter(Repository.fork_id == repo_id)
704 return cls.query().filter(Repository.fork_id == repo_id)
703
705
704 @classmethod
706 @classmethod
705 def base_path(cls):
707 def base_path(cls):
706 """
708 """
707 Returns base path when all repos are stored
709 Returns base path when all repos are stored
708
710
709 :param cls:
711 :param cls:
710 """
712 """
711 q = Session().query(RhodeCodeUi)\
713 q = Session().query(RhodeCodeUi)\
712 .filter(RhodeCodeUi.ui_key == cls.url_sep())
714 .filter(RhodeCodeUi.ui_key == cls.url_sep())
713 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
715 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
714 return q.one().ui_value
716 return q.one().ui_value
715
717
716 @property
718 @property
717 def forks(self):
719 def forks(self):
718 """
720 """
719 Return forks of this repo
721 Return forks of this repo
720 """
722 """
721 return Repository.get_repo_forks(self.repo_id)
723 return Repository.get_repo_forks(self.repo_id)
722
724
723 @property
725 @property
724 def parent(self):
726 def parent(self):
725 """
727 """
726 Returns fork parent
728 Returns fork parent
727 """
729 """
728 return self.fork
730 return self.fork
729
731
730 @property
732 @property
731 def just_name(self):
733 def just_name(self):
732 return self.repo_name.split(Repository.url_sep())[-1]
734 return self.repo_name.split(Repository.url_sep())[-1]
733
735
734 @property
736 @property
735 def groups_with_parents(self):
737 def groups_with_parents(self):
736 groups = []
738 groups = []
737 if self.group is None:
739 if self.group is None:
738 return groups
740 return groups
739
741
740 cur_gr = self.group
742 cur_gr = self.group
741 groups.insert(0, cur_gr)
743 groups.insert(0, cur_gr)
742 while 1:
744 while 1:
743 gr = getattr(cur_gr, 'parent_group', None)
745 gr = getattr(cur_gr, 'parent_group', None)
744 cur_gr = cur_gr.parent_group
746 cur_gr = cur_gr.parent_group
745 if gr is None:
747 if gr is None:
746 break
748 break
747 groups.insert(0, gr)
749 groups.insert(0, gr)
748
750
749 return groups
751 return groups
750
752
751 @property
753 @property
752 def groups_and_repo(self):
754 def groups_and_repo(self):
753 return self.groups_with_parents, self.just_name
755 return self.groups_with_parents, self.just_name
754
756
755 @LazyProperty
757 @LazyProperty
756 def repo_path(self):
758 def repo_path(self):
757 """
759 """
758 Returns base full path for that repository means where it actually
760 Returns base full path for that repository means where it actually
759 exists on a filesystem
761 exists on a filesystem
760 """
762 """
761 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
763 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
762 Repository.url_sep())
764 Repository.url_sep())
763 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
765 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
764 return q.one().ui_value
766 return q.one().ui_value
765
767
766 @property
768 @property
767 def repo_full_path(self):
769 def repo_full_path(self):
768 p = [self.repo_path]
770 p = [self.repo_path]
769 # we need to split the name by / since this is how we store the
771 # we need to split the name by / since this is how we store the
770 # names in the database, but that eventually needs to be converted
772 # names in the database, but that eventually needs to be converted
771 # into a valid system path
773 # into a valid system path
772 p += self.repo_name.split(Repository.url_sep())
774 p += self.repo_name.split(Repository.url_sep())
773 return os.path.join(*p)
775 return os.path.join(*p)
774
776
775 @property
777 @property
776 def cache_keys(self):
778 def cache_keys(self):
777 """
779 """
778 Returns associated cache keys for that repo
780 Returns associated cache keys for that repo
779 """
781 """
780 return CacheInvalidation.query()\
782 return CacheInvalidation.query()\
781 .filter(CacheInvalidation.cache_args == self.repo_name)\
783 .filter(CacheInvalidation.cache_args == self.repo_name)\
782 .order_by(CacheInvalidation.cache_key)\
784 .order_by(CacheInvalidation.cache_key)\
783 .all()
785 .all()
784
786
785 def get_new_name(self, repo_name):
787 def get_new_name(self, repo_name):
786 """
788 """
787 returns new full repository name based on assigned group and new new
789 returns new full repository name based on assigned group and new new
788
790
789 :param group_name:
791 :param group_name:
790 """
792 """
791 path_prefix = self.group.full_path_splitted if self.group else []
793 path_prefix = self.group.full_path_splitted if self.group else []
792 return Repository.url_sep().join(path_prefix + [repo_name])
794 return Repository.url_sep().join(path_prefix + [repo_name])
793
795
794 @property
796 @property
795 def _ui(self):
797 def _ui(self):
796 """
798 """
797 Creates an db based ui object for this repository
799 Creates an db based ui object for this repository
798 """
800 """
799 from rhodecode.lib.utils import make_ui
801 from rhodecode.lib.utils import make_ui
800 return make_ui('db', clear_session=False)
802 return make_ui('db', clear_session=False)
801
803
802 @classmethod
804 @classmethod
803 def inject_ui(cls, repo, extras={}):
805 def inject_ui(cls, repo, extras={}):
804 from rhodecode.lib.vcs.backends.hg import MercurialRepository
806 from rhodecode.lib.vcs.backends.hg import MercurialRepository
805 from rhodecode.lib.vcs.backends.git import GitRepository
807 from rhodecode.lib.vcs.backends.git import GitRepository
806 required = (MercurialRepository, GitRepository)
808 required = (MercurialRepository, GitRepository)
807 if not isinstance(repo, required):
809 if not isinstance(repo, required):
808 raise Exception('repo must be instance of %s' % required)
810 raise Exception('repo must be instance of %s' % required)
809
811
810 # inject ui extra param to log this action via push logger
812 # inject ui extra param to log this action via push logger
811 for k, v in extras.items():
813 for k, v in extras.items():
812 repo._repo.ui.setconfig('rhodecode_extras', k, v)
814 repo._repo.ui.setconfig('rhodecode_extras', k, v)
813
815
814 @classmethod
816 @classmethod
815 def is_valid(cls, repo_name):
817 def is_valid(cls, repo_name):
816 """
818 """
817 returns True if given repo name is a valid filesystem repository
819 returns True if given repo name is a valid filesystem repository
818
820
819 :param cls:
821 :param cls:
820 :param repo_name:
822 :param repo_name:
821 """
823 """
822 from rhodecode.lib.utils import is_valid_repo
824 from rhodecode.lib.utils import is_valid_repo
823
825
824 return is_valid_repo(repo_name, cls.base_path())
826 return is_valid_repo(repo_name, cls.base_path())
825
827
826 def get_api_data(self):
828 def get_api_data(self):
827 """
829 """
828 Common function for generating repo api data
830 Common function for generating repo api data
829
831
830 """
832 """
831 repo = self
833 repo = self
832 data = dict(
834 data = dict(
833 repo_id=repo.repo_id,
835 repo_id=repo.repo_id,
834 repo_name=repo.repo_name,
836 repo_name=repo.repo_name,
835 repo_type=repo.repo_type,
837 repo_type=repo.repo_type,
836 clone_uri=repo.clone_uri,
838 clone_uri=repo.clone_uri,
837 private=repo.private,
839 private=repo.private,
838 created_on=repo.created_on,
840 created_on=repo.created_on,
839 description=repo.description,
841 description=repo.description,
840 landing_rev=repo.landing_rev,
842 landing_rev=repo.landing_rev,
841 owner=repo.user.username,
843 owner=repo.user.username,
842 fork_of=repo.fork.repo_name if repo.fork else None
844 fork_of=repo.fork.repo_name if repo.fork else None
843 )
845 )
844
846
845 return data
847 return data
846
848
847 @classmethod
849 @classmethod
848 def lock(cls, repo, user_id):
850 def lock(cls, repo, user_id):
849 repo.locked = [user_id, time.time()]
851 repo.locked = [user_id, time.time()]
850 Session().add(repo)
852 Session().add(repo)
851 Session().commit()
853 Session().commit()
852
854
853 @classmethod
855 @classmethod
854 def unlock(cls, repo):
856 def unlock(cls, repo):
855 repo.locked = None
857 repo.locked = None
856 Session().add(repo)
858 Session().add(repo)
857 Session().commit()
859 Session().commit()
858
860
859 @property
861 @property
860 def last_db_change(self):
862 def last_db_change(self):
861 return self.updated_on
863 return self.updated_on
862
864
863 #==========================================================================
865 #==========================================================================
864 # SCM PROPERTIES
866 # SCM PROPERTIES
865 #==========================================================================
867 #==========================================================================
866
868
867 def get_changeset(self, rev=None):
869 def get_changeset(self, rev=None):
868 return get_changeset_safe(self.scm_instance, rev)
870 return get_changeset_safe(self.scm_instance, rev)
869
871
870 def get_landing_changeset(self):
872 def get_landing_changeset(self):
871 """
873 """
872 Returns landing changeset, or if that doesn't exist returns the tip
874 Returns landing changeset, or if that doesn't exist returns the tip
873 """
875 """
874 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
876 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
875 return cs
877 return cs
876
878
877 def update_last_change(self, last_change=None):
879 def update_last_change(self, last_change=None):
878 if last_change is None:
880 if last_change is None:
879 last_change = datetime.datetime.now()
881 last_change = datetime.datetime.now()
880 if self.updated_on is None or self.updated_on != last_change:
882 if self.updated_on is None or self.updated_on != last_change:
881 log.debug('updated repo %s with new date %s' % (self, last_change))
883 log.debug('updated repo %s with new date %s' % (self, last_change))
882 self.updated_on = last_change
884 self.updated_on = last_change
883 Session().add(self)
885 Session().add(self)
884 Session().commit()
886 Session().commit()
885
887
886 @property
888 @property
887 def tip(self):
889 def tip(self):
888 return self.get_changeset('tip')
890 return self.get_changeset('tip')
889
891
890 @property
892 @property
891 def author(self):
893 def author(self):
892 return self.tip.author
894 return self.tip.author
893
895
894 @property
896 @property
895 def last_change(self):
897 def last_change(self):
896 return self.scm_instance.last_change
898 return self.scm_instance.last_change
897
899
898 def get_comments(self, revisions=None):
900 def get_comments(self, revisions=None):
899 """
901 """
900 Returns comments for this repository grouped by revisions
902 Returns comments for this repository grouped by revisions
901
903
902 :param revisions: filter query by revisions only
904 :param revisions: filter query by revisions only
903 """
905 """
904 cmts = ChangesetComment.query()\
906 cmts = ChangesetComment.query()\
905 .filter(ChangesetComment.repo == self)
907 .filter(ChangesetComment.repo == self)
906 if revisions:
908 if revisions:
907 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
909 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
908 grouped = defaultdict(list)
910 grouped = defaultdict(list)
909 for cmt in cmts.all():
911 for cmt in cmts.all():
910 grouped[cmt.revision].append(cmt)
912 grouped[cmt.revision].append(cmt)
911 return grouped
913 return grouped
912
914
913 def statuses(self, revisions=None):
915 def statuses(self, revisions=None):
914 """
916 """
915 Returns statuses for this repository
917 Returns statuses for this repository
916
918
917 :param revisions: list of revisions to get statuses for
919 :param revisions: list of revisions to get statuses for
918 :type revisions: list
920 :type revisions: list
919 """
921 """
920
922
921 statuses = ChangesetStatus.query()\
923 statuses = ChangesetStatus.query()\
922 .filter(ChangesetStatus.repo == self)\
924 .filter(ChangesetStatus.repo == self)\
923 .filter(ChangesetStatus.version == 0)
925 .filter(ChangesetStatus.version == 0)
924 if revisions:
926 if revisions:
925 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
927 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
926 grouped = {}
928 grouped = {}
927
929
928 #maybe we have open new pullrequest without a status ?
930 #maybe we have open new pullrequest without a status ?
929 stat = ChangesetStatus.STATUS_UNDER_REVIEW
931 stat = ChangesetStatus.STATUS_UNDER_REVIEW
930 status_lbl = ChangesetStatus.get_status_lbl(stat)
932 status_lbl = ChangesetStatus.get_status_lbl(stat)
931 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
933 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
932 for rev in pr.revisions:
934 for rev in pr.revisions:
933 pr_id = pr.pull_request_id
935 pr_id = pr.pull_request_id
934 pr_repo = pr.other_repo.repo_name
936 pr_repo = pr.other_repo.repo_name
935 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
937 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
936
938
937 for stat in statuses.all():
939 for stat in statuses.all():
938 pr_id = pr_repo = None
940 pr_id = pr_repo = None
939 if stat.pull_request:
941 if stat.pull_request:
940 pr_id = stat.pull_request.pull_request_id
942 pr_id = stat.pull_request.pull_request_id
941 pr_repo = stat.pull_request.other_repo.repo_name
943 pr_repo = stat.pull_request.other_repo.repo_name
942 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
944 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
943 pr_id, pr_repo]
945 pr_id, pr_repo]
944 return grouped
946 return grouped
945
947
946 #==========================================================================
948 #==========================================================================
947 # SCM CACHE INSTANCE
949 # SCM CACHE INSTANCE
948 #==========================================================================
950 #==========================================================================
949
951
950 @property
952 @property
951 def invalidate(self):
953 def invalidate(self):
952 return CacheInvalidation.invalidate(self.repo_name)
954 return CacheInvalidation.invalidate(self.repo_name)
953
955
954 def set_invalidate(self):
956 def set_invalidate(self):
955 """
957 """
956 set a cache for invalidation for this instance
958 set a cache for invalidation for this instance
957 """
959 """
958 CacheInvalidation.set_invalidate(repo_name=self.repo_name)
960 CacheInvalidation.set_invalidate(repo_name=self.repo_name)
959
961
960 @LazyProperty
962 @LazyProperty
961 def scm_instance(self):
963 def scm_instance(self):
962 import rhodecode
964 import rhodecode
963 full_cache = str2bool(rhodecode.CONFIG.get('vcs_full_cache'))
965 full_cache = str2bool(rhodecode.CONFIG.get('vcs_full_cache'))
964 if full_cache:
966 if full_cache:
965 return self.scm_instance_cached()
967 return self.scm_instance_cached()
966 return self.__get_instance()
968 return self.__get_instance()
967
969
968 def scm_instance_cached(self, cache_map=None):
970 def scm_instance_cached(self, cache_map=None):
969 @cache_region('long_term')
971 @cache_region('long_term')
970 def _c(repo_name):
972 def _c(repo_name):
971 return self.__get_instance()
973 return self.__get_instance()
972 rn = self.repo_name
974 rn = self.repo_name
973 log.debug('Getting cached instance of repo')
975 log.debug('Getting cached instance of repo')
974
976
975 if cache_map:
977 if cache_map:
976 # get using prefilled cache_map
978 # get using prefilled cache_map
977 invalidate_repo = cache_map[self.repo_name]
979 invalidate_repo = cache_map[self.repo_name]
978 if invalidate_repo:
980 if invalidate_repo:
979 invalidate_repo = (None if invalidate_repo.cache_active
981 invalidate_repo = (None if invalidate_repo.cache_active
980 else invalidate_repo)
982 else invalidate_repo)
981 else:
983 else:
982 # get from invalidate
984 # get from invalidate
983 invalidate_repo = self.invalidate
985 invalidate_repo = self.invalidate
984
986
985 if invalidate_repo is not None:
987 if invalidate_repo is not None:
986 region_invalidate(_c, None, rn)
988 region_invalidate(_c, None, rn)
987 # update our cache
989 # update our cache
988 CacheInvalidation.set_valid(invalidate_repo.cache_key)
990 CacheInvalidation.set_valid(invalidate_repo.cache_key)
989 return _c(rn)
991 return _c(rn)
990
992
991 def __get_instance(self):
993 def __get_instance(self):
992 repo_full_path = self.repo_full_path
994 repo_full_path = self.repo_full_path
993 try:
995 try:
994 alias = get_scm(repo_full_path)[0]
996 alias = get_scm(repo_full_path)[0]
995 log.debug('Creating instance of %s repository' % alias)
997 log.debug('Creating instance of %s repository' % alias)
996 backend = get_backend(alias)
998 backend = get_backend(alias)
997 except VCSError:
999 except VCSError:
998 log.error(traceback.format_exc())
1000 log.error(traceback.format_exc())
999 log.error('Perhaps this repository is in db and not in '
1001 log.error('Perhaps this repository is in db and not in '
1000 'filesystem run rescan repositories with '
1002 'filesystem run rescan repositories with '
1001 '"destroy old data " option from admin panel')
1003 '"destroy old data " option from admin panel')
1002 return
1004 return
1003
1005
1004 if alias == 'hg':
1006 if alias == 'hg':
1005
1007
1006 repo = backend(safe_str(repo_full_path), create=False,
1008 repo = backend(safe_str(repo_full_path), create=False,
1007 baseui=self._ui)
1009 baseui=self._ui)
1008 # skip hidden web repository
1010 # skip hidden web repository
1009 if repo._get_hidden():
1011 if repo._get_hidden():
1010 return
1012 return
1011 else:
1013 else:
1012 repo = backend(repo_full_path, create=False)
1014 repo = backend(repo_full_path, create=False)
1013
1015
1014 return repo
1016 return repo
1015
1017
1016
1018
1017 class RepoGroup(Base, BaseModel):
1019 class RepoGroup(Base, BaseModel):
1018 __tablename__ = 'groups'
1020 __tablename__ = 'groups'
1019 __table_args__ = (
1021 __table_args__ = (
1020 UniqueConstraint('group_name', 'group_parent_id'),
1022 UniqueConstraint('group_name', 'group_parent_id'),
1021 CheckConstraint('group_id != group_parent_id'),
1023 CheckConstraint('group_id != group_parent_id'),
1022 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1024 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1023 'mysql_charset': 'utf8'},
1025 'mysql_charset': 'utf8'},
1024 )
1026 )
1025 __mapper_args__ = {'order_by': 'group_name'}
1027 __mapper_args__ = {'order_by': 'group_name'}
1026
1028
1027 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1029 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1028 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
1030 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
1029 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
1031 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
1030 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1032 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1031 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
1033 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
1032
1034
1033 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
1035 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
1034 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
1036 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
1035
1037
1036 parent_group = relationship('RepoGroup', remote_side=group_id)
1038 parent_group = relationship('RepoGroup', remote_side=group_id)
1037
1039
1038 def __init__(self, group_name='', parent_group=None):
1040 def __init__(self, group_name='', parent_group=None):
1039 self.group_name = group_name
1041 self.group_name = group_name
1040 self.parent_group = parent_group
1042 self.parent_group = parent_group
1041
1043
1042 def __unicode__(self):
1044 def __unicode__(self):
1043 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
1045 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
1044 self.group_name)
1046 self.group_name)
1045
1047
1046 @classmethod
1048 @classmethod
1047 def groups_choices(cls, check_perms=False):
1049 def groups_choices(cls, check_perms=False):
1048 from webhelpers.html import literal as _literal
1050 from webhelpers.html import literal as _literal
1049 from rhodecode.model.scm import ScmModel
1051 from rhodecode.model.scm import ScmModel
1050 groups = cls.query().all()
1052 groups = cls.query().all()
1051 if check_perms:
1053 if check_perms:
1052 #filter group user have access to, it's done
1054 #filter group user have access to, it's done
1053 #magically inside ScmModel based on current user
1055 #magically inside ScmModel based on current user
1054 groups = ScmModel().get_repos_groups(groups)
1056 groups = ScmModel().get_repos_groups(groups)
1055 repo_groups = [('', '')]
1057 repo_groups = [('', '')]
1056 sep = ' &raquo; '
1058 sep = ' &raquo; '
1057 _name = lambda k: _literal(sep.join(k))
1059 _name = lambda k: _literal(sep.join(k))
1058
1060
1059 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
1061 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
1060 for x in groups])
1062 for x in groups])
1061
1063
1062 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
1064 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
1063 return repo_groups
1065 return repo_groups
1064
1066
1065 @classmethod
1067 @classmethod
1066 def url_sep(cls):
1068 def url_sep(cls):
1067 return URL_SEP
1069 return URL_SEP
1068
1070
1069 @classmethod
1071 @classmethod
1070 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
1072 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
1071 if case_insensitive:
1073 if case_insensitive:
1072 gr = cls.query()\
1074 gr = cls.query()\
1073 .filter(cls.group_name.ilike(group_name))
1075 .filter(cls.group_name.ilike(group_name))
1074 else:
1076 else:
1075 gr = cls.query()\
1077 gr = cls.query()\
1076 .filter(cls.group_name == group_name)
1078 .filter(cls.group_name == group_name)
1077 if cache:
1079 if cache:
1078 gr = gr.options(FromCache(
1080 gr = gr.options(FromCache(
1079 "sql_cache_short",
1081 "sql_cache_short",
1080 "get_group_%s" % _hash_key(group_name)
1082 "get_group_%s" % _hash_key(group_name)
1081 )
1083 )
1082 )
1084 )
1083 return gr.scalar()
1085 return gr.scalar()
1084
1086
1085 @property
1087 @property
1086 def parents(self):
1088 def parents(self):
1087 parents_recursion_limit = 5
1089 parents_recursion_limit = 5
1088 groups = []
1090 groups = []
1089 if self.parent_group is None:
1091 if self.parent_group is None:
1090 return groups
1092 return groups
1091 cur_gr = self.parent_group
1093 cur_gr = self.parent_group
1092 groups.insert(0, cur_gr)
1094 groups.insert(0, cur_gr)
1093 cnt = 0
1095 cnt = 0
1094 while 1:
1096 while 1:
1095 cnt += 1
1097 cnt += 1
1096 gr = getattr(cur_gr, 'parent_group', None)
1098 gr = getattr(cur_gr, 'parent_group', None)
1097 cur_gr = cur_gr.parent_group
1099 cur_gr = cur_gr.parent_group
1098 if gr is None:
1100 if gr is None:
1099 break
1101 break
1100 if cnt == parents_recursion_limit:
1102 if cnt == parents_recursion_limit:
1101 # this will prevent accidental infinit loops
1103 # this will prevent accidental infinit loops
1102 log.error('group nested more than %s' %
1104 log.error('group nested more than %s' %
1103 parents_recursion_limit)
1105 parents_recursion_limit)
1104 break
1106 break
1105
1107
1106 groups.insert(0, gr)
1108 groups.insert(0, gr)
1107 return groups
1109 return groups
1108
1110
1109 @property
1111 @property
1110 def children(self):
1112 def children(self):
1111 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1113 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1112
1114
1113 @property
1115 @property
1114 def name(self):
1116 def name(self):
1115 return self.group_name.split(RepoGroup.url_sep())[-1]
1117 return self.group_name.split(RepoGroup.url_sep())[-1]
1116
1118
1117 @property
1119 @property
1118 def full_path(self):
1120 def full_path(self):
1119 return self.group_name
1121 return self.group_name
1120
1122
1121 @property
1123 @property
1122 def full_path_splitted(self):
1124 def full_path_splitted(self):
1123 return self.group_name.split(RepoGroup.url_sep())
1125 return self.group_name.split(RepoGroup.url_sep())
1124
1126
1125 @property
1127 @property
1126 def repositories(self):
1128 def repositories(self):
1127 return Repository.query()\
1129 return Repository.query()\
1128 .filter(Repository.group == self)\
1130 .filter(Repository.group == self)\
1129 .order_by(Repository.repo_name)
1131 .order_by(Repository.repo_name)
1130
1132
1131 @property
1133 @property
1132 def repositories_recursive_count(self):
1134 def repositories_recursive_count(self):
1133 cnt = self.repositories.count()
1135 cnt = self.repositories.count()
1134
1136
1135 def children_count(group):
1137 def children_count(group):
1136 cnt = 0
1138 cnt = 0
1137 for child in group.children:
1139 for child in group.children:
1138 cnt += child.repositories.count()
1140 cnt += child.repositories.count()
1139 cnt += children_count(child)
1141 cnt += children_count(child)
1140 return cnt
1142 return cnt
1141
1143
1142 return cnt + children_count(self)
1144 return cnt + children_count(self)
1143
1145
1144 def recursive_groups_and_repos(self):
1146 def recursive_groups_and_repos(self):
1145 """
1147 """
1146 Recursive return all groups, with repositories in those groups
1148 Recursive return all groups, with repositories in those groups
1147 """
1149 """
1148 all_ = []
1150 all_ = []
1149
1151
1150 def _get_members(root_gr):
1152 def _get_members(root_gr):
1151 for r in root_gr.repositories:
1153 for r in root_gr.repositories:
1152 all_.append(r)
1154 all_.append(r)
1153 childs = root_gr.children.all()
1155 childs = root_gr.children.all()
1154 if childs:
1156 if childs:
1155 for gr in childs:
1157 for gr in childs:
1156 all_.append(gr)
1158 all_.append(gr)
1157 _get_members(gr)
1159 _get_members(gr)
1158
1160
1159 _get_members(self)
1161 _get_members(self)
1160 return [self] + all_
1162 return [self] + all_
1161
1163
1162 def get_new_name(self, group_name):
1164 def get_new_name(self, group_name):
1163 """
1165 """
1164 returns new full group name based on parent and new name
1166 returns new full group name based on parent and new name
1165
1167
1166 :param group_name:
1168 :param group_name:
1167 """
1169 """
1168 path_prefix = (self.parent_group.full_path_splitted if
1170 path_prefix = (self.parent_group.full_path_splitted if
1169 self.parent_group else [])
1171 self.parent_group else [])
1170 return RepoGroup.url_sep().join(path_prefix + [group_name])
1172 return RepoGroup.url_sep().join(path_prefix + [group_name])
1171
1173
1172
1174
1173 class Permission(Base, BaseModel):
1175 class Permission(Base, BaseModel):
1174 __tablename__ = 'permissions'
1176 __tablename__ = 'permissions'
1175 __table_args__ = (
1177 __table_args__ = (
1176 Index('p_perm_name_idx', 'permission_name'),
1178 Index('p_perm_name_idx', 'permission_name'),
1177 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1179 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1178 'mysql_charset': 'utf8'},
1180 'mysql_charset': 'utf8'},
1179 )
1181 )
1180 PERMS = [
1182 PERMS = [
1181 ('repository.none', _('Repository no access')),
1183 ('repository.none', _('Repository no access')),
1182 ('repository.read', _('Repository read access')),
1184 ('repository.read', _('Repository read access')),
1183 ('repository.write', _('Repository write access')),
1185 ('repository.write', _('Repository write access')),
1184 ('repository.admin', _('Repository admin access')),
1186 ('repository.admin', _('Repository admin access')),
1185
1187
1186 ('group.none', _('Repositories Group no access')),
1188 ('group.none', _('Repositories Group no access')),
1187 ('group.read', _('Repositories Group read access')),
1189 ('group.read', _('Repositories Group read access')),
1188 ('group.write', _('Repositories Group write access')),
1190 ('group.write', _('Repositories Group write access')),
1189 ('group.admin', _('Repositories Group admin access')),
1191 ('group.admin', _('Repositories Group admin access')),
1190
1192
1191 ('hg.admin', _('RhodeCode Administrator')),
1193 ('hg.admin', _('RhodeCode Administrator')),
1192 ('hg.create.none', _('Repository creation disabled')),
1194 ('hg.create.none', _('Repository creation disabled')),
1193 ('hg.create.repository', _('Repository creation enabled')),
1195 ('hg.create.repository', _('Repository creation enabled')),
1194 ('hg.fork.none', _('Repository forking disabled')),
1196 ('hg.fork.none', _('Repository forking disabled')),
1195 ('hg.fork.repository', _('Repository forking enabled')),
1197 ('hg.fork.repository', _('Repository forking enabled')),
1196 ('hg.register.none', _('Register disabled')),
1198 ('hg.register.none', _('Register disabled')),
1197 ('hg.register.manual_activate', _('Register new user with RhodeCode '
1199 ('hg.register.manual_activate', _('Register new user with RhodeCode '
1198 'with manual activation')),
1200 'with manual activation')),
1199
1201
1200 ('hg.register.auto_activate', _('Register new user with RhodeCode '
1202 ('hg.register.auto_activate', _('Register new user with RhodeCode '
1201 'with auto activation')),
1203 'with auto activation')),
1202 ]
1204 ]
1203
1205
1204 # defines which permissions are more important higher the more important
1206 # defines which permissions are more important higher the more important
1205 PERM_WEIGHTS = {
1207 PERM_WEIGHTS = {
1206 'repository.none': 0,
1208 'repository.none': 0,
1207 'repository.read': 1,
1209 'repository.read': 1,
1208 'repository.write': 3,
1210 'repository.write': 3,
1209 'repository.admin': 4,
1211 'repository.admin': 4,
1210
1212
1211 'group.none': 0,
1213 'group.none': 0,
1212 'group.read': 1,
1214 'group.read': 1,
1213 'group.write': 3,
1215 'group.write': 3,
1214 'group.admin': 4,
1216 'group.admin': 4,
1215
1217
1216 'hg.fork.none': 0,
1218 'hg.fork.none': 0,
1217 'hg.fork.repository': 1,
1219 'hg.fork.repository': 1,
1218 'hg.create.none': 0,
1220 'hg.create.none': 0,
1219 'hg.create.repository':1
1221 'hg.create.repository':1
1220 }
1222 }
1221
1223
1222 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1224 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1223 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1225 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1224 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1226 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1225
1227
1226 def __unicode__(self):
1228 def __unicode__(self):
1227 return u"<%s('%s:%s')>" % (
1229 return u"<%s('%s:%s')>" % (
1228 self.__class__.__name__, self.permission_id, self.permission_name
1230 self.__class__.__name__, self.permission_id, self.permission_name
1229 )
1231 )
1230
1232
1231 @classmethod
1233 @classmethod
1232 def get_by_key(cls, key):
1234 def get_by_key(cls, key):
1233 return cls.query().filter(cls.permission_name == key).scalar()
1235 return cls.query().filter(cls.permission_name == key).scalar()
1234
1236
1235 @classmethod
1237 @classmethod
1236 def get_default_perms(cls, default_user_id):
1238 def get_default_perms(cls, default_user_id):
1237 q = Session().query(UserRepoToPerm, Repository, cls)\
1239 q = Session().query(UserRepoToPerm, Repository, cls)\
1238 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1240 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1239 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1241 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1240 .filter(UserRepoToPerm.user_id == default_user_id)
1242 .filter(UserRepoToPerm.user_id == default_user_id)
1241
1243
1242 return q.all()
1244 return q.all()
1243
1245
1244 @classmethod
1246 @classmethod
1245 def get_default_group_perms(cls, default_user_id):
1247 def get_default_group_perms(cls, default_user_id):
1246 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1248 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1247 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1249 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1248 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1250 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1249 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1251 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1250
1252
1251 return q.all()
1253 return q.all()
1252
1254
1253
1255
1254 class UserRepoToPerm(Base, BaseModel):
1256 class UserRepoToPerm(Base, BaseModel):
1255 __tablename__ = 'repo_to_perm'
1257 __tablename__ = 'repo_to_perm'
1256 __table_args__ = (
1258 __table_args__ = (
1257 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1259 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1258 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1260 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1259 'mysql_charset': 'utf8'}
1261 'mysql_charset': 'utf8'}
1260 )
1262 )
1261 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1263 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1262 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1264 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1263 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1265 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1264 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1266 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1265
1267
1266 user = relationship('User')
1268 user = relationship('User')
1267 repository = relationship('Repository')
1269 repository = relationship('Repository')
1268 permission = relationship('Permission')
1270 permission = relationship('Permission')
1269
1271
1270 @classmethod
1272 @classmethod
1271 def create(cls, user, repository, permission):
1273 def create(cls, user, repository, permission):
1272 n = cls()
1274 n = cls()
1273 n.user = user
1275 n.user = user
1274 n.repository = repository
1276 n.repository = repository
1275 n.permission = permission
1277 n.permission = permission
1276 Session().add(n)
1278 Session().add(n)
1277 return n
1279 return n
1278
1280
1279 def __unicode__(self):
1281 def __unicode__(self):
1280 return u'<user:%s => %s >' % (self.user, self.repository)
1282 return u'<user:%s => %s >' % (self.user, self.repository)
1281
1283
1282
1284
1283 class UserToPerm(Base, BaseModel):
1285 class UserToPerm(Base, BaseModel):
1284 __tablename__ = 'user_to_perm'
1286 __tablename__ = 'user_to_perm'
1285 __table_args__ = (
1287 __table_args__ = (
1286 UniqueConstraint('user_id', 'permission_id'),
1288 UniqueConstraint('user_id', 'permission_id'),
1287 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1289 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1288 'mysql_charset': 'utf8'}
1290 'mysql_charset': 'utf8'}
1289 )
1291 )
1290 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1292 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1291 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1293 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1292 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1294 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1293
1295
1294 user = relationship('User')
1296 user = relationship('User')
1295 permission = relationship('Permission', lazy='joined')
1297 permission = relationship('Permission', lazy='joined')
1296
1298
1297
1299
1298 class UsersGroupRepoToPerm(Base, BaseModel):
1300 class UsersGroupRepoToPerm(Base, BaseModel):
1299 __tablename__ = 'users_group_repo_to_perm'
1301 __tablename__ = 'users_group_repo_to_perm'
1300 __table_args__ = (
1302 __table_args__ = (
1301 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1303 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1302 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1304 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1303 'mysql_charset': 'utf8'}
1305 'mysql_charset': 'utf8'}
1304 )
1306 )
1305 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1307 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1306 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1308 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1307 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1309 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1308 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1310 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1309
1311
1310 users_group = relationship('UsersGroup')
1312 users_group = relationship('UsersGroup')
1311 permission = relationship('Permission')
1313 permission = relationship('Permission')
1312 repository = relationship('Repository')
1314 repository = relationship('Repository')
1313
1315
1314 @classmethod
1316 @classmethod
1315 def create(cls, users_group, repository, permission):
1317 def create(cls, users_group, repository, permission):
1316 n = cls()
1318 n = cls()
1317 n.users_group = users_group
1319 n.users_group = users_group
1318 n.repository = repository
1320 n.repository = repository
1319 n.permission = permission
1321 n.permission = permission
1320 Session().add(n)
1322 Session().add(n)
1321 return n
1323 return n
1322
1324
1323 def __unicode__(self):
1325 def __unicode__(self):
1324 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1326 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1325
1327
1326
1328
1327 class UsersGroupToPerm(Base, BaseModel):
1329 class UsersGroupToPerm(Base, BaseModel):
1328 __tablename__ = 'users_group_to_perm'
1330 __tablename__ = 'users_group_to_perm'
1329 __table_args__ = (
1331 __table_args__ = (
1330 UniqueConstraint('users_group_id', 'permission_id',),
1332 UniqueConstraint('users_group_id', 'permission_id',),
1331 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1333 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1332 'mysql_charset': 'utf8'}
1334 'mysql_charset': 'utf8'}
1333 )
1335 )
1334 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1336 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1335 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1337 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1336 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1338 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1337
1339
1338 users_group = relationship('UsersGroup')
1340 users_group = relationship('UsersGroup')
1339 permission = relationship('Permission')
1341 permission = relationship('Permission')
1340
1342
1341
1343
1342 class UserRepoGroupToPerm(Base, BaseModel):
1344 class UserRepoGroupToPerm(Base, BaseModel):
1343 __tablename__ = 'user_repo_group_to_perm'
1345 __tablename__ = 'user_repo_group_to_perm'
1344 __table_args__ = (
1346 __table_args__ = (
1345 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1347 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1346 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1348 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1347 'mysql_charset': 'utf8'}
1349 'mysql_charset': 'utf8'}
1348 )
1350 )
1349
1351
1350 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1352 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1351 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1353 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1352 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1354 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1353 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1355 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1354
1356
1355 user = relationship('User')
1357 user = relationship('User')
1356 group = relationship('RepoGroup')
1358 group = relationship('RepoGroup')
1357 permission = relationship('Permission')
1359 permission = relationship('Permission')
1358
1360
1359
1361
1360 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1362 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1361 __tablename__ = 'users_group_repo_group_to_perm'
1363 __tablename__ = 'users_group_repo_group_to_perm'
1362 __table_args__ = (
1364 __table_args__ = (
1363 UniqueConstraint('users_group_id', 'group_id'),
1365 UniqueConstraint('users_group_id', 'group_id'),
1364 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1366 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1365 'mysql_charset': 'utf8'}
1367 'mysql_charset': 'utf8'}
1366 )
1368 )
1367
1369
1368 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)
1370 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)
1369 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1371 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1370 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1372 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1371 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1373 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1372
1374
1373 users_group = relationship('UsersGroup')
1375 users_group = relationship('UsersGroup')
1374 permission = relationship('Permission')
1376 permission = relationship('Permission')
1375 group = relationship('RepoGroup')
1377 group = relationship('RepoGroup')
1376
1378
1377
1379
1378 class Statistics(Base, BaseModel):
1380 class Statistics(Base, BaseModel):
1379 __tablename__ = 'statistics'
1381 __tablename__ = 'statistics'
1380 __table_args__ = (
1382 __table_args__ = (
1381 UniqueConstraint('repository_id'),
1383 UniqueConstraint('repository_id'),
1382 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1384 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1383 'mysql_charset': 'utf8'}
1385 'mysql_charset': 'utf8'}
1384 )
1386 )
1385 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1387 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1386 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1388 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1387 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1389 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1388 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1390 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1389 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1391 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1390 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1392 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1391
1393
1392 repository = relationship('Repository', single_parent=True)
1394 repository = relationship('Repository', single_parent=True)
1393
1395
1394
1396
1395 class UserFollowing(Base, BaseModel):
1397 class UserFollowing(Base, BaseModel):
1396 __tablename__ = 'user_followings'
1398 __tablename__ = 'user_followings'
1397 __table_args__ = (
1399 __table_args__ = (
1398 UniqueConstraint('user_id', 'follows_repository_id'),
1400 UniqueConstraint('user_id', 'follows_repository_id'),
1399 UniqueConstraint('user_id', 'follows_user_id'),
1401 UniqueConstraint('user_id', 'follows_user_id'),
1400 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1402 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1401 'mysql_charset': 'utf8'}
1403 'mysql_charset': 'utf8'}
1402 )
1404 )
1403
1405
1404 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1406 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1405 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1407 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1406 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1408 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1407 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1409 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1408 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1410 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1409
1411
1410 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1412 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1411
1413
1412 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1414 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1413 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1415 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1414
1416
1415 @classmethod
1417 @classmethod
1416 def get_repo_followers(cls, repo_id):
1418 def get_repo_followers(cls, repo_id):
1417 return cls.query().filter(cls.follows_repo_id == repo_id)
1419 return cls.query().filter(cls.follows_repo_id == repo_id)
1418
1420
1419
1421
1420 class CacheInvalidation(Base, BaseModel):
1422 class CacheInvalidation(Base, BaseModel):
1421 __tablename__ = 'cache_invalidation'
1423 __tablename__ = 'cache_invalidation'
1422 __table_args__ = (
1424 __table_args__ = (
1423 UniqueConstraint('cache_key'),
1425 UniqueConstraint('cache_key'),
1424 Index('key_idx', 'cache_key'),
1426 Index('key_idx', 'cache_key'),
1425 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1427 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1426 'mysql_charset': 'utf8'},
1428 'mysql_charset': 'utf8'},
1427 )
1429 )
1428 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1430 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1429 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1431 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1430 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1432 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1431 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1433 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1432
1434
1433 def __init__(self, cache_key, cache_args=''):
1435 def __init__(self, cache_key, cache_args=''):
1434 self.cache_key = cache_key
1436 self.cache_key = cache_key
1435 self.cache_args = cache_args
1437 self.cache_args = cache_args
1436 self.cache_active = False
1438 self.cache_active = False
1437
1439
1438 def __unicode__(self):
1440 def __unicode__(self):
1439 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1441 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1440 self.cache_id, self.cache_key)
1442 self.cache_id, self.cache_key)
1441
1443
1442 @property
1444 @property
1443 def prefix(self):
1445 def prefix(self):
1444 _split = self.cache_key.split(self.cache_args, 1)
1446 _split = self.cache_key.split(self.cache_args, 1)
1445 if _split and len(_split) == 2:
1447 if _split and len(_split) == 2:
1446 return _split[0]
1448 return _split[0]
1447 return ''
1449 return ''
1448
1450
1449 @classmethod
1451 @classmethod
1450 def clear_cache(cls):
1452 def clear_cache(cls):
1451 cls.query().delete()
1453 cls.query().delete()
1452
1454
1453 @classmethod
1455 @classmethod
1454 def _get_key(cls, key):
1456 def _get_key(cls, key):
1455 """
1457 """
1456 Wrapper for generating a key, together with a prefix
1458 Wrapper for generating a key, together with a prefix
1457
1459
1458 :param key:
1460 :param key:
1459 """
1461 """
1460 import rhodecode
1462 import rhodecode
1461 prefix = ''
1463 prefix = ''
1462 org_key = key
1464 org_key = key
1463 iid = rhodecode.CONFIG.get('instance_id')
1465 iid = rhodecode.CONFIG.get('instance_id')
1464 if iid:
1466 if iid:
1465 prefix = iid
1467 prefix = iid
1466
1468
1467 return "%s%s" % (prefix, key), prefix, org_key
1469 return "%s%s" % (prefix, key), prefix, org_key
1468
1470
1469 @classmethod
1471 @classmethod
1470 def get_by_key(cls, key):
1472 def get_by_key(cls, key):
1471 return cls.query().filter(cls.cache_key == key).scalar()
1473 return cls.query().filter(cls.cache_key == key).scalar()
1472
1474
1473 @classmethod
1475 @classmethod
1474 def get_by_repo_name(cls, repo_name):
1476 def get_by_repo_name(cls, repo_name):
1475 return cls.query().filter(cls.cache_args == repo_name).all()
1477 return cls.query().filter(cls.cache_args == repo_name).all()
1476
1478
1477 @classmethod
1479 @classmethod
1478 def _get_or_create_key(cls, key, repo_name, commit=True):
1480 def _get_or_create_key(cls, key, repo_name, commit=True):
1479 inv_obj = Session().query(cls).filter(cls.cache_key == key).scalar()
1481 inv_obj = Session().query(cls).filter(cls.cache_key == key).scalar()
1480 if not inv_obj:
1482 if not inv_obj:
1481 try:
1483 try:
1482 inv_obj = CacheInvalidation(key, repo_name)
1484 inv_obj = CacheInvalidation(key, repo_name)
1483 Session().add(inv_obj)
1485 Session().add(inv_obj)
1484 if commit:
1486 if commit:
1485 Session().commit()
1487 Session().commit()
1486 except Exception:
1488 except Exception:
1487 log.error(traceback.format_exc())
1489 log.error(traceback.format_exc())
1488 Session().rollback()
1490 Session().rollback()
1489 return inv_obj
1491 return inv_obj
1490
1492
1491 @classmethod
1493 @classmethod
1492 def invalidate(cls, key):
1494 def invalidate(cls, key):
1493 """
1495 """
1494 Returns Invalidation object if this given key should be invalidated
1496 Returns Invalidation object if this given key should be invalidated
1495 None otherwise. `cache_active = False` means that this cache
1497 None otherwise. `cache_active = False` means that this cache
1496 state is not valid and needs to be invalidated
1498 state is not valid and needs to be invalidated
1497
1499
1498 :param key:
1500 :param key:
1499 """
1501 """
1500 repo_name = key
1502 repo_name = key
1501 repo_name = remove_suffix(repo_name, '_README')
1503 repo_name = remove_suffix(repo_name, '_README')
1502 repo_name = remove_suffix(repo_name, '_RSS')
1504 repo_name = remove_suffix(repo_name, '_RSS')
1503 repo_name = remove_suffix(repo_name, '_ATOM')
1505 repo_name = remove_suffix(repo_name, '_ATOM')
1504
1506
1505 # adds instance prefix
1507 # adds instance prefix
1506 key, _prefix, _org_key = cls._get_key(key)
1508 key, _prefix, _org_key = cls._get_key(key)
1507 inv = cls._get_or_create_key(key, repo_name)
1509 inv = cls._get_or_create_key(key, repo_name)
1508
1510
1509 if inv and inv.cache_active is False:
1511 if inv and inv.cache_active is False:
1510 return inv
1512 return inv
1511
1513
1512 @classmethod
1514 @classmethod
1513 def set_invalidate(cls, key=None, repo_name=None):
1515 def set_invalidate(cls, key=None, repo_name=None):
1514 """
1516 """
1515 Mark this Cache key for invalidation, either by key or whole
1517 Mark this Cache key for invalidation, either by key or whole
1516 cache sets based on repo_name
1518 cache sets based on repo_name
1517
1519
1518 :param key:
1520 :param key:
1519 """
1521 """
1520 if key:
1522 if key:
1521 key, _prefix, _org_key = cls._get_key(key)
1523 key, _prefix, _org_key = cls._get_key(key)
1522 inv_objs = Session().query(cls).filter(cls.cache_key == key).all()
1524 inv_objs = Session().query(cls).filter(cls.cache_key == key).all()
1523 elif repo_name:
1525 elif repo_name:
1524 inv_objs = Session().query(cls).filter(cls.cache_args == repo_name).all()
1526 inv_objs = Session().query(cls).filter(cls.cache_args == repo_name).all()
1525
1527
1526 log.debug('marking %s key[s] for invalidation based on key=%s,repo_name=%s'
1528 log.debug('marking %s key[s] for invalidation based on key=%s,repo_name=%s'
1527 % (len(inv_objs), key, repo_name))
1529 % (len(inv_objs), key, repo_name))
1528 try:
1530 try:
1529 for inv_obj in inv_objs:
1531 for inv_obj in inv_objs:
1530 inv_obj.cache_active = False
1532 inv_obj.cache_active = False
1531 Session().add(inv_obj)
1533 Session().add(inv_obj)
1532 Session().commit()
1534 Session().commit()
1533 except Exception:
1535 except Exception:
1534 log.error(traceback.format_exc())
1536 log.error(traceback.format_exc())
1535 Session().rollback()
1537 Session().rollback()
1536
1538
1537 @classmethod
1539 @classmethod
1538 def set_valid(cls, key):
1540 def set_valid(cls, key):
1539 """
1541 """
1540 Mark this cache key as active and currently cached
1542 Mark this cache key as active and currently cached
1541
1543
1542 :param key:
1544 :param key:
1543 """
1545 """
1544 inv_obj = cls.get_by_key(key)
1546 inv_obj = cls.get_by_key(key)
1545 inv_obj.cache_active = True
1547 inv_obj.cache_active = True
1546 Session().add(inv_obj)
1548 Session().add(inv_obj)
1547 Session().commit()
1549 Session().commit()
1548
1550
1549 @classmethod
1551 @classmethod
1550 def get_cache_map(cls):
1552 def get_cache_map(cls):
1551
1553
1552 class cachemapdict(dict):
1554 class cachemapdict(dict):
1553
1555
1554 def __init__(self, *args, **kwargs):
1556 def __init__(self, *args, **kwargs):
1555 fixkey = kwargs.get('fixkey')
1557 fixkey = kwargs.get('fixkey')
1556 if fixkey:
1558 if fixkey:
1557 del kwargs['fixkey']
1559 del kwargs['fixkey']
1558 self.fixkey = fixkey
1560 self.fixkey = fixkey
1559 super(cachemapdict, self).__init__(*args, **kwargs)
1561 super(cachemapdict, self).__init__(*args, **kwargs)
1560
1562
1561 def __getattr__(self, name):
1563 def __getattr__(self, name):
1562 key = name
1564 key = name
1563 if self.fixkey:
1565 if self.fixkey:
1564 key, _prefix, _org_key = cls._get_key(key)
1566 key, _prefix, _org_key = cls._get_key(key)
1565 if key in self.__dict__:
1567 if key in self.__dict__:
1566 return self.__dict__[key]
1568 return self.__dict__[key]
1567 else:
1569 else:
1568 return self[key]
1570 return self[key]
1569
1571
1570 def __getitem__(self, key):
1572 def __getitem__(self, key):
1571 if self.fixkey:
1573 if self.fixkey:
1572 key, _prefix, _org_key = cls._get_key(key)
1574 key, _prefix, _org_key = cls._get_key(key)
1573 try:
1575 try:
1574 return super(cachemapdict, self).__getitem__(key)
1576 return super(cachemapdict, self).__getitem__(key)
1575 except KeyError:
1577 except KeyError:
1576 return
1578 return
1577
1579
1578 cache_map = cachemapdict(fixkey=True)
1580 cache_map = cachemapdict(fixkey=True)
1579 for obj in cls.query().all():
1581 for obj in cls.query().all():
1580 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1582 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1581 return cache_map
1583 return cache_map
1582
1584
1583
1585
1584 class ChangesetComment(Base, BaseModel):
1586 class ChangesetComment(Base, BaseModel):
1585 __tablename__ = 'changeset_comments'
1587 __tablename__ = 'changeset_comments'
1586 __table_args__ = (
1588 __table_args__ = (
1587 Index('cc_revision_idx', 'revision'),
1589 Index('cc_revision_idx', 'revision'),
1588 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1590 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1589 'mysql_charset': 'utf8'},
1591 'mysql_charset': 'utf8'},
1590 )
1592 )
1591 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1593 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1592 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1594 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1593 revision = Column('revision', String(40), nullable=True)
1595 revision = Column('revision', String(40), nullable=True)
1594 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1596 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1595 line_no = Column('line_no', Unicode(10), nullable=True)
1597 line_no = Column('line_no', Unicode(10), nullable=True)
1596 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1598 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1597 f_path = Column('f_path', Unicode(1000), nullable=True)
1599 f_path = Column('f_path', Unicode(1000), nullable=True)
1598 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1600 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1599 text = Column('text', UnicodeText(25000), nullable=False)
1601 text = Column('text', UnicodeText(25000), nullable=False)
1600 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1602 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1601 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1603 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1602
1604
1603 author = relationship('User', lazy='joined')
1605 author = relationship('User', lazy='joined')
1604 repo = relationship('Repository')
1606 repo = relationship('Repository')
1605 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1607 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1606 pull_request = relationship('PullRequest', lazy='joined')
1608 pull_request = relationship('PullRequest', lazy='joined')
1607
1609
1608 @classmethod
1610 @classmethod
1609 def get_users(cls, revision=None, pull_request_id=None):
1611 def get_users(cls, revision=None, pull_request_id=None):
1610 """
1612 """
1611 Returns user associated with this ChangesetComment. ie those
1613 Returns user associated with this ChangesetComment. ie those
1612 who actually commented
1614 who actually commented
1613
1615
1614 :param cls:
1616 :param cls:
1615 :param revision:
1617 :param revision:
1616 """
1618 """
1617 q = Session().query(User)\
1619 q = Session().query(User)\
1618 .join(ChangesetComment.author)
1620 .join(ChangesetComment.author)
1619 if revision:
1621 if revision:
1620 q = q.filter(cls.revision == revision)
1622 q = q.filter(cls.revision == revision)
1621 elif pull_request_id:
1623 elif pull_request_id:
1622 q = q.filter(cls.pull_request_id == pull_request_id)
1624 q = q.filter(cls.pull_request_id == pull_request_id)
1623 return q.all()
1625 return q.all()
1624
1626
1625
1627
1626 class ChangesetStatus(Base, BaseModel):
1628 class ChangesetStatus(Base, BaseModel):
1627 __tablename__ = 'changeset_statuses'
1629 __tablename__ = 'changeset_statuses'
1628 __table_args__ = (
1630 __table_args__ = (
1629 Index('cs_revision_idx', 'revision'),
1631 Index('cs_revision_idx', 'revision'),
1630 Index('cs_version_idx', 'version'),
1632 Index('cs_version_idx', 'version'),
1631 UniqueConstraint('repo_id', 'revision', 'version'),
1633 UniqueConstraint('repo_id', 'revision', 'version'),
1632 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1634 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1633 'mysql_charset': 'utf8'}
1635 'mysql_charset': 'utf8'}
1634 )
1636 )
1635 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1637 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1636 STATUS_APPROVED = 'approved'
1638 STATUS_APPROVED = 'approved'
1637 STATUS_REJECTED = 'rejected'
1639 STATUS_REJECTED = 'rejected'
1638 STATUS_UNDER_REVIEW = 'under_review'
1640 STATUS_UNDER_REVIEW = 'under_review'
1639
1641
1640 STATUSES = [
1642 STATUSES = [
1641 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1643 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1642 (STATUS_APPROVED, _("Approved")),
1644 (STATUS_APPROVED, _("Approved")),
1643 (STATUS_REJECTED, _("Rejected")),
1645 (STATUS_REJECTED, _("Rejected")),
1644 (STATUS_UNDER_REVIEW, _("Under Review")),
1646 (STATUS_UNDER_REVIEW, _("Under Review")),
1645 ]
1647 ]
1646
1648
1647 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1649 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1648 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1650 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1649 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1651 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1650 revision = Column('revision', String(40), nullable=False)
1652 revision = Column('revision', String(40), nullable=False)
1651 status = Column('status', String(128), nullable=False, default=DEFAULT)
1653 status = Column('status', String(128), nullable=False, default=DEFAULT)
1652 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1654 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1653 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1655 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1654 version = Column('version', Integer(), nullable=False, default=0)
1656 version = Column('version', Integer(), nullable=False, default=0)
1655 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1657 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1656
1658
1657 author = relationship('User', lazy='joined')
1659 author = relationship('User', lazy='joined')
1658 repo = relationship('Repository')
1660 repo = relationship('Repository')
1659 comment = relationship('ChangesetComment', lazy='joined')
1661 comment = relationship('ChangesetComment', lazy='joined')
1660 pull_request = relationship('PullRequest', lazy='joined')
1662 pull_request = relationship('PullRequest', lazy='joined')
1661
1663
1662 def __unicode__(self):
1664 def __unicode__(self):
1663 return u"<%s('%s:%s')>" % (
1665 return u"<%s('%s:%s')>" % (
1664 self.__class__.__name__,
1666 self.__class__.__name__,
1665 self.status, self.author
1667 self.status, self.author
1666 )
1668 )
1667
1669
1668 @classmethod
1670 @classmethod
1669 def get_status_lbl(cls, value):
1671 def get_status_lbl(cls, value):
1670 return dict(cls.STATUSES).get(value)
1672 return dict(cls.STATUSES).get(value)
1671
1673
1672 @property
1674 @property
1673 def status_lbl(self):
1675 def status_lbl(self):
1674 return ChangesetStatus.get_status_lbl(self.status)
1676 return ChangesetStatus.get_status_lbl(self.status)
1675
1677
1676
1678
1677 class PullRequest(Base, BaseModel):
1679 class PullRequest(Base, BaseModel):
1678 __tablename__ = 'pull_requests'
1680 __tablename__ = 'pull_requests'
1679 __table_args__ = (
1681 __table_args__ = (
1680 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1682 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1681 'mysql_charset': 'utf8'},
1683 'mysql_charset': 'utf8'},
1682 )
1684 )
1683
1685
1684 STATUS_NEW = u'new'
1686 STATUS_NEW = u'new'
1685 STATUS_OPEN = u'open'
1687 STATUS_OPEN = u'open'
1686 STATUS_CLOSED = u'closed'
1688 STATUS_CLOSED = u'closed'
1687
1689
1688 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1690 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1689 title = Column('title', Unicode(256), nullable=True)
1691 title = Column('title', Unicode(256), nullable=True)
1690 description = Column('description', UnicodeText(10240), nullable=True)
1692 description = Column('description', UnicodeText(10240), nullable=True)
1691 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1693 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1692 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1694 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1693 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1695 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1694 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1696 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1695 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1697 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1696 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1698 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1697 org_ref = Column('org_ref', Unicode(256), nullable=False)
1699 org_ref = Column('org_ref', Unicode(256), nullable=False)
1698 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1700 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1699 other_ref = Column('other_ref', Unicode(256), nullable=False)
1701 other_ref = Column('other_ref', Unicode(256), nullable=False)
1700
1702
1701 @hybrid_property
1703 @hybrid_property
1702 def revisions(self):
1704 def revisions(self):
1703 return self._revisions.split(':')
1705 return self._revisions.split(':')
1704
1706
1705 @revisions.setter
1707 @revisions.setter
1706 def revisions(self, val):
1708 def revisions(self, val):
1707 self._revisions = ':'.join(val)
1709 self._revisions = ':'.join(val)
1708
1710
1709 author = relationship('User', lazy='joined')
1711 author = relationship('User', lazy='joined')
1710 reviewers = relationship('PullRequestReviewers',
1712 reviewers = relationship('PullRequestReviewers',
1711 cascade="all, delete, delete-orphan")
1713 cascade="all, delete, delete-orphan")
1712 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1714 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1713 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1715 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1714 statuses = relationship('ChangesetStatus')
1716 statuses = relationship('ChangesetStatus')
1715 comments = relationship('ChangesetComment',
1717 comments = relationship('ChangesetComment',
1716 cascade="all, delete, delete-orphan")
1718 cascade="all, delete, delete-orphan")
1717
1719
1718 def is_closed(self):
1720 def is_closed(self):
1719 return self.status == self.STATUS_CLOSED
1721 return self.status == self.STATUS_CLOSED
1720
1722
1721 def __json__(self):
1723 def __json__(self):
1722 return dict(
1724 return dict(
1723 revisions=self.revisions
1725 revisions=self.revisions
1724 )
1726 )
1725
1727
1726
1728
1727 class PullRequestReviewers(Base, BaseModel):
1729 class PullRequestReviewers(Base, BaseModel):
1728 __tablename__ = 'pull_request_reviewers'
1730 __tablename__ = 'pull_request_reviewers'
1729 __table_args__ = (
1731 __table_args__ = (
1730 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1732 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1731 'mysql_charset': 'utf8'},
1733 'mysql_charset': 'utf8'},
1732 )
1734 )
1733
1735
1734 def __init__(self, user=None, pull_request=None):
1736 def __init__(self, user=None, pull_request=None):
1735 self.user = user
1737 self.user = user
1736 self.pull_request = pull_request
1738 self.pull_request = pull_request
1737
1739
1738 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1740 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1739 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1741 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1740 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1742 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1741
1743
1742 user = relationship('User')
1744 user = relationship('User')
1743 pull_request = relationship('PullRequest')
1745 pull_request = relationship('PullRequest')
1744
1746
1745
1747
1746 class Notification(Base, BaseModel):
1748 class Notification(Base, BaseModel):
1747 __tablename__ = 'notifications'
1749 __tablename__ = 'notifications'
1748 __table_args__ = (
1750 __table_args__ = (
1749 Index('notification_type_idx', 'type'),
1751 Index('notification_type_idx', 'type'),
1750 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1752 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1751 'mysql_charset': 'utf8'},
1753 'mysql_charset': 'utf8'},
1752 )
1754 )
1753
1755
1754 TYPE_CHANGESET_COMMENT = u'cs_comment'
1756 TYPE_CHANGESET_COMMENT = u'cs_comment'
1755 TYPE_MESSAGE = u'message'
1757 TYPE_MESSAGE = u'message'
1756 TYPE_MENTION = u'mention'
1758 TYPE_MENTION = u'mention'
1757 TYPE_REGISTRATION = u'registration'
1759 TYPE_REGISTRATION = u'registration'
1758 TYPE_PULL_REQUEST = u'pull_request'
1760 TYPE_PULL_REQUEST = u'pull_request'
1759 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1761 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1760
1762
1761 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1763 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1762 subject = Column('subject', Unicode(512), nullable=True)
1764 subject = Column('subject', Unicode(512), nullable=True)
1763 body = Column('body', UnicodeText(50000), nullable=True)
1765 body = Column('body', UnicodeText(50000), nullable=True)
1764 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1766 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1765 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1767 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1766 type_ = Column('type', Unicode(256))
1768 type_ = Column('type', Unicode(256))
1767
1769
1768 created_by_user = relationship('User')
1770 created_by_user = relationship('User')
1769 notifications_to_users = relationship('UserNotification', lazy='joined',
1771 notifications_to_users = relationship('UserNotification', lazy='joined',
1770 cascade="all, delete, delete-orphan")
1772 cascade="all, delete, delete-orphan")
1771
1773
1772 @property
1774 @property
1773 def recipients(self):
1775 def recipients(self):
1774 return [x.user for x in UserNotification.query()\
1776 return [x.user for x in UserNotification.query()\
1775 .filter(UserNotification.notification == self)\
1777 .filter(UserNotification.notification == self)\
1776 .order_by(UserNotification.user_id.asc()).all()]
1778 .order_by(UserNotification.user_id.asc()).all()]
1777
1779
1778 @classmethod
1780 @classmethod
1779 def create(cls, created_by, subject, body, recipients, type_=None):
1781 def create(cls, created_by, subject, body, recipients, type_=None):
1780 if type_ is None:
1782 if type_ is None:
1781 type_ = Notification.TYPE_MESSAGE
1783 type_ = Notification.TYPE_MESSAGE
1782
1784
1783 notification = cls()
1785 notification = cls()
1784 notification.created_by_user = created_by
1786 notification.created_by_user = created_by
1785 notification.subject = subject
1787 notification.subject = subject
1786 notification.body = body
1788 notification.body = body
1787 notification.type_ = type_
1789 notification.type_ = type_
1788 notification.created_on = datetime.datetime.now()
1790 notification.created_on = datetime.datetime.now()
1789
1791
1790 for u in recipients:
1792 for u in recipients:
1791 assoc = UserNotification()
1793 assoc = UserNotification()
1792 assoc.notification = notification
1794 assoc.notification = notification
1793 u.notifications.append(assoc)
1795 u.notifications.append(assoc)
1794 Session().add(notification)
1796 Session().add(notification)
1795 return notification
1797 return notification
1796
1798
1797 @property
1799 @property
1798 def description(self):
1800 def description(self):
1799 from rhodecode.model.notification import NotificationModel
1801 from rhodecode.model.notification import NotificationModel
1800 return NotificationModel().make_description(self)
1802 return NotificationModel().make_description(self)
1801
1803
1802
1804
1803 class UserNotification(Base, BaseModel):
1805 class UserNotification(Base, BaseModel):
1804 __tablename__ = 'user_to_notification'
1806 __tablename__ = 'user_to_notification'
1805 __table_args__ = (
1807 __table_args__ = (
1806 UniqueConstraint('user_id', 'notification_id'),
1808 UniqueConstraint('user_id', 'notification_id'),
1807 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1809 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1808 'mysql_charset': 'utf8'}
1810 'mysql_charset': 'utf8'}
1809 )
1811 )
1810 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1812 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1811 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1813 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1812 read = Column('read', Boolean, default=False)
1814 read = Column('read', Boolean, default=False)
1813 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1815 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1814
1816
1815 user = relationship('User', lazy="joined")
1817 user = relationship('User', lazy="joined")
1816 notification = relationship('Notification', lazy="joined",
1818 notification = relationship('Notification', lazy="joined",
1817 order_by=lambda: Notification.created_on.desc(),)
1819 order_by=lambda: Notification.created_on.desc(),)
1818
1820
1819 def mark_as_read(self):
1821 def mark_as_read(self):
1820 self.read = True
1822 self.read = True
1821 Session().add(self)
1823 Session().add(self)
1822
1824
1823
1825
1824 class DbMigrateVersion(Base, BaseModel):
1826 class DbMigrateVersion(Base, BaseModel):
1825 __tablename__ = 'db_migrate_version'
1827 __tablename__ = 'db_migrate_version'
1826 __table_args__ = (
1828 __table_args__ = (
1827 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1829 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1828 'mysql_charset': 'utf8'},
1830 'mysql_charset': 'utf8'},
1829 )
1831 )
1830 repository_id = Column('repository_id', String(250), primary_key=True)
1832 repository_id = Column('repository_id', String(250), primary_key=True)
1831 repository_path = Column('repository_path', Text)
1833 repository_path = Column('repository_path', Text)
1832 version = Column('version', Integer)
1834 version = Column('version', Integer)
General Comments 0
You need to be logged in to leave comments. Login now