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