##// END OF EJS Templates
sphinx doc cleanup
marcink -
r1819:9451a569 beta
parent child Browse files
Show More
@@ -1,1097 +1,1097
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) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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 from datetime import date
30 from datetime import date
31
31
32 from sqlalchemy import *
32 from sqlalchemy import *
33 from sqlalchemy.ext.hybrid import hybrid_property
33 from sqlalchemy.ext.hybrid import hybrid_property
34 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
34 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
35 from beaker.cache import cache_region, region_invalidate
35 from beaker.cache import cache_region, region_invalidate
36
36
37 from vcs import get_backend
37 from vcs import get_backend
38 from vcs.utils.helpers import get_scm
38 from vcs.utils.helpers import get_scm
39 from vcs.exceptions import VCSError
39 from vcs.exceptions import VCSError
40 from vcs.utils.lazy import LazyProperty
40 from vcs.utils.lazy import LazyProperty
41
41
42 from rhodecode.lib import str2bool, safe_str, get_changeset_safe, \
42 from rhodecode.lib import str2bool, safe_str, get_changeset_safe, \
43 generate_api_key, safe_unicode
43 generate_api_key, safe_unicode
44 from rhodecode.lib.exceptions import UsersGroupsAssignedException
44 from rhodecode.lib.exceptions import UsersGroupsAssignedException
45 from rhodecode.lib.compat import json
45 from rhodecode.lib.compat import json
46
46
47 from rhodecode.model.meta import Base, Session
47 from rhodecode.model.meta import Base, Session
48 from rhodecode.lib.caching_query import FromCache
48 from rhodecode.lib.caching_query import FromCache
49
49
50
50
51 log = logging.getLogger(__name__)
51 log = logging.getLogger(__name__)
52
52
53 #==============================================================================
53 #==============================================================================
54 # BASE CLASSES
54 # BASE CLASSES
55 #==============================================================================
55 #==============================================================================
56
56
57 class ModelSerializer(json.JSONEncoder):
57 class ModelSerializer(json.JSONEncoder):
58 """
58 """
59 Simple Serializer for JSON,
59 Simple Serializer for JSON,
60
60
61 usage::
61 usage::
62
62
63 to make object customized for serialization implement a __json__
63 to make object customized for serialization implement a __json__
64 method that will return a dict for serialization into json
64 method that will return a dict for serialization into json
65
65
66 example::
66 example::
67
67
68 class Task(object):
68 class Task(object):
69
69
70 def __init__(self, name, value):
70 def __init__(self, name, value):
71 self.name = name
71 self.name = name
72 self.value = value
72 self.value = value
73
73
74 def __json__(self):
74 def __json__(self):
75 return dict(name=self.name,
75 return dict(name=self.name,
76 value=self.value)
76 value=self.value)
77
77
78 """
78 """
79
79
80 def default(self, obj):
80 def default(self, obj):
81
81
82 if hasattr(obj, '__json__'):
82 if hasattr(obj, '__json__'):
83 return obj.__json__()
83 return obj.__json__()
84 else:
84 else:
85 return json.JSONEncoder.default(self, obj)
85 return json.JSONEncoder.default(self, obj)
86
86
87 class BaseModel(object):
87 class BaseModel(object):
88 """Base Model for all classess
88 """Base Model for all classess
89
89
90 """
90 """
91
91
92 @classmethod
92 @classmethod
93 def _get_keys(cls):
93 def _get_keys(cls):
94 """return column names for this model """
94 """return column names for this model """
95 return class_mapper(cls).c.keys()
95 return class_mapper(cls).c.keys()
96
96
97 def get_dict(self):
97 def get_dict(self):
98 """return dict with keys and values corresponding
98 """return dict with keys and values corresponding
99 to this model data """
99 to this model data """
100
100
101 d = {}
101 d = {}
102 for k in self._get_keys():
102 for k in self._get_keys():
103 d[k] = getattr(self, k)
103 d[k] = getattr(self, k)
104 return d
104 return d
105
105
106 def get_appstruct(self):
106 def get_appstruct(self):
107 """return list with keys and values tupples corresponding
107 """return list with keys and values tupples corresponding
108 to this model data """
108 to this model data """
109
109
110 l = []
110 l = []
111 for k in self._get_keys():
111 for k in self._get_keys():
112 l.append((k, getattr(self, k),))
112 l.append((k, getattr(self, k),))
113 return l
113 return l
114
114
115 def populate_obj(self, populate_dict):
115 def populate_obj(self, populate_dict):
116 """populate model with data from given populate_dict"""
116 """populate model with data from given populate_dict"""
117
117
118 for k in self._get_keys():
118 for k in self._get_keys():
119 if k in populate_dict:
119 if k in populate_dict:
120 setattr(self, k, populate_dict[k])
120 setattr(self, k, populate_dict[k])
121
121
122 @classmethod
122 @classmethod
123 def query(cls):
123 def query(cls):
124 return Session.query(cls)
124 return Session.query(cls)
125
125
126 @classmethod
126 @classmethod
127 def get(cls, id_):
127 def get(cls, id_):
128 if id_:
128 if id_:
129 return cls.query().get(id_)
129 return cls.query().get(id_)
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 Session.commit()
139 Session.commit()
140
140
141
141
142 class RhodeCodeSetting(Base, BaseModel):
142 class RhodeCodeSetting(Base, BaseModel):
143 __tablename__ = 'rhodecode_settings'
143 __tablename__ = 'rhodecode_settings'
144 __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
144 __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
145 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
145 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
146 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
146 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
147 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
147 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
148
148
149 def __init__(self, k='', v=''):
149 def __init__(self, k='', v=''):
150 self.app_settings_name = k
150 self.app_settings_name = k
151 self.app_settings_value = v
151 self.app_settings_value = v
152
152
153
153
154 @validates('_app_settings_value')
154 @validates('_app_settings_value')
155 def validate_settings_value(self, key, val):
155 def validate_settings_value(self, key, val):
156 assert type(val) == unicode
156 assert type(val) == unicode
157 return val
157 return val
158
158
159 @hybrid_property
159 @hybrid_property
160 def app_settings_value(self):
160 def app_settings_value(self):
161 v = self._app_settings_value
161 v = self._app_settings_value
162 if v == 'ldap_active':
162 if v == 'ldap_active':
163 v = str2bool(v)
163 v = str2bool(v)
164 return v
164 return v
165
165
166 @app_settings_value.setter
166 @app_settings_value.setter
167 def app_settings_value(self, val):
167 def app_settings_value(self, val):
168 """
168 """
169 Setter that will always make sure we use unicode in app_settings_value
169 Setter that will always make sure we use unicode in app_settings_value
170
170
171 :param val:
171 :param val:
172 """
172 """
173 self._app_settings_value = safe_unicode(val)
173 self._app_settings_value = safe_unicode(val)
174
174
175 def __repr__(self):
175 def __repr__(self):
176 return "<%s('%s:%s')>" % (self.__class__.__name__,
176 return "<%s('%s:%s')>" % (self.__class__.__name__,
177 self.app_settings_name, self.app_settings_value)
177 self.app_settings_name, self.app_settings_value)
178
178
179
179
180 @classmethod
180 @classmethod
181 def get_by_name(cls, ldap_key):
181 def get_by_name(cls, ldap_key):
182 return cls.query()\
182 return cls.query()\
183 .filter(cls.app_settings_name == ldap_key).scalar()
183 .filter(cls.app_settings_name == ldap_key).scalar()
184
184
185 @classmethod
185 @classmethod
186 def get_app_settings(cls, cache=False):
186 def get_app_settings(cls, cache=False):
187
187
188 ret = cls.query()
188 ret = cls.query()
189
189
190 if cache:
190 if cache:
191 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
191 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
192
192
193 if not ret:
193 if not ret:
194 raise Exception('Could not get application settings !')
194 raise Exception('Could not get application settings !')
195 settings = {}
195 settings = {}
196 for each in ret:
196 for each in ret:
197 settings['rhodecode_' + each.app_settings_name] = \
197 settings['rhodecode_' + each.app_settings_name] = \
198 each.app_settings_value
198 each.app_settings_value
199
199
200 return settings
200 return settings
201
201
202 @classmethod
202 @classmethod
203 def get_ldap_settings(cls, cache=False):
203 def get_ldap_settings(cls, cache=False):
204 ret = cls.query()\
204 ret = cls.query()\
205 .filter(cls.app_settings_name.startswith('ldap_')).all()
205 .filter(cls.app_settings_name.startswith('ldap_')).all()
206 fd = {}
206 fd = {}
207 for row in ret:
207 for row in ret:
208 fd.update({row.app_settings_name:row.app_settings_value})
208 fd.update({row.app_settings_name:row.app_settings_value})
209
209
210 return fd
210 return fd
211
211
212
212
213 class RhodeCodeUi(Base, BaseModel):
213 class RhodeCodeUi(Base, BaseModel):
214 __tablename__ = 'rhodecode_ui'
214 __tablename__ = 'rhodecode_ui'
215 __table_args__ = (UniqueConstraint('ui_key'), {'extend_existing':True})
215 __table_args__ = (UniqueConstraint('ui_key'), {'extend_existing':True})
216
216
217 HOOK_UPDATE = 'changegroup.update'
217 HOOK_UPDATE = 'changegroup.update'
218 HOOK_REPO_SIZE = 'changegroup.repo_size'
218 HOOK_REPO_SIZE = 'changegroup.repo_size'
219 HOOK_PUSH = 'pretxnchangegroup.push_logger'
219 HOOK_PUSH = 'pretxnchangegroup.push_logger'
220 HOOK_PULL = 'preoutgoing.pull_logger'
220 HOOK_PULL = 'preoutgoing.pull_logger'
221
221
222 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
222 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
223 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
223 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
224 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
224 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
225 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
225 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
226 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
226 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
227
227
228
228
229 @classmethod
229 @classmethod
230 def get_by_key(cls, key):
230 def get_by_key(cls, key):
231 return cls.query().filter(cls.ui_key == key)
231 return cls.query().filter(cls.ui_key == key)
232
232
233
233
234 @classmethod
234 @classmethod
235 def get_builtin_hooks(cls):
235 def get_builtin_hooks(cls):
236 q = cls.query()
236 q = cls.query()
237 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
237 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
238 cls.HOOK_REPO_SIZE,
238 cls.HOOK_REPO_SIZE,
239 cls.HOOK_PUSH, cls.HOOK_PULL]))
239 cls.HOOK_PUSH, cls.HOOK_PULL]))
240 return q.all()
240 return q.all()
241
241
242 @classmethod
242 @classmethod
243 def get_custom_hooks(cls):
243 def get_custom_hooks(cls):
244 q = cls.query()
244 q = cls.query()
245 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
245 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
246 cls.HOOK_REPO_SIZE,
246 cls.HOOK_REPO_SIZE,
247 cls.HOOK_PUSH, cls.HOOK_PULL]))
247 cls.HOOK_PUSH, cls.HOOK_PULL]))
248 q = q.filter(cls.ui_section == 'hooks')
248 q = q.filter(cls.ui_section == 'hooks')
249 return q.all()
249 return q.all()
250
250
251 @classmethod
251 @classmethod
252 def create_or_update_hook(cls, key, val):
252 def create_or_update_hook(cls, key, val):
253 new_ui = cls.get_by_key(key).scalar() or cls()
253 new_ui = cls.get_by_key(key).scalar() or cls()
254 new_ui.ui_section = 'hooks'
254 new_ui.ui_section = 'hooks'
255 new_ui.ui_active = True
255 new_ui.ui_active = True
256 new_ui.ui_key = key
256 new_ui.ui_key = key
257 new_ui.ui_value = val
257 new_ui.ui_value = val
258
258
259 Session.add(new_ui)
259 Session.add(new_ui)
260 Session.commit()
260 Session.commit()
261
261
262
262
263 class User(Base, BaseModel):
263 class User(Base, BaseModel):
264 __tablename__ = 'users'
264 __tablename__ = 'users'
265 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True})
265 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True})
266 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
266 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
267 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
267 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
268 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
268 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
269 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
269 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
270 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
270 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
271 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
271 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
272 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
272 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
273 email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
273 email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
274 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
274 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
275 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
275 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
276 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
276 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
277
277
278 user_log = relationship('UserLog', cascade='all')
278 user_log = relationship('UserLog', cascade='all')
279 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
279 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
280
280
281 repositories = relationship('Repository')
281 repositories = relationship('Repository')
282 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
282 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
283 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
283 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
284
284
285 group_member = relationship('UsersGroupMember', cascade='all')
285 group_member = relationship('UsersGroupMember', cascade='all')
286
286
287 @property
287 @property
288 def full_contact(self):
288 def full_contact(self):
289 return '%s %s <%s>' % (self.name, self.lastname, self.email)
289 return '%s %s <%s>' % (self.name, self.lastname, self.email)
290
290
291 @property
291 @property
292 def short_contact(self):
292 def short_contact(self):
293 return '%s %s' % (self.name, self.lastname)
293 return '%s %s' % (self.name, self.lastname)
294
294
295 @property
295 @property
296 def is_admin(self):
296 def is_admin(self):
297 return self.admin
297 return self.admin
298
298
299 def __repr__(self):
299 def __repr__(self):
300 try:
300 try:
301 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
301 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
302 self.user_id, self.username)
302 self.user_id, self.username)
303 except:
303 except:
304 return self.__class__.__name__
304 return self.__class__.__name__
305
305
306 @classmethod
306 @classmethod
307 def get_by_username(cls, username, case_insensitive=False):
307 def get_by_username(cls, username, case_insensitive=False):
308 if case_insensitive:
308 if case_insensitive:
309 return Session.query(cls).filter(cls.username.ilike(username)).scalar()
309 return Session.query(cls).filter(cls.username.ilike(username)).scalar()
310 else:
310 else:
311 return Session.query(cls).filter(cls.username == username).scalar()
311 return Session.query(cls).filter(cls.username == username).scalar()
312
312
313 @classmethod
313 @classmethod
314 def get_by_api_key(cls, api_key):
314 def get_by_api_key(cls, api_key):
315 return cls.query().filter(cls.api_key == api_key).one()
315 return cls.query().filter(cls.api_key == api_key).one()
316
316
317 def update_lastlogin(self):
317 def update_lastlogin(self):
318 """Update user lastlogin"""
318 """Update user lastlogin"""
319
319
320 self.last_login = datetime.datetime.now()
320 self.last_login = datetime.datetime.now()
321 Session.add(self)
321 Session.add(self)
322 Session.commit()
322 Session.commit()
323 log.debug('updated user %s lastlogin', self.username)
323 log.debug('updated user %s lastlogin', self.username)
324
324
325 @classmethod
325 @classmethod
326 def create(cls, form_data):
326 def create(cls, form_data):
327 from rhodecode.lib.auth import get_crypt_password
327 from rhodecode.lib.auth import get_crypt_password
328
328
329 try:
329 try:
330 new_user = cls()
330 new_user = cls()
331 for k, v in form_data.items():
331 for k, v in form_data.items():
332 if k == 'password':
332 if k == 'password':
333 v = get_crypt_password(v)
333 v = get_crypt_password(v)
334 setattr(new_user, k, v)
334 setattr(new_user, k, v)
335
335
336 new_user.api_key = generate_api_key(form_data['username'])
336 new_user.api_key = generate_api_key(form_data['username'])
337 Session.add(new_user)
337 Session.add(new_user)
338 Session.commit()
338 Session.commit()
339 return new_user
339 return new_user
340 except:
340 except:
341 log.error(traceback.format_exc())
341 log.error(traceback.format_exc())
342 Session.rollback()
342 Session.rollback()
343 raise
343 raise
344
344
345 class UserLog(Base, BaseModel):
345 class UserLog(Base, BaseModel):
346 __tablename__ = 'user_logs'
346 __tablename__ = 'user_logs'
347 __table_args__ = {'extend_existing':True}
347 __table_args__ = {'extend_existing':True}
348 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
348 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
349 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
349 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
350 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
350 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
351 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
351 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
352 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
352 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
353 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
353 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
354 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
354 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
355
355
356 @property
356 @property
357 def action_as_day(self):
357 def action_as_day(self):
358 return date(*self.action_date.timetuple()[:3])
358 return date(*self.action_date.timetuple()[:3])
359
359
360 user = relationship('User')
360 user = relationship('User')
361 repository = relationship('Repository')
361 repository = relationship('Repository')
362
362
363
363
364 class UsersGroup(Base, BaseModel):
364 class UsersGroup(Base, BaseModel):
365 __tablename__ = 'users_groups'
365 __tablename__ = 'users_groups'
366 __table_args__ = {'extend_existing':True}
366 __table_args__ = {'extend_existing':True}
367
367
368 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
368 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
369 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
369 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
370 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
370 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
371
371
372 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
372 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
373
373
374 def __repr__(self):
374 def __repr__(self):
375 return '<userGroup(%s)>' % (self.users_group_name)
375 return '<userGroup(%s)>' % (self.users_group_name)
376
376
377 @classmethod
377 @classmethod
378 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
378 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
379 if case_insensitive:
379 if case_insensitive:
380 gr = cls.query()\
380 gr = cls.query()\
381 .filter(cls.users_group_name.ilike(group_name))
381 .filter(cls.users_group_name.ilike(group_name))
382 else:
382 else:
383 gr = cls.query()\
383 gr = cls.query()\
384 .filter(cls.users_group_name == group_name)
384 .filter(cls.users_group_name == group_name)
385 if cache:
385 if cache:
386 gr = gr.options(FromCache("sql_cache_short",
386 gr = gr.options(FromCache("sql_cache_short",
387 "get_user_%s" % group_name))
387 "get_user_%s" % group_name))
388 return gr.scalar()
388 return gr.scalar()
389
389
390
390
391 @classmethod
391 @classmethod
392 def get(cls, users_group_id, cache=False):
392 def get(cls, users_group_id, cache=False):
393 users_group = cls.query()
393 users_group = cls.query()
394 if cache:
394 if cache:
395 users_group = users_group.options(FromCache("sql_cache_short",
395 users_group = users_group.options(FromCache("sql_cache_short",
396 "get_users_group_%s" % users_group_id))
396 "get_users_group_%s" % users_group_id))
397 return users_group.get(users_group_id)
397 return users_group.get(users_group_id)
398
398
399 @classmethod
399 @classmethod
400 def create(cls, form_data):
400 def create(cls, form_data):
401 try:
401 try:
402 new_users_group = cls()
402 new_users_group = cls()
403 for k, v in form_data.items():
403 for k, v in form_data.items():
404 setattr(new_users_group, k, v)
404 setattr(new_users_group, k, v)
405
405
406 Session.add(new_users_group)
406 Session.add(new_users_group)
407 Session.commit()
407 Session.commit()
408 return new_users_group
408 return new_users_group
409 except:
409 except:
410 log.error(traceback.format_exc())
410 log.error(traceback.format_exc())
411 Session.rollback()
411 Session.rollback()
412 raise
412 raise
413
413
414 @classmethod
414 @classmethod
415 def update(cls, users_group_id, form_data):
415 def update(cls, users_group_id, form_data):
416
416
417 try:
417 try:
418 users_group = cls.get(users_group_id, cache=False)
418 users_group = cls.get(users_group_id, cache=False)
419
419
420 for k, v in form_data.items():
420 for k, v in form_data.items():
421 if k == 'users_group_members':
421 if k == 'users_group_members':
422 users_group.members = []
422 users_group.members = []
423 Session.flush()
423 Session.flush()
424 members_list = []
424 members_list = []
425 if v:
425 if v:
426 v = [v] if isinstance(v, basestring) else v
426 v = [v] if isinstance(v, basestring) else v
427 for u_id in set(v):
427 for u_id in set(v):
428 member = UsersGroupMember(users_group_id, u_id)
428 member = UsersGroupMember(users_group_id, u_id)
429 members_list.append(member)
429 members_list.append(member)
430 setattr(users_group, 'members', members_list)
430 setattr(users_group, 'members', members_list)
431 setattr(users_group, k, v)
431 setattr(users_group, k, v)
432
432
433 Session.add(users_group)
433 Session.add(users_group)
434 Session.commit()
434 Session.commit()
435 except:
435 except:
436 log.error(traceback.format_exc())
436 log.error(traceback.format_exc())
437 Session.rollback()
437 Session.rollback()
438 raise
438 raise
439
439
440 @classmethod
440 @classmethod
441 def delete(cls, users_group_id):
441 def delete(cls, users_group_id):
442 try:
442 try:
443
443
444 # check if this group is not assigned to repo
444 # check if this group is not assigned to repo
445 assigned_groups = UsersGroupRepoToPerm.query()\
445 assigned_groups = UsersGroupRepoToPerm.query()\
446 .filter(UsersGroupRepoToPerm.users_group_id ==
446 .filter(UsersGroupRepoToPerm.users_group_id ==
447 users_group_id).all()
447 users_group_id).all()
448
448
449 if assigned_groups:
449 if assigned_groups:
450 raise UsersGroupsAssignedException('RepoGroup assigned to %s' %
450 raise UsersGroupsAssignedException('RepoGroup assigned to %s' %
451 assigned_groups)
451 assigned_groups)
452
452
453 users_group = cls.get(users_group_id, cache=False)
453 users_group = cls.get(users_group_id, cache=False)
454 Session.delete(users_group)
454 Session.delete(users_group)
455 Session.commit()
455 Session.commit()
456 except:
456 except:
457 log.error(traceback.format_exc())
457 log.error(traceback.format_exc())
458 Session.rollback()
458 Session.rollback()
459 raise
459 raise
460
460
461 class UsersGroupMember(Base, BaseModel):
461 class UsersGroupMember(Base, BaseModel):
462 __tablename__ = 'users_groups_members'
462 __tablename__ = 'users_groups_members'
463 __table_args__ = {'extend_existing':True}
463 __table_args__ = {'extend_existing':True}
464
464
465 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
465 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
466 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
466 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
467 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
467 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
468
468
469 user = relationship('User', lazy='joined')
469 user = relationship('User', lazy='joined')
470 users_group = relationship('UsersGroup')
470 users_group = relationship('UsersGroup')
471
471
472 def __init__(self, gr_id='', u_id=''):
472 def __init__(self, gr_id='', u_id=''):
473 self.users_group_id = gr_id
473 self.users_group_id = gr_id
474 self.user_id = u_id
474 self.user_id = u_id
475
475
476 @staticmethod
476 @staticmethod
477 def add_user_to_group(group, user):
477 def add_user_to_group(group, user):
478 ugm = UsersGroupMember()
478 ugm = UsersGroupMember()
479 ugm.users_group = group
479 ugm.users_group = group
480 ugm.user = user
480 ugm.user = user
481 Session.add(ugm)
481 Session.add(ugm)
482 Session.commit()
482 Session.commit()
483 return ugm
483 return ugm
484
484
485 class Repository(Base, BaseModel):
485 class Repository(Base, BaseModel):
486 __tablename__ = 'repositories'
486 __tablename__ = 'repositories'
487 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
487 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
488
488
489 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
489 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
490 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
490 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
491 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
491 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
492 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
492 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
493 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
493 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
494 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
494 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
495 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
495 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
496 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
496 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
497 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
497 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
498 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
498 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
499
499
500 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
500 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
501 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
501 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
502
502
503
503
504 user = relationship('User')
504 user = relationship('User')
505 fork = relationship('Repository', remote_side=repo_id)
505 fork = relationship('Repository', remote_side=repo_id)
506 group = relationship('RepoGroup')
506 group = relationship('RepoGroup')
507 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
507 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
508 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
508 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
509 stats = relationship('Statistics', cascade='all', uselist=False)
509 stats = relationship('Statistics', cascade='all', uselist=False)
510
510
511 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
511 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
512
512
513 logs = relationship('UserLog', cascade='all')
513 logs = relationship('UserLog', cascade='all')
514
514
515 def __repr__(self):
515 def __repr__(self):
516 return "<%s('%s:%s')>" % (self.__class__.__name__,
516 return "<%s('%s:%s')>" % (self.__class__.__name__,
517 self.repo_id, self.repo_name)
517 self.repo_id, self.repo_name)
518
518
519 @classmethod
519 @classmethod
520 def url_sep(cls):
520 def url_sep(cls):
521 return '/'
521 return '/'
522
522
523 @classmethod
523 @classmethod
524 def get_by_repo_name(cls, repo_name):
524 def get_by_repo_name(cls, repo_name):
525 q = Session.query(cls).filter(cls.repo_name == repo_name)
525 q = Session.query(cls).filter(cls.repo_name == repo_name)
526 q = q.options(joinedload(Repository.fork))\
526 q = q.options(joinedload(Repository.fork))\
527 .options(joinedload(Repository.user))\
527 .options(joinedload(Repository.user))\
528 .options(joinedload(Repository.group))
528 .options(joinedload(Repository.group))
529 return q.one()
529 return q.one()
530
530
531 @classmethod
531 @classmethod
532 def get_repo_forks(cls, repo_id):
532 def get_repo_forks(cls, repo_id):
533 return cls.query().filter(Repository.fork_id == repo_id)
533 return cls.query().filter(Repository.fork_id == repo_id)
534
534
535 @classmethod
535 @classmethod
536 def base_path(cls):
536 def base_path(cls):
537 """
537 """
538 Returns base path when all repos are stored
538 Returns base path when all repos are stored
539
539
540 :param cls:
540 :param cls:
541 """
541 """
542 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
542 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
543 cls.url_sep())
543 cls.url_sep())
544 q.options(FromCache("sql_cache_short", "repository_repo_path"))
544 q.options(FromCache("sql_cache_short", "repository_repo_path"))
545 return q.one().ui_value
545 return q.one().ui_value
546
546
547 @property
547 @property
548 def just_name(self):
548 def just_name(self):
549 return self.repo_name.split(Repository.url_sep())[-1]
549 return self.repo_name.split(Repository.url_sep())[-1]
550
550
551 @property
551 @property
552 def groups_with_parents(self):
552 def groups_with_parents(self):
553 groups = []
553 groups = []
554 if self.group is None:
554 if self.group is None:
555 return groups
555 return groups
556
556
557 cur_gr = self.group
557 cur_gr = self.group
558 groups.insert(0, cur_gr)
558 groups.insert(0, cur_gr)
559 while 1:
559 while 1:
560 gr = getattr(cur_gr, 'parent_group', None)
560 gr = getattr(cur_gr, 'parent_group', None)
561 cur_gr = cur_gr.parent_group
561 cur_gr = cur_gr.parent_group
562 if gr is None:
562 if gr is None:
563 break
563 break
564 groups.insert(0, gr)
564 groups.insert(0, gr)
565
565
566 return groups
566 return groups
567
567
568 @property
568 @property
569 def groups_and_repo(self):
569 def groups_and_repo(self):
570 return self.groups_with_parents, self.just_name
570 return self.groups_with_parents, self.just_name
571
571
572 @LazyProperty
572 @LazyProperty
573 def repo_path(self):
573 def repo_path(self):
574 """
574 """
575 Returns base full path for that repository means where it actually
575 Returns base full path for that repository means where it actually
576 exists on a filesystem
576 exists on a filesystem
577 """
577 """
578 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
578 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
579 Repository.url_sep())
579 Repository.url_sep())
580 q.options(FromCache("sql_cache_short", "repository_repo_path"))
580 q.options(FromCache("sql_cache_short", "repository_repo_path"))
581 return q.one().ui_value
581 return q.one().ui_value
582
582
583 @property
583 @property
584 def repo_full_path(self):
584 def repo_full_path(self):
585 p = [self.repo_path]
585 p = [self.repo_path]
586 # we need to split the name by / since this is how we store the
586 # we need to split the name by / since this is how we store the
587 # names in the database, but that eventually needs to be converted
587 # names in the database, but that eventually needs to be converted
588 # into a valid system path
588 # into a valid system path
589 p += self.repo_name.split(Repository.url_sep())
589 p += self.repo_name.split(Repository.url_sep())
590 return os.path.join(*p)
590 return os.path.join(*p)
591
591
592 def get_new_name(self, repo_name):
592 def get_new_name(self, repo_name):
593 """
593 """
594 returns new full repository name based on assigned group and new new
594 returns new full repository name based on assigned group and new new
595
595
596 :param group_name:
596 :param group_name:
597 """
597 """
598 path_prefix = self.group.full_path_splitted if self.group else []
598 path_prefix = self.group.full_path_splitted if self.group else []
599 return Repository.url_sep().join(path_prefix + [repo_name])
599 return Repository.url_sep().join(path_prefix + [repo_name])
600
600
601 @property
601 @property
602 def _ui(self):
602 def _ui(self):
603 """
603 """
604 Creates an db based ui object for this repository
604 Creates an db based ui object for this repository
605 """
605 """
606 from mercurial import ui
606 from mercurial import ui
607 from mercurial import config
607 from mercurial import config
608 baseui = ui.ui()
608 baseui = ui.ui()
609
609
610 #clean the baseui object
610 #clean the baseui object
611 baseui._ocfg = config.config()
611 baseui._ocfg = config.config()
612 baseui._ucfg = config.config()
612 baseui._ucfg = config.config()
613 baseui._tcfg = config.config()
613 baseui._tcfg = config.config()
614
614
615
615
616 ret = RhodeCodeUi.query()\
616 ret = RhodeCodeUi.query()\
617 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
617 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
618
618
619 hg_ui = ret
619 hg_ui = ret
620 for ui_ in hg_ui:
620 for ui_ in hg_ui:
621 if ui_.ui_active:
621 if ui_.ui_active:
622 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
622 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
623 ui_.ui_key, ui_.ui_value)
623 ui_.ui_key, ui_.ui_value)
624 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
624 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
625
625
626 return baseui
626 return baseui
627
627
628 @classmethod
628 @classmethod
629 def is_valid(cls, repo_name):
629 def is_valid(cls, repo_name):
630 """
630 """
631 returns True if given repo name is a valid filesystem repository
631 returns True if given repo name is a valid filesystem repository
632
632
633 @param cls:
633 :param cls:
634 @param repo_name:
634 :param repo_name:
635 """
635 """
636 from rhodecode.lib.utils import is_valid_repo
636 from rhodecode.lib.utils import is_valid_repo
637
637
638 return is_valid_repo(repo_name, cls.base_path())
638 return is_valid_repo(repo_name, cls.base_path())
639
639
640
640
641 #==========================================================================
641 #==========================================================================
642 # SCM PROPERTIES
642 # SCM PROPERTIES
643 #==========================================================================
643 #==========================================================================
644
644
645 def get_changeset(self, rev):
645 def get_changeset(self, rev):
646 return get_changeset_safe(self.scm_instance, rev)
646 return get_changeset_safe(self.scm_instance, rev)
647
647
648 @property
648 @property
649 def tip(self):
649 def tip(self):
650 return self.get_changeset('tip')
650 return self.get_changeset('tip')
651
651
652 @property
652 @property
653 def author(self):
653 def author(self):
654 return self.tip.author
654 return self.tip.author
655
655
656 @property
656 @property
657 def last_change(self):
657 def last_change(self):
658 return self.scm_instance.last_change
658 return self.scm_instance.last_change
659
659
660 #==========================================================================
660 #==========================================================================
661 # SCM CACHE INSTANCE
661 # SCM CACHE INSTANCE
662 #==========================================================================
662 #==========================================================================
663
663
664 @property
664 @property
665 def invalidate(self):
665 def invalidate(self):
666 return CacheInvalidation.invalidate(self.repo_name)
666 return CacheInvalidation.invalidate(self.repo_name)
667
667
668 def set_invalidate(self):
668 def set_invalidate(self):
669 """
669 """
670 set a cache for invalidation for this instance
670 set a cache for invalidation for this instance
671 """
671 """
672 CacheInvalidation.set_invalidate(self.repo_name)
672 CacheInvalidation.set_invalidate(self.repo_name)
673
673
674 @LazyProperty
674 @LazyProperty
675 def scm_instance(self):
675 def scm_instance(self):
676 return self.__get_instance()
676 return self.__get_instance()
677
677
678 @property
678 @property
679 def scm_instance_cached(self):
679 def scm_instance_cached(self):
680 @cache_region('long_term')
680 @cache_region('long_term')
681 def _c(repo_name):
681 def _c(repo_name):
682 return self.__get_instance()
682 return self.__get_instance()
683 rn = self.repo_name
683 rn = self.repo_name
684
684
685 inv = self.invalidate
685 inv = self.invalidate
686 if inv is not None:
686 if inv is not None:
687 region_invalidate(_c, None, rn)
687 region_invalidate(_c, None, rn)
688 # update our cache
688 # update our cache
689 CacheInvalidation.set_valid(inv.cache_key)
689 CacheInvalidation.set_valid(inv.cache_key)
690 return _c(rn)
690 return _c(rn)
691
691
692 def __get_instance(self):
692 def __get_instance(self):
693
693
694 repo_full_path = self.repo_full_path
694 repo_full_path = self.repo_full_path
695
695
696 try:
696 try:
697 alias = get_scm(repo_full_path)[0]
697 alias = get_scm(repo_full_path)[0]
698 log.debug('Creating instance of %s repository', alias)
698 log.debug('Creating instance of %s repository', alias)
699 backend = get_backend(alias)
699 backend = get_backend(alias)
700 except VCSError:
700 except VCSError:
701 log.error(traceback.format_exc())
701 log.error(traceback.format_exc())
702 log.error('Perhaps this repository is in db and not in '
702 log.error('Perhaps this repository is in db and not in '
703 'filesystem run rescan repositories with '
703 'filesystem run rescan repositories with '
704 '"destroy old data " option from admin panel')
704 '"destroy old data " option from admin panel')
705 return
705 return
706
706
707 if alias == 'hg':
707 if alias == 'hg':
708
708
709 repo = backend(safe_str(repo_full_path), create=False,
709 repo = backend(safe_str(repo_full_path), create=False,
710 baseui=self._ui)
710 baseui=self._ui)
711 # skip hidden web repository
711 # skip hidden web repository
712 if repo._get_hidden():
712 if repo._get_hidden():
713 return
713 return
714 else:
714 else:
715 repo = backend(repo_full_path, create=False)
715 repo = backend(repo_full_path, create=False)
716
716
717 return repo
717 return repo
718
718
719
719
720 class RepoGroup(Base, BaseModel):
720 class RepoGroup(Base, BaseModel):
721 __tablename__ = 'groups'
721 __tablename__ = 'groups'
722 __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'),
722 __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'),
723 CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},)
723 CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},)
724 __mapper_args__ = {'order_by':'group_name'}
724 __mapper_args__ = {'order_by':'group_name'}
725
725
726 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
726 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
727 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
727 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
728 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
728 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
729 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
729 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
730
730
731 parent_group = relationship('RepoGroup', remote_side=group_id)
731 parent_group = relationship('RepoGroup', remote_side=group_id)
732
732
733
733
734 def __init__(self, group_name='', parent_group=None):
734 def __init__(self, group_name='', parent_group=None):
735 self.group_name = group_name
735 self.group_name = group_name
736 self.parent_group = parent_group
736 self.parent_group = parent_group
737
737
738 def __repr__(self):
738 def __repr__(self):
739 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
739 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
740 self.group_name)
740 self.group_name)
741
741
742 @classmethod
742 @classmethod
743 def groups_choices(cls):
743 def groups_choices(cls):
744 from webhelpers.html import literal as _literal
744 from webhelpers.html import literal as _literal
745 repo_groups = [('', '')]
745 repo_groups = [('', '')]
746 sep = ' &raquo; '
746 sep = ' &raquo; '
747 _name = lambda k: _literal(sep.join(k))
747 _name = lambda k: _literal(sep.join(k))
748
748
749 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
749 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
750 for x in cls.query().all()])
750 for x in cls.query().all()])
751
751
752 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
752 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
753 return repo_groups
753 return repo_groups
754
754
755 @classmethod
755 @classmethod
756 def url_sep(cls):
756 def url_sep(cls):
757 return '/'
757 return '/'
758
758
759 @classmethod
759 @classmethod
760 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
760 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
761 if case_insensitive:
761 if case_insensitive:
762 gr = cls.query()\
762 gr = cls.query()\
763 .filter(cls.group_name.ilike(group_name))
763 .filter(cls.group_name.ilike(group_name))
764 else:
764 else:
765 gr = cls.query()\
765 gr = cls.query()\
766 .filter(cls.group_name == group_name)
766 .filter(cls.group_name == group_name)
767 if cache:
767 if cache:
768 gr = gr.options(FromCache("sql_cache_short",
768 gr = gr.options(FromCache("sql_cache_short",
769 "get_group_%s" % group_name))
769 "get_group_%s" % group_name))
770 return gr.scalar()
770 return gr.scalar()
771
771
772 @property
772 @property
773 def parents(self):
773 def parents(self):
774 parents_recursion_limit = 5
774 parents_recursion_limit = 5
775 groups = []
775 groups = []
776 if self.parent_group is None:
776 if self.parent_group is None:
777 return groups
777 return groups
778 cur_gr = self.parent_group
778 cur_gr = self.parent_group
779 groups.insert(0, cur_gr)
779 groups.insert(0, cur_gr)
780 cnt = 0
780 cnt = 0
781 while 1:
781 while 1:
782 cnt += 1
782 cnt += 1
783 gr = getattr(cur_gr, 'parent_group', None)
783 gr = getattr(cur_gr, 'parent_group', None)
784 cur_gr = cur_gr.parent_group
784 cur_gr = cur_gr.parent_group
785 if gr is None:
785 if gr is None:
786 break
786 break
787 if cnt == parents_recursion_limit:
787 if cnt == parents_recursion_limit:
788 # this will prevent accidental infinit loops
788 # this will prevent accidental infinit loops
789 log.error('group nested more than %s' %
789 log.error('group nested more than %s' %
790 parents_recursion_limit)
790 parents_recursion_limit)
791 break
791 break
792
792
793 groups.insert(0, gr)
793 groups.insert(0, gr)
794 return groups
794 return groups
795
795
796 @property
796 @property
797 def children(self):
797 def children(self):
798 return Group.query().filter(Group.parent_group == self)
798 return Group.query().filter(Group.parent_group == self)
799
799
800 @property
800 @property
801 def name(self):
801 def name(self):
802 return self.group_name.split(Group.url_sep())[-1]
802 return self.group_name.split(Group.url_sep())[-1]
803
803
804 @property
804 @property
805 def full_path(self):
805 def full_path(self):
806 return self.group_name
806 return self.group_name
807
807
808 @property
808 @property
809 def full_path_splitted(self):
809 def full_path_splitted(self):
810 return self.group_name.split(Group.url_sep())
810 return self.group_name.split(Group.url_sep())
811
811
812 @property
812 @property
813 def repositories(self):
813 def repositories(self):
814 return Repository.query().filter(Repository.group == self)
814 return Repository.query().filter(Repository.group == self)
815
815
816 @property
816 @property
817 def repositories_recursive_count(self):
817 def repositories_recursive_count(self):
818 cnt = self.repositories.count()
818 cnt = self.repositories.count()
819
819
820 def children_count(group):
820 def children_count(group):
821 cnt = 0
821 cnt = 0
822 for child in group.children:
822 for child in group.children:
823 cnt += child.repositories.count()
823 cnt += child.repositories.count()
824 cnt += children_count(child)
824 cnt += children_count(child)
825 return cnt
825 return cnt
826
826
827 return cnt + children_count(self)
827 return cnt + children_count(self)
828
828
829
829
830 def get_new_name(self, group_name):
830 def get_new_name(self, group_name):
831 """
831 """
832 returns new full group name based on parent and new name
832 returns new full group name based on parent and new name
833
833
834 :param group_name:
834 :param group_name:
835 """
835 """
836 path_prefix = (self.parent_group.full_path_splitted if
836 path_prefix = (self.parent_group.full_path_splitted if
837 self.parent_group else [])
837 self.parent_group else [])
838 return Group.url_sep().join(path_prefix + [group_name])
838 return Group.url_sep().join(path_prefix + [group_name])
839
839
840
840
841 class Permission(Base, BaseModel):
841 class Permission(Base, BaseModel):
842 __tablename__ = 'permissions'
842 __tablename__ = 'permissions'
843 __table_args__ = {'extend_existing':True}
843 __table_args__ = {'extend_existing':True}
844 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
844 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
845 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
845 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
846 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
846 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
847
847
848 def __repr__(self):
848 def __repr__(self):
849 return "<%s('%s:%s')>" % (self.__class__.__name__,
849 return "<%s('%s:%s')>" % (self.__class__.__name__,
850 self.permission_id, self.permission_name)
850 self.permission_id, self.permission_name)
851
851
852 @classmethod
852 @classmethod
853 def get_by_key(cls, key):
853 def get_by_key(cls, key):
854 return cls.query().filter(cls.permission_name == key).scalar()
854 return cls.query().filter(cls.permission_name == key).scalar()
855
855
856 class UserRepoToPerm(Base, BaseModel):
856 class UserRepoToPerm(Base, BaseModel):
857 __tablename__ = 'repo_to_perm'
857 __tablename__ = 'repo_to_perm'
858 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True})
858 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True})
859 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
859 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
860 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
860 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
861 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
861 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
862 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
862 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
863
863
864 user = relationship('User')
864 user = relationship('User')
865 permission = relationship('Permission')
865 permission = relationship('Permission')
866 repository = relationship('Repository')
866 repository = relationship('Repository')
867
867
868 class UserToPerm(Base, BaseModel):
868 class UserToPerm(Base, BaseModel):
869 __tablename__ = 'user_to_perm'
869 __tablename__ = 'user_to_perm'
870 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
870 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
871 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
871 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
872 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
872 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
873 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
873 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
874
874
875 user = relationship('User')
875 user = relationship('User')
876 permission = relationship('Permission')
876 permission = relationship('Permission')
877
877
878 @classmethod
878 @classmethod
879 def has_perm(cls, user_id, perm):
879 def has_perm(cls, user_id, perm):
880 if not isinstance(perm, Permission):
880 if not isinstance(perm, Permission):
881 raise Exception('perm needs to be an instance of Permission class')
881 raise Exception('perm needs to be an instance of Permission class')
882
882
883 return cls.query().filter(cls.user_id == user_id)\
883 return cls.query().filter(cls.user_id == user_id)\
884 .filter(cls.permission == perm).scalar() is not None
884 .filter(cls.permission == perm).scalar() is not None
885
885
886 @classmethod
886 @classmethod
887 def grant_perm(cls, user_id, perm):
887 def grant_perm(cls, user_id, perm):
888 if not isinstance(perm, Permission):
888 if not isinstance(perm, Permission):
889 raise Exception('perm needs to be an instance of Permission class')
889 raise Exception('perm needs to be an instance of Permission class')
890
890
891 new = cls()
891 new = cls()
892 new.user_id = user_id
892 new.user_id = user_id
893 new.permission = perm
893 new.permission = perm
894 try:
894 try:
895 Session.add(new)
895 Session.add(new)
896 Session.commit()
896 Session.commit()
897 except:
897 except:
898 Session.rollback()
898 Session.rollback()
899
899
900
900
901 @classmethod
901 @classmethod
902 def revoke_perm(cls, user_id, perm):
902 def revoke_perm(cls, user_id, perm):
903 if not isinstance(perm, Permission):
903 if not isinstance(perm, Permission):
904 raise Exception('perm needs to be an instance of Permission class')
904 raise Exception('perm needs to be an instance of Permission class')
905
905
906 try:
906 try:
907 cls.query().filter(cls.user_id == user_id)\
907 cls.query().filter(cls.user_id == user_id)\
908 .filter(cls.permission == perm).delete()
908 .filter(cls.permission == perm).delete()
909 Session.commit()
909 Session.commit()
910 except:
910 except:
911 Session.rollback()
911 Session.rollback()
912
912
913 class UsersGroupRepoToPerm(Base, BaseModel):
913 class UsersGroupRepoToPerm(Base, BaseModel):
914 __tablename__ = 'users_group_repo_to_perm'
914 __tablename__ = 'users_group_repo_to_perm'
915 __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True})
915 __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True})
916 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
916 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
917 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
917 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
918 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
918 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
919 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
919 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
920
920
921 users_group = relationship('UsersGroup')
921 users_group = relationship('UsersGroup')
922 permission = relationship('Permission')
922 permission = relationship('Permission')
923 repository = relationship('Repository')
923 repository = relationship('Repository')
924
924
925 def __repr__(self):
925 def __repr__(self):
926 return '<userGroup:%s => %s >' % (self.users_group, self.repository)
926 return '<userGroup:%s => %s >' % (self.users_group, self.repository)
927
927
928 class UsersGroupToPerm(Base, BaseModel):
928 class UsersGroupToPerm(Base, BaseModel):
929 __tablename__ = 'users_group_to_perm'
929 __tablename__ = 'users_group_to_perm'
930 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
930 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
931 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
931 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
932 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
932 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
933
933
934 users_group = relationship('UsersGroup')
934 users_group = relationship('UsersGroup')
935 permission = relationship('Permission')
935 permission = relationship('Permission')
936
936
937
937
938 @classmethod
938 @classmethod
939 def has_perm(cls, users_group_id, perm):
939 def has_perm(cls, users_group_id, perm):
940 if not isinstance(perm, Permission):
940 if not isinstance(perm, Permission):
941 raise Exception('perm needs to be an instance of Permission class')
941 raise Exception('perm needs to be an instance of Permission class')
942
942
943 return cls.query().filter(cls.users_group_id ==
943 return cls.query().filter(cls.users_group_id ==
944 users_group_id)\
944 users_group_id)\
945 .filter(cls.permission == perm)\
945 .filter(cls.permission == perm)\
946 .scalar() is not None
946 .scalar() is not None
947
947
948 @classmethod
948 @classmethod
949 def grant_perm(cls, users_group_id, perm):
949 def grant_perm(cls, users_group_id, perm):
950 if not isinstance(perm, Permission):
950 if not isinstance(perm, Permission):
951 raise Exception('perm needs to be an instance of Permission class')
951 raise Exception('perm needs to be an instance of Permission class')
952
952
953 new = cls()
953 new = cls()
954 new.users_group_id = users_group_id
954 new.users_group_id = users_group_id
955 new.permission = perm
955 new.permission = perm
956 try:
956 try:
957 Session.add(new)
957 Session.add(new)
958 Session.commit()
958 Session.commit()
959 except:
959 except:
960 Session.rollback()
960 Session.rollback()
961
961
962
962
963 @classmethod
963 @classmethod
964 def revoke_perm(cls, users_group_id, perm):
964 def revoke_perm(cls, users_group_id, perm):
965 if not isinstance(perm, Permission):
965 if not isinstance(perm, Permission):
966 raise Exception('perm needs to be an instance of Permission class')
966 raise Exception('perm needs to be an instance of Permission class')
967
967
968 try:
968 try:
969 cls.query().filter(cls.users_group_id == users_group_id)\
969 cls.query().filter(cls.users_group_id == users_group_id)\
970 .filter(cls.permission == perm).delete()
970 .filter(cls.permission == perm).delete()
971 Session.commit()
971 Session.commit()
972 except:
972 except:
973 Session.rollback()
973 Session.rollback()
974
974
975
975
976 class UserRepoGroupToPerm(Base, BaseModel):
976 class UserRepoGroupToPerm(Base, BaseModel):
977 __tablename__ = 'group_to_perm'
977 __tablename__ = 'group_to_perm'
978 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
978 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
979
979
980 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
980 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
981 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
981 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
982 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
982 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
983 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
983 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
984
984
985 user = relationship('User')
985 user = relationship('User')
986 permission = relationship('Permission')
986 permission = relationship('Permission')
987 group = relationship('RepoGroup')
987 group = relationship('RepoGroup')
988
988
989 class Statistics(Base, BaseModel):
989 class Statistics(Base, BaseModel):
990 __tablename__ = 'statistics'
990 __tablename__ = 'statistics'
991 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True})
991 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True})
992 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
992 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
993 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
993 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
994 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
994 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
995 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
995 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
996 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
996 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
997 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
997 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
998
998
999 repository = relationship('Repository', single_parent=True)
999 repository = relationship('Repository', single_parent=True)
1000
1000
1001 class UserFollowing(Base, BaseModel):
1001 class UserFollowing(Base, BaseModel):
1002 __tablename__ = 'user_followings'
1002 __tablename__ = 'user_followings'
1003 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
1003 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
1004 UniqueConstraint('user_id', 'follows_user_id')
1004 UniqueConstraint('user_id', 'follows_user_id')
1005 , {'extend_existing':True})
1005 , {'extend_existing':True})
1006
1006
1007 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1007 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1008 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1008 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1009 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1009 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1010 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1010 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1011 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1011 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1012
1012
1013 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1013 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1014
1014
1015 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1015 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1016 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1016 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1017
1017
1018
1018
1019 @classmethod
1019 @classmethod
1020 def get_repo_followers(cls, repo_id):
1020 def get_repo_followers(cls, repo_id):
1021 return cls.query().filter(cls.follows_repo_id == repo_id)
1021 return cls.query().filter(cls.follows_repo_id == repo_id)
1022
1022
1023 class CacheInvalidation(Base, BaseModel):
1023 class CacheInvalidation(Base, BaseModel):
1024 __tablename__ = 'cache_invalidation'
1024 __tablename__ = 'cache_invalidation'
1025 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True})
1025 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True})
1026 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1026 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1027 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1027 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1028 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1028 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1029 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1029 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1030
1030
1031
1031
1032 def __init__(self, cache_key, cache_args=''):
1032 def __init__(self, cache_key, cache_args=''):
1033 self.cache_key = cache_key
1033 self.cache_key = cache_key
1034 self.cache_args = cache_args
1034 self.cache_args = cache_args
1035 self.cache_active = False
1035 self.cache_active = False
1036
1036
1037 def __repr__(self):
1037 def __repr__(self):
1038 return "<%s('%s:%s')>" % (self.__class__.__name__,
1038 return "<%s('%s:%s')>" % (self.__class__.__name__,
1039 self.cache_id, self.cache_key)
1039 self.cache_id, self.cache_key)
1040
1040
1041 @classmethod
1041 @classmethod
1042 def invalidate(cls, key):
1042 def invalidate(cls, key):
1043 """
1043 """
1044 Returns Invalidation object if this given key should be invalidated
1044 Returns Invalidation object if this given key should be invalidated
1045 None otherwise. `cache_active = False` means that this cache
1045 None otherwise. `cache_active = False` means that this cache
1046 state is not valid and needs to be invalidated
1046 state is not valid and needs to be invalidated
1047
1047
1048 :param key:
1048 :param key:
1049 """
1049 """
1050 return cls.query()\
1050 return cls.query()\
1051 .filter(CacheInvalidation.cache_key == key)\
1051 .filter(CacheInvalidation.cache_key == key)\
1052 .filter(CacheInvalidation.cache_active == False)\
1052 .filter(CacheInvalidation.cache_active == False)\
1053 .scalar()
1053 .scalar()
1054
1054
1055 @classmethod
1055 @classmethod
1056 def set_invalidate(cls, key):
1056 def set_invalidate(cls, key):
1057 """
1057 """
1058 Mark this Cache key for invalidation
1058 Mark this Cache key for invalidation
1059
1059
1060 :param key:
1060 :param key:
1061 """
1061 """
1062
1062
1063 log.debug('marking %s for invalidation' % key)
1063 log.debug('marking %s for invalidation' % key)
1064 inv_obj = Session.query(cls)\
1064 inv_obj = Session.query(cls)\
1065 .filter(cls.cache_key == key).scalar()
1065 .filter(cls.cache_key == key).scalar()
1066 if inv_obj:
1066 if inv_obj:
1067 inv_obj.cache_active = False
1067 inv_obj.cache_active = False
1068 else:
1068 else:
1069 log.debug('cache key not found in invalidation db -> creating one')
1069 log.debug('cache key not found in invalidation db -> creating one')
1070 inv_obj = CacheInvalidation(key)
1070 inv_obj = CacheInvalidation(key)
1071
1071
1072 try:
1072 try:
1073 Session.add(inv_obj)
1073 Session.add(inv_obj)
1074 Session.commit()
1074 Session.commit()
1075 except Exception:
1075 except Exception:
1076 log.error(traceback.format_exc())
1076 log.error(traceback.format_exc())
1077 Session.rollback()
1077 Session.rollback()
1078
1078
1079 @classmethod
1079 @classmethod
1080 def set_valid(cls, key):
1080 def set_valid(cls, key):
1081 """
1081 """
1082 Mark this cache key as active and currently cached
1082 Mark this cache key as active and currently cached
1083
1083
1084 :param key:
1084 :param key:
1085 """
1085 """
1086 inv_obj = Session.query(CacheInvalidation)\
1086 inv_obj = Session.query(CacheInvalidation)\
1087 .filter(CacheInvalidation.cache_key == key).scalar()
1087 .filter(CacheInvalidation.cache_key == key).scalar()
1088 inv_obj.cache_active = True
1088 inv_obj.cache_active = True
1089 Session.add(inv_obj)
1089 Session.add(inv_obj)
1090 Session.commit()
1090 Session.commit()
1091
1091
1092 class DbMigrateVersion(Base, BaseModel):
1092 class DbMigrateVersion(Base, BaseModel):
1093 __tablename__ = 'db_migrate_version'
1093 __tablename__ = 'db_migrate_version'
1094 __table_args__ = {'extend_existing':True}
1094 __table_args__ = {'extend_existing':True}
1095 repository_id = Column('repository_id', String(250), primary_key=True)
1095 repository_id = Column('repository_id', String(250), primary_key=True)
1096 repository_path = Column('repository_path', Text)
1096 repository_path = Column('repository_path', Text)
1097 version = Column('version', Integer)
1097 version = Column('version', Integer)
@@ -1,1122 +1,1122
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) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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
30
31 from sqlalchemy import *
31 from sqlalchemy import *
32 from sqlalchemy.ext.hybrid import hybrid_property
32 from sqlalchemy.ext.hybrid import hybrid_property
33 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
33 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
34 from beaker.cache import cache_region, region_invalidate
34 from beaker.cache import cache_region, region_invalidate
35
35
36 from vcs import get_backend
36 from vcs import get_backend
37 from vcs.utils.helpers import get_scm
37 from vcs.utils.helpers import get_scm
38 from vcs.exceptions import VCSError
38 from vcs.exceptions import VCSError
39 from vcs.utils.lazy import LazyProperty
39 from vcs.utils.lazy import LazyProperty
40
40
41 from rhodecode.lib import str2bool, safe_str, get_changeset_safe, safe_unicode
41 from rhodecode.lib import str2bool, safe_str, get_changeset_safe, safe_unicode
42 from rhodecode.lib.exceptions import UsersGroupsAssignedException
42 from rhodecode.lib.exceptions import UsersGroupsAssignedException
43 from rhodecode.lib.compat import json
43 from rhodecode.lib.compat import json
44 from rhodecode.lib.caching_query import FromCache
44 from rhodecode.lib.caching_query import FromCache
45
45
46 from rhodecode.model.meta import Base, Session
46 from rhodecode.model.meta import Base, Session
47
47
48 log = logging.getLogger(__name__)
48 log = logging.getLogger(__name__)
49
49
50 #==============================================================================
50 #==============================================================================
51 # BASE CLASSES
51 # BASE CLASSES
52 #==============================================================================
52 #==============================================================================
53
53
54
54
55 class ModelSerializer(json.JSONEncoder):
55 class ModelSerializer(json.JSONEncoder):
56 """
56 """
57 Simple Serializer for JSON,
57 Simple Serializer for JSON,
58
58
59 usage::
59 usage::
60
60
61 to make object customized for serialization implement a __json__
61 to make object customized for serialization implement a __json__
62 method that will return a dict for serialization into json
62 method that will return a dict for serialization into json
63
63
64 example::
64 example::
65
65
66 class Task(object):
66 class Task(object):
67
67
68 def __init__(self, name, value):
68 def __init__(self, name, value):
69 self.name = name
69 self.name = name
70 self.value = value
70 self.value = value
71
71
72 def __json__(self):
72 def __json__(self):
73 return dict(name=self.name,
73 return dict(name=self.name,
74 value=self.value)
74 value=self.value)
75
75
76 """
76 """
77
77
78 def default(self, obj):
78 def default(self, obj):
79
79
80 if hasattr(obj, '__json__'):
80 if hasattr(obj, '__json__'):
81 return obj.__json__()
81 return obj.__json__()
82 else:
82 else:
83 return json.JSONEncoder.default(self, obj)
83 return json.JSONEncoder.default(self, obj)
84
84
85
85
86 class BaseModel(object):
86 class BaseModel(object):
87 """
87 """
88 Base Model for all classess
88 Base Model for all classess
89 """
89 """
90
90
91 @classmethod
91 @classmethod
92 def _get_keys(cls):
92 def _get_keys(cls):
93 """return column names for this model """
93 """return column names for this model """
94 return class_mapper(cls).c.keys()
94 return class_mapper(cls).c.keys()
95
95
96 def get_dict(self, serialized=False):
96 def get_dict(self, serialized=False):
97 """
97 """
98 return dict with keys and values corresponding
98 return dict with keys and values corresponding
99 to this model data
99 to this model data
100 """
100 """
101
101
102 d = {}
102 d = {}
103 for k in self._get_keys():
103 for k in self._get_keys():
104 d[k] = getattr(self, k)
104 d[k] = getattr(self, k)
105
105
106 # also use __json__() if present to get additional fields
106 # also use __json__() if present to get additional fields
107 if hasattr(self, '__json__'):
107 if hasattr(self, '__json__'):
108 for k, val in self.__json__().iteritems():
108 for k, val in self.__json__().iteritems():
109 d[k] = val
109 d[k] = val
110 return d
110 return d
111
111
112 def get_appstruct(self):
112 def get_appstruct(self):
113 """return list with keys and values tupples corresponding
113 """return list with keys and values tupples corresponding
114 to this model data """
114 to this model data """
115
115
116 l = []
116 l = []
117 for k in self._get_keys():
117 for k in self._get_keys():
118 l.append((k, getattr(self, k),))
118 l.append((k, getattr(self, k),))
119 return l
119 return l
120
120
121 def populate_obj(self, populate_dict):
121 def populate_obj(self, populate_dict):
122 """populate model with data from given populate_dict"""
122 """populate model with data from given populate_dict"""
123
123
124 for k in self._get_keys():
124 for k in self._get_keys():
125 if k in populate_dict:
125 if k in populate_dict:
126 setattr(self, k, populate_dict[k])
126 setattr(self, k, populate_dict[k])
127
127
128 @classmethod
128 @classmethod
129 def query(cls):
129 def query(cls):
130 return Session.query(cls)
130 return Session.query(cls)
131
131
132 @classmethod
132 @classmethod
133 def get(cls, id_):
133 def get(cls, id_):
134 if id_:
134 if id_:
135 return cls.query().get(id_)
135 return cls.query().get(id_)
136
136
137 @classmethod
137 @classmethod
138 def getAll(cls):
138 def getAll(cls):
139 return cls.query().all()
139 return cls.query().all()
140
140
141 @classmethod
141 @classmethod
142 def delete(cls, id_):
142 def delete(cls, id_):
143 obj = cls.query().get(id_)
143 obj = cls.query().get(id_)
144 Session.delete(obj)
144 Session.delete(obj)
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__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
149 __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
150 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
150 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
151 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
151 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
152 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
152 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
153
153
154 def __init__(self, k='', v=''):
154 def __init__(self, k='', v=''):
155 self.app_settings_name = k
155 self.app_settings_name = k
156 self.app_settings_value = v
156 self.app_settings_value = v
157
157
158
158
159 @validates('_app_settings_value')
159 @validates('_app_settings_value')
160 def validate_settings_value(self, key, val):
160 def validate_settings_value(self, key, val):
161 assert type(val) == unicode
161 assert type(val) == unicode
162 return val
162 return val
163
163
164 @hybrid_property
164 @hybrid_property
165 def app_settings_value(self):
165 def app_settings_value(self):
166 v = self._app_settings_value
166 v = self._app_settings_value
167 if v == 'ldap_active':
167 if v == 'ldap_active':
168 v = str2bool(v)
168 v = str2bool(v)
169 return v
169 return v
170
170
171 @app_settings_value.setter
171 @app_settings_value.setter
172 def app_settings_value(self, val):
172 def app_settings_value(self, val):
173 """
173 """
174 Setter that will always make sure we use unicode in app_settings_value
174 Setter that will always make sure we use unicode in app_settings_value
175
175
176 :param val:
176 :param val:
177 """
177 """
178 self._app_settings_value = safe_unicode(val)
178 self._app_settings_value = safe_unicode(val)
179
179
180 def __repr__(self):
180 def __repr__(self):
181 return "<%s('%s:%s')>" % (self.__class__.__name__,
181 return "<%s('%s:%s')>" % (self.__class__.__name__,
182 self.app_settings_name, self.app_settings_value)
182 self.app_settings_name, self.app_settings_value)
183
183
184 @classmethod
184 @classmethod
185 def get_by_name(cls, ldap_key):
185 def get_by_name(cls, ldap_key):
186 return cls.query()\
186 return cls.query()\
187 .filter(cls.app_settings_name == ldap_key).scalar()
187 .filter(cls.app_settings_name == ldap_key).scalar()
188
188
189 @classmethod
189 @classmethod
190 def get_app_settings(cls, cache=False):
190 def get_app_settings(cls, cache=False):
191
191
192 ret = cls.query()
192 ret = cls.query()
193
193
194 if cache:
194 if cache:
195 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
195 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
196
196
197 if not ret:
197 if not ret:
198 raise Exception('Could not get application settings !')
198 raise Exception('Could not get application settings !')
199 settings = {}
199 settings = {}
200 for each in ret:
200 for each in ret:
201 settings['rhodecode_' + each.app_settings_name] = \
201 settings['rhodecode_' + each.app_settings_name] = \
202 each.app_settings_value
202 each.app_settings_value
203
203
204 return settings
204 return settings
205
205
206 @classmethod
206 @classmethod
207 def get_ldap_settings(cls, cache=False):
207 def get_ldap_settings(cls, cache=False):
208 ret = cls.query()\
208 ret = cls.query()\
209 .filter(cls.app_settings_name.startswith('ldap_')).all()
209 .filter(cls.app_settings_name.startswith('ldap_')).all()
210 fd = {}
210 fd = {}
211 for row in ret:
211 for row in ret:
212 fd.update({row.app_settings_name:row.app_settings_value})
212 fd.update({row.app_settings_name:row.app_settings_value})
213
213
214 return fd
214 return fd
215
215
216
216
217 class RhodeCodeUi(Base, BaseModel):
217 class RhodeCodeUi(Base, BaseModel):
218 __tablename__ = 'rhodecode_ui'
218 __tablename__ = 'rhodecode_ui'
219 __table_args__ = (UniqueConstraint('ui_key'), {'extend_existing':True})
219 __table_args__ = (UniqueConstraint('ui_key'), {'extend_existing':True})
220
220
221 HOOK_UPDATE = 'changegroup.update'
221 HOOK_UPDATE = 'changegroup.update'
222 HOOK_REPO_SIZE = 'changegroup.repo_size'
222 HOOK_REPO_SIZE = 'changegroup.repo_size'
223 HOOK_PUSH = 'pretxnchangegroup.push_logger'
223 HOOK_PUSH = 'pretxnchangegroup.push_logger'
224 HOOK_PULL = 'preoutgoing.pull_logger'
224 HOOK_PULL = 'preoutgoing.pull_logger'
225
225
226 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
226 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
227 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
227 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
228 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
228 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
229 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
229 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
230 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
230 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
231
231
232 @classmethod
232 @classmethod
233 def get_by_key(cls, key):
233 def get_by_key(cls, key):
234 return cls.query().filter(cls.ui_key == key)
234 return cls.query().filter(cls.ui_key == key)
235
235
236 @classmethod
236 @classmethod
237 def get_builtin_hooks(cls):
237 def get_builtin_hooks(cls):
238 q = cls.query()
238 q = cls.query()
239 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
239 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
240 cls.HOOK_REPO_SIZE,
240 cls.HOOK_REPO_SIZE,
241 cls.HOOK_PUSH, cls.HOOK_PULL]))
241 cls.HOOK_PUSH, cls.HOOK_PULL]))
242 return q.all()
242 return q.all()
243
243
244 @classmethod
244 @classmethod
245 def get_custom_hooks(cls):
245 def get_custom_hooks(cls):
246 q = cls.query()
246 q = cls.query()
247 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
247 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
248 cls.HOOK_REPO_SIZE,
248 cls.HOOK_REPO_SIZE,
249 cls.HOOK_PUSH, cls.HOOK_PULL]))
249 cls.HOOK_PUSH, cls.HOOK_PULL]))
250 q = q.filter(cls.ui_section == 'hooks')
250 q = q.filter(cls.ui_section == 'hooks')
251 return q.all()
251 return q.all()
252
252
253 @classmethod
253 @classmethod
254 def create_or_update_hook(cls, key, val):
254 def create_or_update_hook(cls, key, val):
255 new_ui = cls.get_by_key(key).scalar() or cls()
255 new_ui = cls.get_by_key(key).scalar() or cls()
256 new_ui.ui_section = 'hooks'
256 new_ui.ui_section = 'hooks'
257 new_ui.ui_active = True
257 new_ui.ui_active = True
258 new_ui.ui_key = key
258 new_ui.ui_key = key
259 new_ui.ui_value = val
259 new_ui.ui_value = val
260
260
261 Session.add(new_ui)
261 Session.add(new_ui)
262
262
263
263
264 class User(Base, BaseModel):
264 class User(Base, BaseModel):
265 __tablename__ = 'users'
265 __tablename__ = 'users'
266 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True})
266 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True})
267 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
267 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
268 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
268 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
269 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
269 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
270 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
270 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
271 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
271 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
272 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
272 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
273 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
273 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
274 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
274 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
275 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
275 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
276 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
276 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
277 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
277 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
278
278
279 user_log = relationship('UserLog', cascade='all')
279 user_log = relationship('UserLog', cascade='all')
280 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
280 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
281
281
282 repositories = relationship('Repository')
282 repositories = relationship('Repository')
283 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
283 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
284 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
284 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
285
285
286 group_member = relationship('UsersGroupMember', cascade='all')
286 group_member = relationship('UsersGroupMember', cascade='all')
287
287
288 notifications = relationship('UserNotification',)
288 notifications = relationship('UserNotification',)
289
289
290 @hybrid_property
290 @hybrid_property
291 def email(self):
291 def email(self):
292 return self._email
292 return self._email
293
293
294 @email.setter
294 @email.setter
295 def email(self, val):
295 def email(self, val):
296 self._email = val.lower() if val else None
296 self._email = val.lower() if val else None
297
297
298 @property
298 @property
299 def full_name(self):
299 def full_name(self):
300 return '%s %s' % (self.name, self.lastname)
300 return '%s %s' % (self.name, self.lastname)
301
301
302 @property
302 @property
303 def full_contact(self):
303 def full_contact(self):
304 return '%s %s <%s>' % (self.name, self.lastname, self.email)
304 return '%s %s <%s>' % (self.name, self.lastname, self.email)
305
305
306 @property
306 @property
307 def short_contact(self):
307 def short_contact(self):
308 return '%s %s' % (self.name, self.lastname)
308 return '%s %s' % (self.name, self.lastname)
309
309
310 @property
310 @property
311 def is_admin(self):
311 def is_admin(self):
312 return self.admin
312 return self.admin
313
313
314 def __repr__(self):
314 def __repr__(self):
315 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
315 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
316 self.user_id, self.username)
316 self.user_id, self.username)
317
317
318 @classmethod
318 @classmethod
319 def get_by_username(cls, username, case_insensitive=False, cache=False):
319 def get_by_username(cls, username, case_insensitive=False, cache=False):
320 if case_insensitive:
320 if case_insensitive:
321 q = cls.query().filter(cls.username.ilike(username))
321 q = cls.query().filter(cls.username.ilike(username))
322 else:
322 else:
323 q = cls.query().filter(cls.username == username)
323 q = cls.query().filter(cls.username == username)
324
324
325 if cache:
325 if cache:
326 q = q.options(FromCache("sql_cache_short",
326 q = q.options(FromCache("sql_cache_short",
327 "get_user_%s" % username))
327 "get_user_%s" % username))
328 return q.scalar()
328 return q.scalar()
329
329
330 @classmethod
330 @classmethod
331 def get_by_api_key(cls, api_key, cache=False):
331 def get_by_api_key(cls, api_key, cache=False):
332 q = cls.query().filter(cls.api_key == api_key)
332 q = cls.query().filter(cls.api_key == api_key)
333
333
334 if cache:
334 if cache:
335 q = q.options(FromCache("sql_cache_short",
335 q = q.options(FromCache("sql_cache_short",
336 "get_api_key_%s" % api_key))
336 "get_api_key_%s" % api_key))
337 return q.scalar()
337 return q.scalar()
338
338
339 @classmethod
339 @classmethod
340 def get_by_email(cls, email, case_insensitive=False, cache=False):
340 def get_by_email(cls, email, case_insensitive=False, cache=False):
341 if case_insensitive:
341 if case_insensitive:
342 q = cls.query().filter(cls.email.ilike(email))
342 q = cls.query().filter(cls.email.ilike(email))
343 else:
343 else:
344 q = cls.query().filter(cls.email == email)
344 q = cls.query().filter(cls.email == email)
345
345
346 if cache:
346 if cache:
347 q = q.options(FromCache("sql_cache_short",
347 q = q.options(FromCache("sql_cache_short",
348 "get_api_key_%s" % email))
348 "get_api_key_%s" % email))
349 return q.scalar()
349 return q.scalar()
350
350
351 def update_lastlogin(self):
351 def update_lastlogin(self):
352 """Update user lastlogin"""
352 """Update user lastlogin"""
353 self.last_login = datetime.datetime.now()
353 self.last_login = datetime.datetime.now()
354 Session.add(self)
354 Session.add(self)
355 log.debug('updated user %s lastlogin', self.username)
355 log.debug('updated user %s lastlogin', self.username)
356
356
357 def __json__(self):
357 def __json__(self):
358 return dict(email=self.email,
358 return dict(email=self.email,
359 full_name=self.full_name)
359 full_name=self.full_name)
360
360
361
361
362 class UserLog(Base, BaseModel):
362 class UserLog(Base, BaseModel):
363 __tablename__ = 'user_logs'
363 __tablename__ = 'user_logs'
364 __table_args__ = {'extend_existing':True}
364 __table_args__ = {'extend_existing':True}
365 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
365 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
366 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
366 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
367 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
367 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
368 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
368 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
369 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
369 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
370 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
370 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
371 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
371 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
372
372
373 @property
373 @property
374 def action_as_day(self):
374 def action_as_day(self):
375 return datetime.date(*self.action_date.timetuple()[:3])
375 return datetime.date(*self.action_date.timetuple()[:3])
376
376
377 user = relationship('User')
377 user = relationship('User')
378 repository = relationship('Repository',cascade='')
378 repository = relationship('Repository',cascade='')
379
379
380
380
381 class UsersGroup(Base, BaseModel):
381 class UsersGroup(Base, BaseModel):
382 __tablename__ = 'users_groups'
382 __tablename__ = 'users_groups'
383 __table_args__ = {'extend_existing':True}
383 __table_args__ = {'extend_existing':True}
384
384
385 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
385 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
386 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
386 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
387 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
387 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
388
388
389 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
389 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
390
390
391 def __repr__(self):
391 def __repr__(self):
392 return '<userGroup(%s)>' % (self.users_group_name)
392 return '<userGroup(%s)>' % (self.users_group_name)
393
393
394 @classmethod
394 @classmethod
395 def get_by_group_name(cls, group_name, cache=False,
395 def get_by_group_name(cls, group_name, cache=False,
396 case_insensitive=False):
396 case_insensitive=False):
397 if case_insensitive:
397 if case_insensitive:
398 q = cls.query().filter(cls.users_group_name.ilike(group_name))
398 q = cls.query().filter(cls.users_group_name.ilike(group_name))
399 else:
399 else:
400 q = cls.query().filter(cls.users_group_name == group_name)
400 q = cls.query().filter(cls.users_group_name == group_name)
401 if cache:
401 if cache:
402 q = q.options(FromCache("sql_cache_short",
402 q = q.options(FromCache("sql_cache_short",
403 "get_user_%s" % group_name))
403 "get_user_%s" % group_name))
404 return q.scalar()
404 return q.scalar()
405
405
406 @classmethod
406 @classmethod
407 def get(cls, users_group_id, cache=False):
407 def get(cls, users_group_id, cache=False):
408 users_group = cls.query()
408 users_group = cls.query()
409 if cache:
409 if cache:
410 users_group = users_group.options(FromCache("sql_cache_short",
410 users_group = users_group.options(FromCache("sql_cache_short",
411 "get_users_group_%s" % users_group_id))
411 "get_users_group_%s" % users_group_id))
412 return users_group.get(users_group_id)
412 return users_group.get(users_group_id)
413
413
414
414
415 class UsersGroupMember(Base, BaseModel):
415 class UsersGroupMember(Base, BaseModel):
416 __tablename__ = 'users_groups_members'
416 __tablename__ = 'users_groups_members'
417 __table_args__ = {'extend_existing':True}
417 __table_args__ = {'extend_existing':True}
418
418
419 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
419 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
420 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
420 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
421 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
421 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
422
422
423 user = relationship('User', lazy='joined')
423 user = relationship('User', lazy='joined')
424 users_group = relationship('UsersGroup')
424 users_group = relationship('UsersGroup')
425
425
426 def __init__(self, gr_id='', u_id=''):
426 def __init__(self, gr_id='', u_id=''):
427 self.users_group_id = gr_id
427 self.users_group_id = gr_id
428 self.user_id = u_id
428 self.user_id = u_id
429
429
430 @staticmethod
430 @staticmethod
431 def add_user_to_group(group, user):
431 def add_user_to_group(group, user):
432 ugm = UsersGroupMember()
432 ugm = UsersGroupMember()
433 ugm.users_group = group
433 ugm.users_group = group
434 ugm.user = user
434 ugm.user = user
435 Session.add(ugm)
435 Session.add(ugm)
436 Session.commit()
436 Session.commit()
437 return ugm
437 return ugm
438
438
439
439
440 class Repository(Base, BaseModel):
440 class Repository(Base, BaseModel):
441 __tablename__ = 'repositories'
441 __tablename__ = 'repositories'
442 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
442 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
443
443
444 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
444 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
445 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
445 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
446 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
446 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
447 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
447 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
448 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
448 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
449 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
449 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
450 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
450 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
451 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
451 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
452 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
452 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
453 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
453 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
454
454
455 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
455 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
456 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
456 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
457
457
458 user = relationship('User')
458 user = relationship('User')
459 fork = relationship('Repository', remote_side=repo_id)
459 fork = relationship('Repository', remote_side=repo_id)
460 group = relationship('RepoGroup')
460 group = relationship('RepoGroup')
461 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
461 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
462 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
462 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
463 stats = relationship('Statistics', cascade='all', uselist=False)
463 stats = relationship('Statistics', cascade='all', uselist=False)
464
464
465 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
465 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
466
466
467 logs = relationship('UserLog')
467 logs = relationship('UserLog')
468
468
469 def __repr__(self):
469 def __repr__(self):
470 return "<%s('%s:%s')>" % (self.__class__.__name__,
470 return "<%s('%s:%s')>" % (self.__class__.__name__,
471 self.repo_id, self.repo_name)
471 self.repo_id, self.repo_name)
472
472
473 @classmethod
473 @classmethod
474 def url_sep(cls):
474 def url_sep(cls):
475 return '/'
475 return '/'
476
476
477 @classmethod
477 @classmethod
478 def get_by_repo_name(cls, repo_name):
478 def get_by_repo_name(cls, repo_name):
479 q = Session.query(cls).filter(cls.repo_name == repo_name)
479 q = Session.query(cls).filter(cls.repo_name == repo_name)
480 q = q.options(joinedload(Repository.fork))\
480 q = q.options(joinedload(Repository.fork))\
481 .options(joinedload(Repository.user))\
481 .options(joinedload(Repository.user))\
482 .options(joinedload(Repository.group))
482 .options(joinedload(Repository.group))
483 return q.scalar()
483 return q.scalar()
484
484
485 @classmethod
485 @classmethod
486 def get_repo_forks(cls, repo_id):
486 def get_repo_forks(cls, repo_id):
487 return cls.query().filter(Repository.fork_id == repo_id)
487 return cls.query().filter(Repository.fork_id == repo_id)
488
488
489 @classmethod
489 @classmethod
490 def base_path(cls):
490 def base_path(cls):
491 """
491 """
492 Returns base path when all repos are stored
492 Returns base path when all repos are stored
493
493
494 :param cls:
494 :param cls:
495 """
495 """
496 q = Session.query(RhodeCodeUi)\
496 q = Session.query(RhodeCodeUi)\
497 .filter(RhodeCodeUi.ui_key == cls.url_sep())
497 .filter(RhodeCodeUi.ui_key == cls.url_sep())
498 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
498 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
499 return q.one().ui_value
499 return q.one().ui_value
500
500
501 @property
501 @property
502 def just_name(self):
502 def just_name(self):
503 return self.repo_name.split(Repository.url_sep())[-1]
503 return self.repo_name.split(Repository.url_sep())[-1]
504
504
505 @property
505 @property
506 def groups_with_parents(self):
506 def groups_with_parents(self):
507 groups = []
507 groups = []
508 if self.group is None:
508 if self.group is None:
509 return groups
509 return groups
510
510
511 cur_gr = self.group
511 cur_gr = self.group
512 groups.insert(0, cur_gr)
512 groups.insert(0, cur_gr)
513 while 1:
513 while 1:
514 gr = getattr(cur_gr, 'parent_group', None)
514 gr = getattr(cur_gr, 'parent_group', None)
515 cur_gr = cur_gr.parent_group
515 cur_gr = cur_gr.parent_group
516 if gr is None:
516 if gr is None:
517 break
517 break
518 groups.insert(0, gr)
518 groups.insert(0, gr)
519
519
520 return groups
520 return groups
521
521
522 @property
522 @property
523 def groups_and_repo(self):
523 def groups_and_repo(self):
524 return self.groups_with_parents, self.just_name
524 return self.groups_with_parents, self.just_name
525
525
526 @LazyProperty
526 @LazyProperty
527 def repo_path(self):
527 def repo_path(self):
528 """
528 """
529 Returns base full path for that repository means where it actually
529 Returns base full path for that repository means where it actually
530 exists on a filesystem
530 exists on a filesystem
531 """
531 """
532 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
532 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
533 Repository.url_sep())
533 Repository.url_sep())
534 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
534 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
535 return q.one().ui_value
535 return q.one().ui_value
536
536
537 @property
537 @property
538 def repo_full_path(self):
538 def repo_full_path(self):
539 p = [self.repo_path]
539 p = [self.repo_path]
540 # we need to split the name by / since this is how we store the
540 # we need to split the name by / since this is how we store the
541 # names in the database, but that eventually needs to be converted
541 # names in the database, but that eventually needs to be converted
542 # into a valid system path
542 # into a valid system path
543 p += self.repo_name.split(Repository.url_sep())
543 p += self.repo_name.split(Repository.url_sep())
544 return os.path.join(*p)
544 return os.path.join(*p)
545
545
546 def get_new_name(self, repo_name):
546 def get_new_name(self, repo_name):
547 """
547 """
548 returns new full repository name based on assigned group and new new
548 returns new full repository name based on assigned group and new new
549
549
550 :param group_name:
550 :param group_name:
551 """
551 """
552 path_prefix = self.group.full_path_splitted if self.group else []
552 path_prefix = self.group.full_path_splitted if self.group else []
553 return Repository.url_sep().join(path_prefix + [repo_name])
553 return Repository.url_sep().join(path_prefix + [repo_name])
554
554
555 @property
555 @property
556 def _ui(self):
556 def _ui(self):
557 """
557 """
558 Creates an db based ui object for this repository
558 Creates an db based ui object for this repository
559 """
559 """
560 from mercurial import ui
560 from mercurial import ui
561 from mercurial import config
561 from mercurial import config
562 baseui = ui.ui()
562 baseui = ui.ui()
563
563
564 #clean the baseui object
564 #clean the baseui object
565 baseui._ocfg = config.config()
565 baseui._ocfg = config.config()
566 baseui._ucfg = config.config()
566 baseui._ucfg = config.config()
567 baseui._tcfg = config.config()
567 baseui._tcfg = config.config()
568
568
569 ret = RhodeCodeUi.query()\
569 ret = RhodeCodeUi.query()\
570 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
570 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
571
571
572 hg_ui = ret
572 hg_ui = ret
573 for ui_ in hg_ui:
573 for ui_ in hg_ui:
574 if ui_.ui_active:
574 if ui_.ui_active:
575 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
575 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
576 ui_.ui_key, ui_.ui_value)
576 ui_.ui_key, ui_.ui_value)
577 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
577 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
578
578
579 return baseui
579 return baseui
580
580
581 @classmethod
581 @classmethod
582 def is_valid(cls, repo_name):
582 def is_valid(cls, repo_name):
583 """
583 """
584 returns True if given repo name is a valid filesystem repository
584 returns True if given repo name is a valid filesystem repository
585
585
586 @param cls:
586 :param cls:
587 @param repo_name:
587 :param repo_name:
588 """
588 """
589 from rhodecode.lib.utils import is_valid_repo
589 from rhodecode.lib.utils import is_valid_repo
590
590
591 return is_valid_repo(repo_name, cls.base_path())
591 return is_valid_repo(repo_name, cls.base_path())
592
592
593 #==========================================================================
593 #==========================================================================
594 # SCM PROPERTIES
594 # SCM PROPERTIES
595 #==========================================================================
595 #==========================================================================
596
596
597 def get_changeset(self, rev):
597 def get_changeset(self, rev):
598 return get_changeset_safe(self.scm_instance, rev)
598 return get_changeset_safe(self.scm_instance, rev)
599
599
600 @property
600 @property
601 def tip(self):
601 def tip(self):
602 return self.get_changeset('tip')
602 return self.get_changeset('tip')
603
603
604 @property
604 @property
605 def author(self):
605 def author(self):
606 return self.tip.author
606 return self.tip.author
607
607
608 @property
608 @property
609 def last_change(self):
609 def last_change(self):
610 return self.scm_instance.last_change
610 return self.scm_instance.last_change
611
611
612 #==========================================================================
612 #==========================================================================
613 # SCM CACHE INSTANCE
613 # SCM CACHE INSTANCE
614 #==========================================================================
614 #==========================================================================
615
615
616 @property
616 @property
617 def invalidate(self):
617 def invalidate(self):
618 return CacheInvalidation.invalidate(self.repo_name)
618 return CacheInvalidation.invalidate(self.repo_name)
619
619
620 def set_invalidate(self):
620 def set_invalidate(self):
621 """
621 """
622 set a cache for invalidation for this instance
622 set a cache for invalidation for this instance
623 """
623 """
624 CacheInvalidation.set_invalidate(self.repo_name)
624 CacheInvalidation.set_invalidate(self.repo_name)
625
625
626 @LazyProperty
626 @LazyProperty
627 def scm_instance(self):
627 def scm_instance(self):
628 return self.__get_instance()
628 return self.__get_instance()
629
629
630 @property
630 @property
631 def scm_instance_cached(self):
631 def scm_instance_cached(self):
632 @cache_region('long_term')
632 @cache_region('long_term')
633 def _c(repo_name):
633 def _c(repo_name):
634 return self.__get_instance()
634 return self.__get_instance()
635 rn = self.repo_name
635 rn = self.repo_name
636 log.debug('Getting cached instance of repo')
636 log.debug('Getting cached instance of repo')
637 inv = self.invalidate
637 inv = self.invalidate
638 if inv is not None:
638 if inv is not None:
639 region_invalidate(_c, None, rn)
639 region_invalidate(_c, None, rn)
640 # update our cache
640 # update our cache
641 CacheInvalidation.set_valid(inv.cache_key)
641 CacheInvalidation.set_valid(inv.cache_key)
642 return _c(rn)
642 return _c(rn)
643
643
644 def __get_instance(self):
644 def __get_instance(self):
645 repo_full_path = self.repo_full_path
645 repo_full_path = self.repo_full_path
646 try:
646 try:
647 alias = get_scm(repo_full_path)[0]
647 alias = get_scm(repo_full_path)[0]
648 log.debug('Creating instance of %s repository', alias)
648 log.debug('Creating instance of %s repository', alias)
649 backend = get_backend(alias)
649 backend = get_backend(alias)
650 except VCSError:
650 except VCSError:
651 log.error(traceback.format_exc())
651 log.error(traceback.format_exc())
652 log.error('Perhaps this repository is in db and not in '
652 log.error('Perhaps this repository is in db and not in '
653 'filesystem run rescan repositories with '
653 'filesystem run rescan repositories with '
654 '"destroy old data " option from admin panel')
654 '"destroy old data " option from admin panel')
655 return
655 return
656
656
657 if alias == 'hg':
657 if alias == 'hg':
658 repo = backend(safe_str(repo_full_path), create=False,
658 repo = backend(safe_str(repo_full_path), create=False,
659 baseui=self._ui)
659 baseui=self._ui)
660 # skip hidden web repository
660 # skip hidden web repository
661 if repo._get_hidden():
661 if repo._get_hidden():
662 return
662 return
663 else:
663 else:
664 repo = backend(repo_full_path, create=False)
664 repo = backend(repo_full_path, create=False)
665
665
666 return repo
666 return repo
667
667
668
668
669 class RepoGroup(Base, BaseModel):
669 class RepoGroup(Base, BaseModel):
670 __tablename__ = 'groups'
670 __tablename__ = 'groups'
671 __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'),
671 __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'),
672 CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},)
672 CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},)
673 __mapper_args__ = {'order_by':'group_name'}
673 __mapper_args__ = {'order_by':'group_name'}
674
674
675 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
675 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
676 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
676 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
677 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
677 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
678 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
678 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
679
679
680 parent_group = relationship('RepoGroup', remote_side=group_id)
680 parent_group = relationship('RepoGroup', remote_side=group_id)
681
681
682 def __init__(self, group_name='', parent_group=None):
682 def __init__(self, group_name='', parent_group=None):
683 self.group_name = group_name
683 self.group_name = group_name
684 self.parent_group = parent_group
684 self.parent_group = parent_group
685
685
686 def __repr__(self):
686 def __repr__(self):
687 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
687 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
688 self.group_name)
688 self.group_name)
689
689
690 @classmethod
690 @classmethod
691 def groups_choices(cls):
691 def groups_choices(cls):
692 from webhelpers.html import literal as _literal
692 from webhelpers.html import literal as _literal
693 repo_groups = [('', '')]
693 repo_groups = [('', '')]
694 sep = ' &raquo; '
694 sep = ' &raquo; '
695 _name = lambda k: _literal(sep.join(k))
695 _name = lambda k: _literal(sep.join(k))
696
696
697 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
697 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
698 for x in cls.query().all()])
698 for x in cls.query().all()])
699
699
700 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
700 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
701 return repo_groups
701 return repo_groups
702
702
703 @classmethod
703 @classmethod
704 def url_sep(cls):
704 def url_sep(cls):
705 return '/'
705 return '/'
706
706
707 @classmethod
707 @classmethod
708 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
708 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
709 if case_insensitive:
709 if case_insensitive:
710 gr = cls.query()\
710 gr = cls.query()\
711 .filter(cls.group_name.ilike(group_name))
711 .filter(cls.group_name.ilike(group_name))
712 else:
712 else:
713 gr = cls.query()\
713 gr = cls.query()\
714 .filter(cls.group_name == group_name)
714 .filter(cls.group_name == group_name)
715 if cache:
715 if cache:
716 gr = gr.options(FromCache("sql_cache_short",
716 gr = gr.options(FromCache("sql_cache_short",
717 "get_group_%s" % group_name))
717 "get_group_%s" % group_name))
718 return gr.scalar()
718 return gr.scalar()
719
719
720 @property
720 @property
721 def parents(self):
721 def parents(self):
722 parents_recursion_limit = 5
722 parents_recursion_limit = 5
723 groups = []
723 groups = []
724 if self.parent_group is None:
724 if self.parent_group is None:
725 return groups
725 return groups
726 cur_gr = self.parent_group
726 cur_gr = self.parent_group
727 groups.insert(0, cur_gr)
727 groups.insert(0, cur_gr)
728 cnt = 0
728 cnt = 0
729 while 1:
729 while 1:
730 cnt += 1
730 cnt += 1
731 gr = getattr(cur_gr, 'parent_group', None)
731 gr = getattr(cur_gr, 'parent_group', None)
732 cur_gr = cur_gr.parent_group
732 cur_gr = cur_gr.parent_group
733 if gr is None:
733 if gr is None:
734 break
734 break
735 if cnt == parents_recursion_limit:
735 if cnt == parents_recursion_limit:
736 # this will prevent accidental infinit loops
736 # this will prevent accidental infinit loops
737 log.error('group nested more than %s' %
737 log.error('group nested more than %s' %
738 parents_recursion_limit)
738 parents_recursion_limit)
739 break
739 break
740
740
741 groups.insert(0, gr)
741 groups.insert(0, gr)
742 return groups
742 return groups
743
743
744 @property
744 @property
745 def children(self):
745 def children(self):
746 return RepoGroup.query().filter(RepoGroup.parent_group == self)
746 return RepoGroup.query().filter(RepoGroup.parent_group == self)
747
747
748 @property
748 @property
749 def name(self):
749 def name(self):
750 return self.group_name.split(RepoGroup.url_sep())[-1]
750 return self.group_name.split(RepoGroup.url_sep())[-1]
751
751
752 @property
752 @property
753 def full_path(self):
753 def full_path(self):
754 return self.group_name
754 return self.group_name
755
755
756 @property
756 @property
757 def full_path_splitted(self):
757 def full_path_splitted(self):
758 return self.group_name.split(RepoGroup.url_sep())
758 return self.group_name.split(RepoGroup.url_sep())
759
759
760 @property
760 @property
761 def repositories(self):
761 def repositories(self):
762 return Repository.query().filter(Repository.group == self)
762 return Repository.query().filter(Repository.group == self)
763
763
764 @property
764 @property
765 def repositories_recursive_count(self):
765 def repositories_recursive_count(self):
766 cnt = self.repositories.count()
766 cnt = self.repositories.count()
767
767
768 def children_count(group):
768 def children_count(group):
769 cnt = 0
769 cnt = 0
770 for child in group.children:
770 for child in group.children:
771 cnt += child.repositories.count()
771 cnt += child.repositories.count()
772 cnt += children_count(child)
772 cnt += children_count(child)
773 return cnt
773 return cnt
774
774
775 return cnt + children_count(self)
775 return cnt + children_count(self)
776
776
777
777
778 def get_new_name(self, group_name):
778 def get_new_name(self, group_name):
779 """
779 """
780 returns new full group name based on parent and new name
780 returns new full group name based on parent and new name
781
781
782 :param group_name:
782 :param group_name:
783 """
783 """
784 path_prefix = (self.parent_group.full_path_splitted if
784 path_prefix = (self.parent_group.full_path_splitted if
785 self.parent_group else [])
785 self.parent_group else [])
786 return RepoGroup.url_sep().join(path_prefix + [group_name])
786 return RepoGroup.url_sep().join(path_prefix + [group_name])
787
787
788
788
789 class Permission(Base, BaseModel):
789 class Permission(Base, BaseModel):
790 __tablename__ = 'permissions'
790 __tablename__ = 'permissions'
791 __table_args__ = {'extend_existing':True}
791 __table_args__ = {'extend_existing':True}
792 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
792 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
793 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
793 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
794 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
794 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
795
795
796 def __repr__(self):
796 def __repr__(self):
797 return "<%s('%s:%s')>" % (self.__class__.__name__,
797 return "<%s('%s:%s')>" % (self.__class__.__name__,
798 self.permission_id, self.permission_name)
798 self.permission_id, self.permission_name)
799
799
800 @classmethod
800 @classmethod
801 def get_by_key(cls, key):
801 def get_by_key(cls, key):
802 return cls.query().filter(cls.permission_name == key).scalar()
802 return cls.query().filter(cls.permission_name == key).scalar()
803
803
804 @classmethod
804 @classmethod
805 def get_default_perms(cls, default_user_id):
805 def get_default_perms(cls, default_user_id):
806 q = Session.query(UserRepoToPerm, Repository, cls)\
806 q = Session.query(UserRepoToPerm, Repository, cls)\
807 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
807 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
808 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
808 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
809 .filter(UserRepoToPerm.user_id == default_user_id)
809 .filter(UserRepoToPerm.user_id == default_user_id)
810
810
811 return q.all()
811 return q.all()
812
812
813
813
814 class UserRepoToPerm(Base, BaseModel):
814 class UserRepoToPerm(Base, BaseModel):
815 __tablename__ = 'repo_to_perm'
815 __tablename__ = 'repo_to_perm'
816 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True})
816 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True})
817 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
817 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
818 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
818 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
819 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
819 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
820 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
820 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
821
821
822 user = relationship('User')
822 user = relationship('User')
823 permission = relationship('Permission')
823 permission = relationship('Permission')
824 repository = relationship('Repository')
824 repository = relationship('Repository')
825
825
826 @classmethod
826 @classmethod
827 def create(cls, user, repository, permission):
827 def create(cls, user, repository, permission):
828 n = cls()
828 n = cls()
829 n.user = user
829 n.user = user
830 n.repository = repository
830 n.repository = repository
831 n.permission = permission
831 n.permission = permission
832 Session.add(n)
832 Session.add(n)
833 return n
833 return n
834
834
835 def __repr__(self):
835 def __repr__(self):
836 return '<user:%s => %s >' % (self.user, self.repository)
836 return '<user:%s => %s >' % (self.user, self.repository)
837
837
838
838
839 class UserToPerm(Base, BaseModel):
839 class UserToPerm(Base, BaseModel):
840 __tablename__ = 'user_to_perm'
840 __tablename__ = 'user_to_perm'
841 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
841 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
842 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
842 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
843 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
843 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
844 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
844 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
845
845
846 user = relationship('User')
846 user = relationship('User')
847 permission = relationship('Permission', lazy='joined')
847 permission = relationship('Permission', lazy='joined')
848
848
849
849
850 class UsersGroupRepoToPerm(Base, BaseModel):
850 class UsersGroupRepoToPerm(Base, BaseModel):
851 __tablename__ = 'users_group_repo_to_perm'
851 __tablename__ = 'users_group_repo_to_perm'
852 __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True})
852 __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True})
853 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
853 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
854 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
854 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
855 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
855 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
856 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
856 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
857
857
858 users_group = relationship('UsersGroup')
858 users_group = relationship('UsersGroup')
859 permission = relationship('Permission')
859 permission = relationship('Permission')
860 repository = relationship('Repository')
860 repository = relationship('Repository')
861
861
862 @classmethod
862 @classmethod
863 def create(cls, users_group, repository, permission):
863 def create(cls, users_group, repository, permission):
864 n = cls()
864 n = cls()
865 n.users_group = users_group
865 n.users_group = users_group
866 n.repository = repository
866 n.repository = repository
867 n.permission = permission
867 n.permission = permission
868 Session.add(n)
868 Session.add(n)
869 return n
869 return n
870
870
871 def __repr__(self):
871 def __repr__(self):
872 return '<userGroup:%s => %s >' % (self.users_group, self.repository)
872 return '<userGroup:%s => %s >' % (self.users_group, self.repository)
873
873
874
874
875 class UsersGroupToPerm(Base, BaseModel):
875 class UsersGroupToPerm(Base, BaseModel):
876 __tablename__ = 'users_group_to_perm'
876 __tablename__ = 'users_group_to_perm'
877 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
877 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
878 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
878 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
879 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
879 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
880
880
881 users_group = relationship('UsersGroup')
881 users_group = relationship('UsersGroup')
882 permission = relationship('Permission')
882 permission = relationship('Permission')
883
883
884
884
885 class UserRepoGroupToPerm(Base, BaseModel):
885 class UserRepoGroupToPerm(Base, BaseModel):
886 __tablename__ = 'group_to_perm'
886 __tablename__ = 'group_to_perm'
887 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
887 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
888
888
889 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
889 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
890 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
890 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
891 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
891 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
892 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
892 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
893
893
894 user = relationship('User')
894 user = relationship('User')
895 permission = relationship('Permission')
895 permission = relationship('Permission')
896 group = relationship('RepoGroup')
896 group = relationship('RepoGroup')
897
897
898
898
899 class UsersGroupRepoGroupToPerm(Base, BaseModel):
899 class UsersGroupRepoGroupToPerm(Base, BaseModel):
900 __tablename__ = 'users_group_repo_group_to_perm'
900 __tablename__ = 'users_group_repo_group_to_perm'
901 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
901 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
902
902
903 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)
903 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)
904 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
904 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
905 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
905 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
906 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
906 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
907
907
908 users_group = relationship('UsersGroup')
908 users_group = relationship('UsersGroup')
909 permission = relationship('Permission')
909 permission = relationship('Permission')
910 group = relationship('RepoGroup')
910 group = relationship('RepoGroup')
911
911
912
912
913 class Statistics(Base, BaseModel):
913 class Statistics(Base, BaseModel):
914 __tablename__ = 'statistics'
914 __tablename__ = 'statistics'
915 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True})
915 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True})
916 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
916 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
917 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
917 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
918 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
918 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
919 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
919 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
920 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
920 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
921 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
921 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
922
922
923 repository = relationship('Repository', single_parent=True)
923 repository = relationship('Repository', single_parent=True)
924
924
925
925
926 class UserFollowing(Base, BaseModel):
926 class UserFollowing(Base, BaseModel):
927 __tablename__ = 'user_followings'
927 __tablename__ = 'user_followings'
928 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
928 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
929 UniqueConstraint('user_id', 'follows_user_id')
929 UniqueConstraint('user_id', 'follows_user_id')
930 , {'extend_existing':True})
930 , {'extend_existing':True})
931
931
932 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
932 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
933 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
933 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
934 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
934 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
935 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
935 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
936 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
936 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
937
937
938 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
938 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
939
939
940 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
940 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
941 follows_repository = relationship('Repository', order_by='Repository.repo_name')
941 follows_repository = relationship('Repository', order_by='Repository.repo_name')
942
942
943
943
944 @classmethod
944 @classmethod
945 def get_repo_followers(cls, repo_id):
945 def get_repo_followers(cls, repo_id):
946 return cls.query().filter(cls.follows_repo_id == repo_id)
946 return cls.query().filter(cls.follows_repo_id == repo_id)
947
947
948
948
949 class CacheInvalidation(Base, BaseModel):
949 class CacheInvalidation(Base, BaseModel):
950 __tablename__ = 'cache_invalidation'
950 __tablename__ = 'cache_invalidation'
951 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True})
951 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True})
952 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
952 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
953 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
953 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
954 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
954 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
955 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
955 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
956
956
957
957
958 def __init__(self, cache_key, cache_args=''):
958 def __init__(self, cache_key, cache_args=''):
959 self.cache_key = cache_key
959 self.cache_key = cache_key
960 self.cache_args = cache_args
960 self.cache_args = cache_args
961 self.cache_active = False
961 self.cache_active = False
962
962
963 def __repr__(self):
963 def __repr__(self):
964 return "<%s('%s:%s')>" % (self.__class__.__name__,
964 return "<%s('%s:%s')>" % (self.__class__.__name__,
965 self.cache_id, self.cache_key)
965 self.cache_id, self.cache_key)
966
966
967 @classmethod
967 @classmethod
968 def invalidate(cls, key):
968 def invalidate(cls, key):
969 """
969 """
970 Returns Invalidation object if this given key should be invalidated
970 Returns Invalidation object if this given key should be invalidated
971 None otherwise. `cache_active = False` means that this cache
971 None otherwise. `cache_active = False` means that this cache
972 state is not valid and needs to be invalidated
972 state is not valid and needs to be invalidated
973
973
974 :param key:
974 :param key:
975 """
975 """
976 return cls.query()\
976 return cls.query()\
977 .filter(CacheInvalidation.cache_key == key)\
977 .filter(CacheInvalidation.cache_key == key)\
978 .filter(CacheInvalidation.cache_active == False)\
978 .filter(CacheInvalidation.cache_active == False)\
979 .scalar()
979 .scalar()
980
980
981 @classmethod
981 @classmethod
982 def set_invalidate(cls, key):
982 def set_invalidate(cls, key):
983 """
983 """
984 Mark this Cache key for invalidation
984 Mark this Cache key for invalidation
985
985
986 :param key:
986 :param key:
987 """
987 """
988
988
989 log.debug('marking %s for invalidation' % key)
989 log.debug('marking %s for invalidation' % key)
990 inv_obj = Session.query(cls)\
990 inv_obj = Session.query(cls)\
991 .filter(cls.cache_key == key).scalar()
991 .filter(cls.cache_key == key).scalar()
992 if inv_obj:
992 if inv_obj:
993 inv_obj.cache_active = False
993 inv_obj.cache_active = False
994 else:
994 else:
995 log.debug('cache key not found in invalidation db -> creating one')
995 log.debug('cache key not found in invalidation db -> creating one')
996 inv_obj = CacheInvalidation(key)
996 inv_obj = CacheInvalidation(key)
997
997
998 try:
998 try:
999 Session.add(inv_obj)
999 Session.add(inv_obj)
1000 Session.commit()
1000 Session.commit()
1001 except Exception:
1001 except Exception:
1002 log.error(traceback.format_exc())
1002 log.error(traceback.format_exc())
1003 Session.rollback()
1003 Session.rollback()
1004
1004
1005 @classmethod
1005 @classmethod
1006 def set_valid(cls, key):
1006 def set_valid(cls, key):
1007 """
1007 """
1008 Mark this cache key as active and currently cached
1008 Mark this cache key as active and currently cached
1009
1009
1010 :param key:
1010 :param key:
1011 """
1011 """
1012 inv_obj = CacheInvalidation.query()\
1012 inv_obj = CacheInvalidation.query()\
1013 .filter(CacheInvalidation.cache_key == key).scalar()
1013 .filter(CacheInvalidation.cache_key == key).scalar()
1014 inv_obj.cache_active = True
1014 inv_obj.cache_active = True
1015 Session.add(inv_obj)
1015 Session.add(inv_obj)
1016 Session.commit()
1016 Session.commit()
1017
1017
1018
1018
1019 class ChangesetComment(Base, BaseModel):
1019 class ChangesetComment(Base, BaseModel):
1020 __tablename__ = 'changeset_comments'
1020 __tablename__ = 'changeset_comments'
1021 __table_args__ = ({'extend_existing':True},)
1021 __table_args__ = ({'extend_existing':True},)
1022 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1022 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1023 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1023 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1024 revision = Column('revision', String(40), nullable=False)
1024 revision = Column('revision', String(40), nullable=False)
1025 line_no = Column('line_no', Unicode(10), nullable=True)
1025 line_no = Column('line_no', Unicode(10), nullable=True)
1026 f_path = Column('f_path', Unicode(1000), nullable=True)
1026 f_path = Column('f_path', Unicode(1000), nullable=True)
1027 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1027 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1028 text = Column('text', Unicode(25000), nullable=False)
1028 text = Column('text', Unicode(25000), nullable=False)
1029 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1029 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1030
1030
1031 author = relationship('User', lazy='joined')
1031 author = relationship('User', lazy='joined')
1032 repo = relationship('Repository')
1032 repo = relationship('Repository')
1033
1033
1034
1034
1035 @classmethod
1035 @classmethod
1036 def get_users(cls, revision):
1036 def get_users(cls, revision):
1037 """
1037 """
1038 Returns user associated with this changesetComment. ie those
1038 Returns user associated with this changesetComment. ie those
1039 who actually commented
1039 who actually commented
1040
1040
1041 :param cls:
1041 :param cls:
1042 :param revision:
1042 :param revision:
1043 """
1043 """
1044 return Session.query(User)\
1044 return Session.query(User)\
1045 .filter(cls.revision == revision)\
1045 .filter(cls.revision == revision)\
1046 .join(ChangesetComment.author).all()
1046 .join(ChangesetComment.author).all()
1047
1047
1048
1048
1049 class Notification(Base, BaseModel):
1049 class Notification(Base, BaseModel):
1050 __tablename__ = 'notifications'
1050 __tablename__ = 'notifications'
1051 __table_args__ = ({'extend_existing':True})
1051 __table_args__ = ({'extend_existing':True})
1052
1052
1053 TYPE_CHANGESET_COMMENT = u'cs_comment'
1053 TYPE_CHANGESET_COMMENT = u'cs_comment'
1054 TYPE_MESSAGE = u'message'
1054 TYPE_MESSAGE = u'message'
1055 TYPE_MENTION = u'mention'
1055 TYPE_MENTION = u'mention'
1056 TYPE_REGISTRATION = u'registration'
1056 TYPE_REGISTRATION = u'registration'
1057
1057
1058 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1058 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1059 subject = Column('subject', Unicode(512), nullable=True)
1059 subject = Column('subject', Unicode(512), nullable=True)
1060 body = Column('body', Unicode(50000), nullable=True)
1060 body = Column('body', Unicode(50000), nullable=True)
1061 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1061 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1062 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1062 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1063 type_ = Column('type', Unicode(256))
1063 type_ = Column('type', Unicode(256))
1064
1064
1065 created_by_user = relationship('User')
1065 created_by_user = relationship('User')
1066 notifications_to_users = relationship('UserNotification', lazy='joined',
1066 notifications_to_users = relationship('UserNotification', lazy='joined',
1067 cascade="all, delete, delete-orphan")
1067 cascade="all, delete, delete-orphan")
1068
1068
1069 @property
1069 @property
1070 def recipients(self):
1070 def recipients(self):
1071 return [x.user for x in UserNotification.query()\
1071 return [x.user for x in UserNotification.query()\
1072 .filter(UserNotification.notification == self).all()]
1072 .filter(UserNotification.notification == self).all()]
1073
1073
1074 @classmethod
1074 @classmethod
1075 def create(cls, created_by, subject, body, recipients, type_=None):
1075 def create(cls, created_by, subject, body, recipients, type_=None):
1076 if type_ is None:
1076 if type_ is None:
1077 type_ = Notification.TYPE_MESSAGE
1077 type_ = Notification.TYPE_MESSAGE
1078
1078
1079 notification = cls()
1079 notification = cls()
1080 notification.created_by_user = created_by
1080 notification.created_by_user = created_by
1081 notification.subject = subject
1081 notification.subject = subject
1082 notification.body = body
1082 notification.body = body
1083 notification.type_ = type_
1083 notification.type_ = type_
1084 notification.created_on = datetime.datetime.now()
1084 notification.created_on = datetime.datetime.now()
1085
1085
1086 for u in recipients:
1086 for u in recipients:
1087 assoc = UserNotification()
1087 assoc = UserNotification()
1088 assoc.notification = notification
1088 assoc.notification = notification
1089 u.notifications.append(assoc)
1089 u.notifications.append(assoc)
1090 Session.add(notification)
1090 Session.add(notification)
1091 return notification
1091 return notification
1092
1092
1093 @property
1093 @property
1094 def description(self):
1094 def description(self):
1095 from rhodecode.model.notification import NotificationModel
1095 from rhodecode.model.notification import NotificationModel
1096 return NotificationModel().make_description(self)
1096 return NotificationModel().make_description(self)
1097
1097
1098
1098
1099 class UserNotification(Base, BaseModel):
1099 class UserNotification(Base, BaseModel):
1100 __tablename__ = 'user_to_notification'
1100 __tablename__ = 'user_to_notification'
1101 __table_args__ = (UniqueConstraint('user_id', 'notification_id'),
1101 __table_args__ = (UniqueConstraint('user_id', 'notification_id'),
1102 {'extend_existing':True})
1102 {'extend_existing':True})
1103 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), primary_key=True)
1103 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), primary_key=True)
1104 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1104 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1105 read = Column('read', Boolean, default=False)
1105 read = Column('read', Boolean, default=False)
1106 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1106 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1107
1107
1108 user = relationship('User', lazy="joined")
1108 user = relationship('User', lazy="joined")
1109 notification = relationship('Notification', lazy="joined",
1109 notification = relationship('Notification', lazy="joined",
1110 order_by=lambda:Notification.created_on.desc(),)
1110 order_by=lambda:Notification.created_on.desc(),)
1111
1111
1112 def mark_as_read(self):
1112 def mark_as_read(self):
1113 self.read = True
1113 self.read = True
1114 Session.add(self)
1114 Session.add(self)
1115
1115
1116
1116
1117 class DbMigrateVersion(Base, BaseModel):
1117 class DbMigrateVersion(Base, BaseModel):
1118 __tablename__ = 'db_migrate_version'
1118 __tablename__ = 'db_migrate_version'
1119 __table_args__ = {'extend_existing':True}
1119 __table_args__ = {'extend_existing':True}
1120 repository_id = Column('repository_id', String(250), primary_key=True)
1120 repository_id = Column('repository_id', String(250), primary_key=True)
1121 repository_path = Column('repository_path', Text)
1121 repository_path = Column('repository_path', Text)
1122 version = Column('version', Integer)
1122 version = Column('version', Integer)
General Comments 0
You need to be logged in to leave comments. Login now