##// END OF EJS Templates
caches: use optional identity cache options on get_by_repo_name and get_by_username...
marcink -
r255:a979e37d default
parent child Browse files
Show More
@@ -1,3437 +1,3462 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Database Models for RhodeCode Enterprise
22 Database Models for RhodeCode Enterprise
23 """
23 """
24
24
25 import os
25 import os
26 import sys
26 import sys
27 import time
27 import time
28 import hashlib
28 import hashlib
29 import logging
29 import logging
30 import datetime
30 import datetime
31 import warnings
31 import warnings
32 import ipaddress
32 import ipaddress
33 import functools
33 import functools
34 import traceback
34 import traceback
35 import collections
35 import collections
36
36
37
37
38 from sqlalchemy import *
38 from sqlalchemy import *
39 from sqlalchemy.exc import IntegrityError
39 from sqlalchemy.exc import IntegrityError
40 from sqlalchemy.ext.declarative import declared_attr
40 from sqlalchemy.ext.declarative import declared_attr
41 from sqlalchemy.ext.hybrid import hybrid_property
41 from sqlalchemy.ext.hybrid import hybrid_property
42 from sqlalchemy.orm import (
42 from sqlalchemy.orm import (
43 relationship, joinedload, class_mapper, validates, aliased)
43 relationship, joinedload, class_mapper, validates, aliased)
44 from sqlalchemy.sql.expression import true
44 from sqlalchemy.sql.expression import true
45 from beaker.cache import cache_region, region_invalidate
45 from beaker.cache import cache_region, region_invalidate
46 from webob.exc import HTTPNotFound
46 from webob.exc import HTTPNotFound
47 from zope.cachedescriptors.property import Lazy as LazyProperty
47 from zope.cachedescriptors.property import Lazy as LazyProperty
48
48
49 from pylons import url
49 from pylons import url
50 from pylons.i18n.translation import lazy_ugettext as _
50 from pylons.i18n.translation import lazy_ugettext as _
51
51
52 from rhodecode.lib.vcs import get_backend
52 from rhodecode.lib.vcs import get_backend
53 from rhodecode.lib.vcs.utils.helpers import get_scm
53 from rhodecode.lib.vcs.utils.helpers import get_scm
54 from rhodecode.lib.vcs.exceptions import VCSError
54 from rhodecode.lib.vcs.exceptions import VCSError
55 from rhodecode.lib.vcs.backends.base import (
55 from rhodecode.lib.vcs.backends.base import (
56 EmptyCommit, Reference, MergeFailureReason)
56 EmptyCommit, Reference, MergeFailureReason)
57 from rhodecode.lib.utils2 import (
57 from rhodecode.lib.utils2 import (
58 str2bool, safe_str, get_commit_safe, safe_unicode, remove_prefix, md5_safe,
58 str2bool, safe_str, get_commit_safe, safe_unicode, remove_prefix, md5_safe,
59 time_to_datetime, aslist, Optional, safe_int, get_clone_url, AttributeDict)
59 time_to_datetime, aslist, Optional, safe_int, get_clone_url, AttributeDict)
60 from rhodecode.lib.ext_json import json
60 from rhodecode.lib.ext_json import json
61 from rhodecode.lib.caching_query import FromCache
61 from rhodecode.lib.caching_query import FromCache
62 from rhodecode.lib.encrypt import AESCipher
62 from rhodecode.lib.encrypt import AESCipher
63
63
64 from rhodecode.model.meta import Base, Session
64 from rhodecode.model.meta import Base, Session
65
65
66 URL_SEP = '/'
66 URL_SEP = '/'
67 log = logging.getLogger(__name__)
67 log = logging.getLogger(__name__)
68
68
69 # =============================================================================
69 # =============================================================================
70 # BASE CLASSES
70 # BASE CLASSES
71 # =============================================================================
71 # =============================================================================
72
72
73 # this is propagated from .ini file beaker.session.secret
73 # this is propagated from .ini file beaker.session.secret
74 # and initialized at environment.py
74 # and initialized at environment.py
75 ENCRYPTION_KEY = None
75 ENCRYPTION_KEY = None
76
76
77 # used to sort permissions by types, '#' used here is not allowed to be in
77 # used to sort permissions by types, '#' used here is not allowed to be in
78 # usernames, and it's very early in sorted string.printable table.
78 # usernames, and it's very early in sorted string.printable table.
79 PERMISSION_TYPE_SORT = {
79 PERMISSION_TYPE_SORT = {
80 'admin': '####',
80 'admin': '####',
81 'write': '###',
81 'write': '###',
82 'read': '##',
82 'read': '##',
83 'none': '#',
83 'none': '#',
84 }
84 }
85
85
86
86
87 def display_sort(obj):
87 def display_sort(obj):
88 """
88 """
89 Sort function used to sort permissions in .permissions() function of
89 Sort function used to sort permissions in .permissions() function of
90 Repository, RepoGroup, UserGroup. Also it put the default user in front
90 Repository, RepoGroup, UserGroup. Also it put the default user in front
91 of all other resources
91 of all other resources
92 """
92 """
93
93
94 if obj.username == User.DEFAULT_USER:
94 if obj.username == User.DEFAULT_USER:
95 return '#####'
95 return '#####'
96 prefix = PERMISSION_TYPE_SORT.get(obj.permission.split('.')[-1], '')
96 prefix = PERMISSION_TYPE_SORT.get(obj.permission.split('.')[-1], '')
97 return prefix + obj.username
97 return prefix + obj.username
98
98
99
99
100 def _hash_key(k):
100 def _hash_key(k):
101 return md5_safe(k)
101 return md5_safe(k)
102
102
103
103
104 class EncryptedTextValue(TypeDecorator):
104 class EncryptedTextValue(TypeDecorator):
105 """
105 """
106 Special column for encrypted long text data, use like::
106 Special column for encrypted long text data, use like::
107
107
108 value = Column("encrypted_value", EncryptedValue(), nullable=False)
108 value = Column("encrypted_value", EncryptedValue(), nullable=False)
109
109
110 This column is intelligent so if value is in unencrypted form it return
110 This column is intelligent so if value is in unencrypted form it return
111 unencrypted form, but on save it always encrypts
111 unencrypted form, but on save it always encrypts
112 """
112 """
113 impl = Text
113 impl = Text
114
114
115 def process_bind_param(self, value, dialect):
115 def process_bind_param(self, value, dialect):
116 if not value:
116 if not value:
117 return value
117 return value
118 if value.startswith('enc$aes$'):
118 if value.startswith('enc$aes$'):
119 # protect against double encrypting if someone manually starts
119 # protect against double encrypting if someone manually starts
120 # doing
120 # doing
121 raise ValueError('value needs to be in unencrypted format, ie. '
121 raise ValueError('value needs to be in unencrypted format, ie. '
122 'not starting with enc$aes$')
122 'not starting with enc$aes$')
123 return 'enc$aes$%s' % AESCipher(ENCRYPTION_KEY).encrypt(value)
123 return 'enc$aes$%s' % AESCipher(ENCRYPTION_KEY).encrypt(value)
124
124
125 def process_result_value(self, value, dialect):
125 def process_result_value(self, value, dialect):
126 if not value:
126 if not value:
127 return value
127 return value
128
128
129 parts = value.split('$', 3)
129 parts = value.split('$', 3)
130 if not len(parts) == 3:
130 if not len(parts) == 3:
131 # probably not encrypted values
131 # probably not encrypted values
132 return value
132 return value
133 else:
133 else:
134 if parts[0] != 'enc':
134 if parts[0] != 'enc':
135 # parts ok but without our header ?
135 # parts ok but without our header ?
136 return value
136 return value
137
137
138 # at that stage we know it's our encryption
138 # at that stage we know it's our encryption
139 decrypted_data = AESCipher(ENCRYPTION_KEY).decrypt(parts[2])
139 decrypted_data = AESCipher(ENCRYPTION_KEY).decrypt(parts[2])
140 return decrypted_data
140 return decrypted_data
141
141
142
142
143 class BaseModel(object):
143 class BaseModel(object):
144 """
144 """
145 Base Model for all classes
145 Base Model for all classes
146 """
146 """
147
147
148 @classmethod
148 @classmethod
149 def _get_keys(cls):
149 def _get_keys(cls):
150 """return column names for this model """
150 """return column names for this model """
151 return class_mapper(cls).c.keys()
151 return class_mapper(cls).c.keys()
152
152
153 def get_dict(self):
153 def get_dict(self):
154 """
154 """
155 return dict with keys and values corresponding
155 return dict with keys and values corresponding
156 to this model data """
156 to this model data """
157
157
158 d = {}
158 d = {}
159 for k in self._get_keys():
159 for k in self._get_keys():
160 d[k] = getattr(self, k)
160 d[k] = getattr(self, k)
161
161
162 # also use __json__() if present to get additional fields
162 # also use __json__() if present to get additional fields
163 _json_attr = getattr(self, '__json__', None)
163 _json_attr = getattr(self, '__json__', None)
164 if _json_attr:
164 if _json_attr:
165 # update with attributes from __json__
165 # update with attributes from __json__
166 if callable(_json_attr):
166 if callable(_json_attr):
167 _json_attr = _json_attr()
167 _json_attr = _json_attr()
168 for k, val in _json_attr.iteritems():
168 for k, val in _json_attr.iteritems():
169 d[k] = val
169 d[k] = val
170 return d
170 return d
171
171
172 def get_appstruct(self):
172 def get_appstruct(self):
173 """return list with keys and values tuples corresponding
173 """return list with keys and values tuples corresponding
174 to this model data """
174 to this model data """
175
175
176 l = []
176 l = []
177 for k in self._get_keys():
177 for k in self._get_keys():
178 l.append((k, getattr(self, k),))
178 l.append((k, getattr(self, k),))
179 return l
179 return l
180
180
181 def populate_obj(self, populate_dict):
181 def populate_obj(self, populate_dict):
182 """populate model with data from given populate_dict"""
182 """populate model with data from given populate_dict"""
183
183
184 for k in self._get_keys():
184 for k in self._get_keys():
185 if k in populate_dict:
185 if k in populate_dict:
186 setattr(self, k, populate_dict[k])
186 setattr(self, k, populate_dict[k])
187
187
188 @classmethod
188 @classmethod
189 def query(cls):
189 def query(cls):
190 return Session().query(cls)
190 return Session().query(cls)
191
191
192 @classmethod
192 @classmethod
193 def get(cls, id_):
193 def get(cls, id_):
194 if id_:
194 if id_:
195 return cls.query().get(id_)
195 return cls.query().get(id_)
196
196
197 @classmethod
197 @classmethod
198 def get_or_404(cls, id_):
198 def get_or_404(cls, id_):
199 try:
199 try:
200 id_ = int(id_)
200 id_ = int(id_)
201 except (TypeError, ValueError):
201 except (TypeError, ValueError):
202 raise HTTPNotFound
202 raise HTTPNotFound
203
203
204 res = cls.query().get(id_)
204 res = cls.query().get(id_)
205 if not res:
205 if not res:
206 raise HTTPNotFound
206 raise HTTPNotFound
207 return res
207 return res
208
208
209 @classmethod
209 @classmethod
210 def getAll(cls):
210 def getAll(cls):
211 # deprecated and left for backward compatibility
211 # deprecated and left for backward compatibility
212 return cls.get_all()
212 return cls.get_all()
213
213
214 @classmethod
214 @classmethod
215 def get_all(cls):
215 def get_all(cls):
216 return cls.query().all()
216 return cls.query().all()
217
217
218 @classmethod
218 @classmethod
219 def delete(cls, id_):
219 def delete(cls, id_):
220 obj = cls.query().get(id_)
220 obj = cls.query().get(id_)
221 Session().delete(obj)
221 Session().delete(obj)
222
222
223 @classmethod
224 def identity_cache(cls, session, attr_name, value):
225 exist_in_session = []
226 for (item_cls, pkey), instance in session.identity_map.items():
227 if cls == item_cls and getattr(instance, attr_name) == value:
228 exist_in_session.append(instance)
229 if exist_in_session:
230 if len(exist_in_session) == 1:
231 return exist_in_session[0]
232 log.exception(
233 'multiple objects with attr %s and '
234 'value %s found with same name: %r',
235 attr_name, value, exist_in_session)
236
223 def __repr__(self):
237 def __repr__(self):
224 if hasattr(self, '__unicode__'):
238 if hasattr(self, '__unicode__'):
225 # python repr needs to return str
239 # python repr needs to return str
226 try:
240 try:
227 return safe_str(self.__unicode__())
241 return safe_str(self.__unicode__())
228 except UnicodeDecodeError:
242 except UnicodeDecodeError:
229 pass
243 pass
230 return '<DB:%s>' % (self.__class__.__name__)
244 return '<DB:%s>' % (self.__class__.__name__)
231
245
232
246
233 class RhodeCodeSetting(Base, BaseModel):
247 class RhodeCodeSetting(Base, BaseModel):
234 __tablename__ = 'rhodecode_settings'
248 __tablename__ = 'rhodecode_settings'
235 __table_args__ = (
249 __table_args__ = (
236 UniqueConstraint('app_settings_name'),
250 UniqueConstraint('app_settings_name'),
237 {'extend_existing': True, 'mysql_engine': 'InnoDB',
251 {'extend_existing': True, 'mysql_engine': 'InnoDB',
238 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
252 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
239 )
253 )
240
254
241 SETTINGS_TYPES = {
255 SETTINGS_TYPES = {
242 'str': safe_str,
256 'str': safe_str,
243 'int': safe_int,
257 'int': safe_int,
244 'unicode': safe_unicode,
258 'unicode': safe_unicode,
245 'bool': str2bool,
259 'bool': str2bool,
246 'list': functools.partial(aslist, sep=',')
260 'list': functools.partial(aslist, sep=',')
247 }
261 }
248 DEFAULT_UPDATE_URL = 'https://rhodecode.com/api/v1/info/versions'
262 DEFAULT_UPDATE_URL = 'https://rhodecode.com/api/v1/info/versions'
249 GLOBAL_CONF_KEY = 'app_settings'
263 GLOBAL_CONF_KEY = 'app_settings'
250
264
251 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
265 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
252 app_settings_name = Column("app_settings_name", String(255), nullable=True, unique=None, default=None)
266 app_settings_name = Column("app_settings_name", String(255), nullable=True, unique=None, default=None)
253 _app_settings_value = Column("app_settings_value", String(4096), nullable=True, unique=None, default=None)
267 _app_settings_value = Column("app_settings_value", String(4096), nullable=True, unique=None, default=None)
254 _app_settings_type = Column("app_settings_type", String(255), nullable=True, unique=None, default=None)
268 _app_settings_type = Column("app_settings_type", String(255), nullable=True, unique=None, default=None)
255
269
256 def __init__(self, key='', val='', type='unicode'):
270 def __init__(self, key='', val='', type='unicode'):
257 self.app_settings_name = key
271 self.app_settings_name = key
258 self.app_settings_type = type
272 self.app_settings_type = type
259 self.app_settings_value = val
273 self.app_settings_value = val
260
274
261 @validates('_app_settings_value')
275 @validates('_app_settings_value')
262 def validate_settings_value(self, key, val):
276 def validate_settings_value(self, key, val):
263 assert type(val) == unicode
277 assert type(val) == unicode
264 return val
278 return val
265
279
266 @hybrid_property
280 @hybrid_property
267 def app_settings_value(self):
281 def app_settings_value(self):
268 v = self._app_settings_value
282 v = self._app_settings_value
269 _type = self.app_settings_type
283 _type = self.app_settings_type
270 if _type:
284 if _type:
271 _type = self.app_settings_type.split('.')[0]
285 _type = self.app_settings_type.split('.')[0]
272 # decode the encrypted value
286 # decode the encrypted value
273 if 'encrypted' in self.app_settings_type:
287 if 'encrypted' in self.app_settings_type:
274 cipher = EncryptedTextValue()
288 cipher = EncryptedTextValue()
275 v = safe_unicode(cipher.process_result_value(v, None))
289 v = safe_unicode(cipher.process_result_value(v, None))
276
290
277 converter = self.SETTINGS_TYPES.get(_type) or \
291 converter = self.SETTINGS_TYPES.get(_type) or \
278 self.SETTINGS_TYPES['unicode']
292 self.SETTINGS_TYPES['unicode']
279 return converter(v)
293 return converter(v)
280
294
281 @app_settings_value.setter
295 @app_settings_value.setter
282 def app_settings_value(self, val):
296 def app_settings_value(self, val):
283 """
297 """
284 Setter that will always make sure we use unicode in app_settings_value
298 Setter that will always make sure we use unicode in app_settings_value
285
299
286 :param val:
300 :param val:
287 """
301 """
288 val = safe_unicode(val)
302 val = safe_unicode(val)
289 # encode the encrypted value
303 # encode the encrypted value
290 if 'encrypted' in self.app_settings_type:
304 if 'encrypted' in self.app_settings_type:
291 cipher = EncryptedTextValue()
305 cipher = EncryptedTextValue()
292 val = safe_unicode(cipher.process_bind_param(val, None))
306 val = safe_unicode(cipher.process_bind_param(val, None))
293 self._app_settings_value = val
307 self._app_settings_value = val
294
308
295 @hybrid_property
309 @hybrid_property
296 def app_settings_type(self):
310 def app_settings_type(self):
297 return self._app_settings_type
311 return self._app_settings_type
298
312
299 @app_settings_type.setter
313 @app_settings_type.setter
300 def app_settings_type(self, val):
314 def app_settings_type(self, val):
301 if val.split('.')[0] not in self.SETTINGS_TYPES:
315 if val.split('.')[0] not in self.SETTINGS_TYPES:
302 raise Exception('type must be one of %s got %s'
316 raise Exception('type must be one of %s got %s'
303 % (self.SETTINGS_TYPES.keys(), val))
317 % (self.SETTINGS_TYPES.keys(), val))
304 self._app_settings_type = val
318 self._app_settings_type = val
305
319
306 def __unicode__(self):
320 def __unicode__(self):
307 return u"<%s('%s:%s[%s]')>" % (
321 return u"<%s('%s:%s[%s]')>" % (
308 self.__class__.__name__,
322 self.__class__.__name__,
309 self.app_settings_name, self.app_settings_value,
323 self.app_settings_name, self.app_settings_value,
310 self.app_settings_type
324 self.app_settings_type
311 )
325 )
312
326
313
327
314 class RhodeCodeUi(Base, BaseModel):
328 class RhodeCodeUi(Base, BaseModel):
315 __tablename__ = 'rhodecode_ui'
329 __tablename__ = 'rhodecode_ui'
316 __table_args__ = (
330 __table_args__ = (
317 UniqueConstraint('ui_key'),
331 UniqueConstraint('ui_key'),
318 {'extend_existing': True, 'mysql_engine': 'InnoDB',
332 {'extend_existing': True, 'mysql_engine': 'InnoDB',
319 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
333 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
320 )
334 )
321
335
322 HOOK_REPO_SIZE = 'changegroup.repo_size'
336 HOOK_REPO_SIZE = 'changegroup.repo_size'
323 # HG
337 # HG
324 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
338 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
325 HOOK_PULL = 'outgoing.pull_logger'
339 HOOK_PULL = 'outgoing.pull_logger'
326 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
340 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
327 HOOK_PUSH = 'changegroup.push_logger'
341 HOOK_PUSH = 'changegroup.push_logger'
328
342
329 # TODO: johbo: Unify way how hooks are configured for git and hg,
343 # TODO: johbo: Unify way how hooks are configured for git and hg,
330 # git part is currently hardcoded.
344 # git part is currently hardcoded.
331
345
332 # SVN PATTERNS
346 # SVN PATTERNS
333 SVN_BRANCH_ID = 'vcs_svn_branch'
347 SVN_BRANCH_ID = 'vcs_svn_branch'
334 SVN_TAG_ID = 'vcs_svn_tag'
348 SVN_TAG_ID = 'vcs_svn_tag'
335
349
336 ui_id = Column(
350 ui_id = Column(
337 "ui_id", Integer(), nullable=False, unique=True, default=None,
351 "ui_id", Integer(), nullable=False, unique=True, default=None,
338 primary_key=True)
352 primary_key=True)
339 ui_section = Column(
353 ui_section = Column(
340 "ui_section", String(255), nullable=True, unique=None, default=None)
354 "ui_section", String(255), nullable=True, unique=None, default=None)
341 ui_key = Column(
355 ui_key = Column(
342 "ui_key", String(255), nullable=True, unique=None, default=None)
356 "ui_key", String(255), nullable=True, unique=None, default=None)
343 ui_value = Column(
357 ui_value = Column(
344 "ui_value", String(255), nullable=True, unique=None, default=None)
358 "ui_value", String(255), nullable=True, unique=None, default=None)
345 ui_active = Column(
359 ui_active = Column(
346 "ui_active", Boolean(), nullable=True, unique=None, default=True)
360 "ui_active", Boolean(), nullable=True, unique=None, default=True)
347
361
348 def __repr__(self):
362 def __repr__(self):
349 return '<%s[%s]%s=>%s]>' % (self.__class__.__name__, self.ui_section,
363 return '<%s[%s]%s=>%s]>' % (self.__class__.__name__, self.ui_section,
350 self.ui_key, self.ui_value)
364 self.ui_key, self.ui_value)
351
365
352
366
353 class RepoRhodeCodeSetting(Base, BaseModel):
367 class RepoRhodeCodeSetting(Base, BaseModel):
354 __tablename__ = 'repo_rhodecode_settings'
368 __tablename__ = 'repo_rhodecode_settings'
355 __table_args__ = (
369 __table_args__ = (
356 UniqueConstraint(
370 UniqueConstraint(
357 'app_settings_name', 'repository_id',
371 'app_settings_name', 'repository_id',
358 name='uq_repo_rhodecode_setting_name_repo_id'),
372 name='uq_repo_rhodecode_setting_name_repo_id'),
359 {'extend_existing': True, 'mysql_engine': 'InnoDB',
373 {'extend_existing': True, 'mysql_engine': 'InnoDB',
360 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
374 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
361 )
375 )
362
376
363 repository_id = Column(
377 repository_id = Column(
364 "repository_id", Integer(), ForeignKey('repositories.repo_id'),
378 "repository_id", Integer(), ForeignKey('repositories.repo_id'),
365 nullable=False)
379 nullable=False)
366 app_settings_id = Column(
380 app_settings_id = Column(
367 "app_settings_id", Integer(), nullable=False, unique=True,
381 "app_settings_id", Integer(), nullable=False, unique=True,
368 default=None, primary_key=True)
382 default=None, primary_key=True)
369 app_settings_name = Column(
383 app_settings_name = Column(
370 "app_settings_name", String(255), nullable=True, unique=None,
384 "app_settings_name", String(255), nullable=True, unique=None,
371 default=None)
385 default=None)
372 _app_settings_value = Column(
386 _app_settings_value = Column(
373 "app_settings_value", String(4096), nullable=True, unique=None,
387 "app_settings_value", String(4096), nullable=True, unique=None,
374 default=None)
388 default=None)
375 _app_settings_type = Column(
389 _app_settings_type = Column(
376 "app_settings_type", String(255), nullable=True, unique=None,
390 "app_settings_type", String(255), nullable=True, unique=None,
377 default=None)
391 default=None)
378
392
379 repository = relationship('Repository')
393 repository = relationship('Repository')
380
394
381 def __init__(self, repository_id, key='', val='', type='unicode'):
395 def __init__(self, repository_id, key='', val='', type='unicode'):
382 self.repository_id = repository_id
396 self.repository_id = repository_id
383 self.app_settings_name = key
397 self.app_settings_name = key
384 self.app_settings_type = type
398 self.app_settings_type = type
385 self.app_settings_value = val
399 self.app_settings_value = val
386
400
387 @validates('_app_settings_value')
401 @validates('_app_settings_value')
388 def validate_settings_value(self, key, val):
402 def validate_settings_value(self, key, val):
389 assert type(val) == unicode
403 assert type(val) == unicode
390 return val
404 return val
391
405
392 @hybrid_property
406 @hybrid_property
393 def app_settings_value(self):
407 def app_settings_value(self):
394 v = self._app_settings_value
408 v = self._app_settings_value
395 type_ = self.app_settings_type
409 type_ = self.app_settings_type
396 SETTINGS_TYPES = RhodeCodeSetting.SETTINGS_TYPES
410 SETTINGS_TYPES = RhodeCodeSetting.SETTINGS_TYPES
397 converter = SETTINGS_TYPES.get(type_) or SETTINGS_TYPES['unicode']
411 converter = SETTINGS_TYPES.get(type_) or SETTINGS_TYPES['unicode']
398 return converter(v)
412 return converter(v)
399
413
400 @app_settings_value.setter
414 @app_settings_value.setter
401 def app_settings_value(self, val):
415 def app_settings_value(self, val):
402 """
416 """
403 Setter that will always make sure we use unicode in app_settings_value
417 Setter that will always make sure we use unicode in app_settings_value
404
418
405 :param val:
419 :param val:
406 """
420 """
407 self._app_settings_value = safe_unicode(val)
421 self._app_settings_value = safe_unicode(val)
408
422
409 @hybrid_property
423 @hybrid_property
410 def app_settings_type(self):
424 def app_settings_type(self):
411 return self._app_settings_type
425 return self._app_settings_type
412
426
413 @app_settings_type.setter
427 @app_settings_type.setter
414 def app_settings_type(self, val):
428 def app_settings_type(self, val):
415 SETTINGS_TYPES = RhodeCodeSetting.SETTINGS_TYPES
429 SETTINGS_TYPES = RhodeCodeSetting.SETTINGS_TYPES
416 if val not in SETTINGS_TYPES:
430 if val not in SETTINGS_TYPES:
417 raise Exception('type must be one of %s got %s'
431 raise Exception('type must be one of %s got %s'
418 % (SETTINGS_TYPES.keys(), val))
432 % (SETTINGS_TYPES.keys(), val))
419 self._app_settings_type = val
433 self._app_settings_type = val
420
434
421 def __unicode__(self):
435 def __unicode__(self):
422 return u"<%s('%s:%s:%s[%s]')>" % (
436 return u"<%s('%s:%s:%s[%s]')>" % (
423 self.__class__.__name__, self.repository.repo_name,
437 self.__class__.__name__, self.repository.repo_name,
424 self.app_settings_name, self.app_settings_value,
438 self.app_settings_name, self.app_settings_value,
425 self.app_settings_type
439 self.app_settings_type
426 )
440 )
427
441
428
442
429 class RepoRhodeCodeUi(Base, BaseModel):
443 class RepoRhodeCodeUi(Base, BaseModel):
430 __tablename__ = 'repo_rhodecode_ui'
444 __tablename__ = 'repo_rhodecode_ui'
431 __table_args__ = (
445 __table_args__ = (
432 UniqueConstraint(
446 UniqueConstraint(
433 'repository_id', 'ui_section', 'ui_key',
447 'repository_id', 'ui_section', 'ui_key',
434 name='uq_repo_rhodecode_ui_repository_id_section_key'),
448 name='uq_repo_rhodecode_ui_repository_id_section_key'),
435 {'extend_existing': True, 'mysql_engine': 'InnoDB',
449 {'extend_existing': True, 'mysql_engine': 'InnoDB',
436 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
450 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
437 )
451 )
438
452
439 repository_id = Column(
453 repository_id = Column(
440 "repository_id", Integer(), ForeignKey('repositories.repo_id'),
454 "repository_id", Integer(), ForeignKey('repositories.repo_id'),
441 nullable=False)
455 nullable=False)
442 ui_id = Column(
456 ui_id = Column(
443 "ui_id", Integer(), nullable=False, unique=True, default=None,
457 "ui_id", Integer(), nullable=False, unique=True, default=None,
444 primary_key=True)
458 primary_key=True)
445 ui_section = Column(
459 ui_section = Column(
446 "ui_section", String(255), nullable=True, unique=None, default=None)
460 "ui_section", String(255), nullable=True, unique=None, default=None)
447 ui_key = Column(
461 ui_key = Column(
448 "ui_key", String(255), nullable=True, unique=None, default=None)
462 "ui_key", String(255), nullable=True, unique=None, default=None)
449 ui_value = Column(
463 ui_value = Column(
450 "ui_value", String(255), nullable=True, unique=None, default=None)
464 "ui_value", String(255), nullable=True, unique=None, default=None)
451 ui_active = Column(
465 ui_active = Column(
452 "ui_active", Boolean(), nullable=True, unique=None, default=True)
466 "ui_active", Boolean(), nullable=True, unique=None, default=True)
453
467
454 repository = relationship('Repository')
468 repository = relationship('Repository')
455
469
456 def __repr__(self):
470 def __repr__(self):
457 return '<%s[%s:%s]%s=>%s]>' % (
471 return '<%s[%s:%s]%s=>%s]>' % (
458 self.__class__.__name__, self.repository.repo_name,
472 self.__class__.__name__, self.repository.repo_name,
459 self.ui_section, self.ui_key, self.ui_value)
473 self.ui_section, self.ui_key, self.ui_value)
460
474
461
475
462 class User(Base, BaseModel):
476 class User(Base, BaseModel):
463 __tablename__ = 'users'
477 __tablename__ = 'users'
464 __table_args__ = (
478 __table_args__ = (
465 UniqueConstraint('username'), UniqueConstraint('email'),
479 UniqueConstraint('username'), UniqueConstraint('email'),
466 Index('u_username_idx', 'username'),
480 Index('u_username_idx', 'username'),
467 Index('u_email_idx', 'email'),
481 Index('u_email_idx', 'email'),
468 {'extend_existing': True, 'mysql_engine': 'InnoDB',
482 {'extend_existing': True, 'mysql_engine': 'InnoDB',
469 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
483 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
470 )
484 )
471 DEFAULT_USER = 'default'
485 DEFAULT_USER = 'default'
472 DEFAULT_USER_EMAIL = 'anonymous@rhodecode.org'
486 DEFAULT_USER_EMAIL = 'anonymous@rhodecode.org'
473 DEFAULT_GRAVATAR_URL = 'https://secure.gravatar.com/avatar/{md5email}?d=identicon&s={size}'
487 DEFAULT_GRAVATAR_URL = 'https://secure.gravatar.com/avatar/{md5email}?d=identicon&s={size}'
474
488
475 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
489 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
476 username = Column("username", String(255), nullable=True, unique=None, default=None)
490 username = Column("username", String(255), nullable=True, unique=None, default=None)
477 password = Column("password", String(255), nullable=True, unique=None, default=None)
491 password = Column("password", String(255), nullable=True, unique=None, default=None)
478 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
492 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
479 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
493 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
480 name = Column("firstname", String(255), nullable=True, unique=None, default=None)
494 name = Column("firstname", String(255), nullable=True, unique=None, default=None)
481 lastname = Column("lastname", String(255), nullable=True, unique=None, default=None)
495 lastname = Column("lastname", String(255), nullable=True, unique=None, default=None)
482 _email = Column("email", String(255), nullable=True, unique=None, default=None)
496 _email = Column("email", String(255), nullable=True, unique=None, default=None)
483 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
497 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
484 extern_type = Column("extern_type", String(255), nullable=True, unique=None, default=None)
498 extern_type = Column("extern_type", String(255), nullable=True, unique=None, default=None)
485 extern_name = Column("extern_name", String(255), nullable=True, unique=None, default=None)
499 extern_name = Column("extern_name", String(255), nullable=True, unique=None, default=None)
486 api_key = Column("api_key", String(255), nullable=True, unique=None, default=None)
500 api_key = Column("api_key", String(255), nullable=True, unique=None, default=None)
487 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
501 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
488 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
502 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
489 _user_data = Column("user_data", LargeBinary(), nullable=True) # JSON data
503 _user_data = Column("user_data", LargeBinary(), nullable=True) # JSON data
490
504
491 user_log = relationship('UserLog')
505 user_log = relationship('UserLog')
492 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
506 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
493
507
494 repositories = relationship('Repository')
508 repositories = relationship('Repository')
495 repository_groups = relationship('RepoGroup')
509 repository_groups = relationship('RepoGroup')
496 user_groups = relationship('UserGroup')
510 user_groups = relationship('UserGroup')
497
511
498 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
512 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
499 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
513 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
500
514
501 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
515 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
502 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
516 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
503 user_group_to_perm = relationship('UserUserGroupToPerm', primaryjoin='UserUserGroupToPerm.user_id==User.user_id', cascade='all')
517 user_group_to_perm = relationship('UserUserGroupToPerm', primaryjoin='UserUserGroupToPerm.user_id==User.user_id', cascade='all')
504
518
505 group_member = relationship('UserGroupMember', cascade='all')
519 group_member = relationship('UserGroupMember', cascade='all')
506
520
507 notifications = relationship('UserNotification', cascade='all')
521 notifications = relationship('UserNotification', cascade='all')
508 # notifications assigned to this user
522 # notifications assigned to this user
509 user_created_notifications = relationship('Notification', cascade='all')
523 user_created_notifications = relationship('Notification', cascade='all')
510 # comments created by this user
524 # comments created by this user
511 user_comments = relationship('ChangesetComment', cascade='all')
525 user_comments = relationship('ChangesetComment', cascade='all')
512 # user profile extra info
526 # user profile extra info
513 user_emails = relationship('UserEmailMap', cascade='all')
527 user_emails = relationship('UserEmailMap', cascade='all')
514 user_ip_map = relationship('UserIpMap', cascade='all')
528 user_ip_map = relationship('UserIpMap', cascade='all')
515 user_auth_tokens = relationship('UserApiKeys', cascade='all')
529 user_auth_tokens = relationship('UserApiKeys', cascade='all')
516 # gists
530 # gists
517 user_gists = relationship('Gist', cascade='all')
531 user_gists = relationship('Gist', cascade='all')
518 # user pull requests
532 # user pull requests
519 user_pull_requests = relationship('PullRequest', cascade='all')
533 user_pull_requests = relationship('PullRequest', cascade='all')
520 # external identities
534 # external identities
521 extenal_identities = relationship(
535 extenal_identities = relationship(
522 'ExternalIdentity',
536 'ExternalIdentity',
523 primaryjoin="User.user_id==ExternalIdentity.local_user_id",
537 primaryjoin="User.user_id==ExternalIdentity.local_user_id",
524 cascade='all')
538 cascade='all')
525
539
526 def __unicode__(self):
540 def __unicode__(self):
527 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
541 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
528 self.user_id, self.username)
542 self.user_id, self.username)
529
543
530 @hybrid_property
544 @hybrid_property
531 def email(self):
545 def email(self):
532 return self._email
546 return self._email
533
547
534 @email.setter
548 @email.setter
535 def email(self, val):
549 def email(self, val):
536 self._email = val.lower() if val else None
550 self._email = val.lower() if val else None
537
551
538 @property
552 @property
539 def firstname(self):
553 def firstname(self):
540 # alias for future
554 # alias for future
541 return self.name
555 return self.name
542
556
543 @property
557 @property
544 def emails(self):
558 def emails(self):
545 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
559 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
546 return [self.email] + [x.email for x in other]
560 return [self.email] + [x.email for x in other]
547
561
548 @property
562 @property
549 def auth_tokens(self):
563 def auth_tokens(self):
550 return [self.api_key] + [x.api_key for x in self.extra_auth_tokens]
564 return [self.api_key] + [x.api_key for x in self.extra_auth_tokens]
551
565
552 @property
566 @property
553 def extra_auth_tokens(self):
567 def extra_auth_tokens(self):
554 return UserApiKeys.query().filter(UserApiKeys.user == self).all()
568 return UserApiKeys.query().filter(UserApiKeys.user == self).all()
555
569
556 @property
570 @property
557 def feed_token(self):
571 def feed_token(self):
558 feed_tokens = UserApiKeys.query()\
572 feed_tokens = UserApiKeys.query()\
559 .filter(UserApiKeys.user == self)\
573 .filter(UserApiKeys.user == self)\
560 .filter(UserApiKeys.role == UserApiKeys.ROLE_FEED)\
574 .filter(UserApiKeys.role == UserApiKeys.ROLE_FEED)\
561 .all()
575 .all()
562 if feed_tokens:
576 if feed_tokens:
563 return feed_tokens[0].api_key
577 return feed_tokens[0].api_key
564 else:
578 else:
565 # use the main token so we don't end up with nothing...
579 # use the main token so we don't end up with nothing...
566 return self.api_key
580 return self.api_key
567
581
568 @classmethod
582 @classmethod
569 def extra_valid_auth_tokens(cls, user, role=None):
583 def extra_valid_auth_tokens(cls, user, role=None):
570 tokens = UserApiKeys.query().filter(UserApiKeys.user == user)\
584 tokens = UserApiKeys.query().filter(UserApiKeys.user == user)\
571 .filter(or_(UserApiKeys.expires == -1,
585 .filter(or_(UserApiKeys.expires == -1,
572 UserApiKeys.expires >= time.time()))
586 UserApiKeys.expires >= time.time()))
573 if role:
587 if role:
574 tokens = tokens.filter(or_(UserApiKeys.role == role,
588 tokens = tokens.filter(or_(UserApiKeys.role == role,
575 UserApiKeys.role == UserApiKeys.ROLE_ALL))
589 UserApiKeys.role == UserApiKeys.ROLE_ALL))
576 return tokens.all()
590 return tokens.all()
577
591
578 @property
592 @property
579 def ip_addresses(self):
593 def ip_addresses(self):
580 ret = UserIpMap.query().filter(UserIpMap.user == self).all()
594 ret = UserIpMap.query().filter(UserIpMap.user == self).all()
581 return [x.ip_addr for x in ret]
595 return [x.ip_addr for x in ret]
582
596
583 @property
597 @property
584 def username_and_name(self):
598 def username_and_name(self):
585 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
599 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
586
600
587 @property
601 @property
588 def username_or_name_or_email(self):
602 def username_or_name_or_email(self):
589 full_name = self.full_name if self.full_name is not ' ' else None
603 full_name = self.full_name if self.full_name is not ' ' else None
590 return self.username or full_name or self.email
604 return self.username or full_name or self.email
591
605
592 @property
606 @property
593 def full_name(self):
607 def full_name(self):
594 return '%s %s' % (self.firstname, self.lastname)
608 return '%s %s' % (self.firstname, self.lastname)
595
609
596 @property
610 @property
597 def full_name_or_username(self):
611 def full_name_or_username(self):
598 return ('%s %s' % (self.firstname, self.lastname)
612 return ('%s %s' % (self.firstname, self.lastname)
599 if (self.firstname and self.lastname) else self.username)
613 if (self.firstname and self.lastname) else self.username)
600
614
601 @property
615 @property
602 def full_contact(self):
616 def full_contact(self):
603 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
617 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
604
618
605 @property
619 @property
606 def short_contact(self):
620 def short_contact(self):
607 return '%s %s' % (self.firstname, self.lastname)
621 return '%s %s' % (self.firstname, self.lastname)
608
622
609 @property
623 @property
610 def is_admin(self):
624 def is_admin(self):
611 return self.admin
625 return self.admin
612
626
613 @property
627 @property
614 def AuthUser(self):
628 def AuthUser(self):
615 """
629 """
616 Returns instance of AuthUser for this user
630 Returns instance of AuthUser for this user
617 """
631 """
618 from rhodecode.lib.auth import AuthUser
632 from rhodecode.lib.auth import AuthUser
619 return AuthUser(user_id=self.user_id, api_key=self.api_key,
633 return AuthUser(user_id=self.user_id, api_key=self.api_key,
620 username=self.username)
634 username=self.username)
621
635
622 @hybrid_property
636 @hybrid_property
623 def user_data(self):
637 def user_data(self):
624 if not self._user_data:
638 if not self._user_data:
625 return {}
639 return {}
626
640
627 try:
641 try:
628 return json.loads(self._user_data)
642 return json.loads(self._user_data)
629 except TypeError:
643 except TypeError:
630 return {}
644 return {}
631
645
632 @user_data.setter
646 @user_data.setter
633 def user_data(self, val):
647 def user_data(self, val):
634 if not isinstance(val, dict):
648 if not isinstance(val, dict):
635 raise Exception('user_data must be dict, got %s' % type(val))
649 raise Exception('user_data must be dict, got %s' % type(val))
636 try:
650 try:
637 self._user_data = json.dumps(val)
651 self._user_data = json.dumps(val)
638 except Exception:
652 except Exception:
639 log.error(traceback.format_exc())
653 log.error(traceback.format_exc())
640
654
641 @classmethod
655 @classmethod
642 def get_by_username(cls, username, case_insensitive=False, cache=False):
656 def get_by_username(cls, username, case_insensitive=False,
657 cache=False, identity_cache=False):
658 session = Session()
659
643 if case_insensitive:
660 if case_insensitive:
644 q = cls.query().filter(func.lower(cls.username) == func.lower(username))
661 q = cls.query().filter(
662 func.lower(cls.username) == func.lower(username))
645 else:
663 else:
646 q = cls.query().filter(cls.username == username)
664 q = cls.query().filter(cls.username == username)
647
665
648 if cache:
666 if cache:
649 q = q.options(FromCache(
667 if identity_cache:
650 "sql_cache_short",
668 val = cls.identity_cache(session, 'username', username)
651 "get_user_%s" % _hash_key(username)))
669 if val:
670 return val
671 else:
672 q = q.options(
673 FromCache("sql_cache_short",
674 "get_user_by_name_%s" % _hash_key(username)))
675
652 return q.scalar()
676 return q.scalar()
653
677
654 @classmethod
678 @classmethod
655 def get_by_auth_token(cls, auth_token, cache=False, fallback=True):
679 def get_by_auth_token(cls, auth_token, cache=False, fallback=True):
656 q = cls.query().filter(cls.api_key == auth_token)
680 q = cls.query().filter(cls.api_key == auth_token)
657
681
658 if cache:
682 if cache:
659 q = q.options(FromCache("sql_cache_short",
683 q = q.options(FromCache("sql_cache_short",
660 "get_auth_token_%s" % auth_token))
684 "get_auth_token_%s" % auth_token))
661 res = q.scalar()
685 res = q.scalar()
662
686
663 if fallback and not res:
687 if fallback and not res:
664 #fallback to additional keys
688 #fallback to additional keys
665 _res = UserApiKeys.query()\
689 _res = UserApiKeys.query()\
666 .filter(UserApiKeys.api_key == auth_token)\
690 .filter(UserApiKeys.api_key == auth_token)\
667 .filter(or_(UserApiKeys.expires == -1,
691 .filter(or_(UserApiKeys.expires == -1,
668 UserApiKeys.expires >= time.time()))\
692 UserApiKeys.expires >= time.time()))\
669 .first()
693 .first()
670 if _res:
694 if _res:
671 res = _res.user
695 res = _res.user
672 return res
696 return res
673
697
674 @classmethod
698 @classmethod
675 def get_by_email(cls, email, case_insensitive=False, cache=False):
699 def get_by_email(cls, email, case_insensitive=False, cache=False):
676
700
677 if case_insensitive:
701 if case_insensitive:
678 q = cls.query().filter(func.lower(cls.email) == func.lower(email))
702 q = cls.query().filter(func.lower(cls.email) == func.lower(email))
679
703
680 else:
704 else:
681 q = cls.query().filter(cls.email == email)
705 q = cls.query().filter(cls.email == email)
682
706
683 if cache:
707 if cache:
684 q = q.options(FromCache("sql_cache_short",
708 q = q.options(FromCache("sql_cache_short",
685 "get_email_key_%s" % email))
709 "get_email_key_%s" % email))
686
710
687 ret = q.scalar()
711 ret = q.scalar()
688 if ret is None:
712 if ret is None:
689 q = UserEmailMap.query()
713 q = UserEmailMap.query()
690 # try fetching in alternate email map
714 # try fetching in alternate email map
691 if case_insensitive:
715 if case_insensitive:
692 q = q.filter(func.lower(UserEmailMap.email) == func.lower(email))
716 q = q.filter(func.lower(UserEmailMap.email) == func.lower(email))
693 else:
717 else:
694 q = q.filter(UserEmailMap.email == email)
718 q = q.filter(UserEmailMap.email == email)
695 q = q.options(joinedload(UserEmailMap.user))
719 q = q.options(joinedload(UserEmailMap.user))
696 if cache:
720 if cache:
697 q = q.options(FromCache("sql_cache_short",
721 q = q.options(FromCache("sql_cache_short",
698 "get_email_map_key_%s" % email))
722 "get_email_map_key_%s" % email))
699 ret = getattr(q.scalar(), 'user', None)
723 ret = getattr(q.scalar(), 'user', None)
700
724
701 return ret
725 return ret
702
726
703 @classmethod
727 @classmethod
704 def get_from_cs_author(cls, author):
728 def get_from_cs_author(cls, author):
705 """
729 """
706 Tries to get User objects out of commit author string
730 Tries to get User objects out of commit author string
707
731
708 :param author:
732 :param author:
709 """
733 """
710 from rhodecode.lib.helpers import email, author_name
734 from rhodecode.lib.helpers import email, author_name
711 # Valid email in the attribute passed, see if they're in the system
735 # Valid email in the attribute passed, see if they're in the system
712 _email = email(author)
736 _email = email(author)
713 if _email:
737 if _email:
714 user = cls.get_by_email(_email, case_insensitive=True)
738 user = cls.get_by_email(_email, case_insensitive=True)
715 if user:
739 if user:
716 return user
740 return user
717 # Maybe we can match by username?
741 # Maybe we can match by username?
718 _author = author_name(author)
742 _author = author_name(author)
719 user = cls.get_by_username(_author, case_insensitive=True)
743 user = cls.get_by_username(_author, case_insensitive=True)
720 if user:
744 if user:
721 return user
745 return user
722
746
723 def update_userdata(self, **kwargs):
747 def update_userdata(self, **kwargs):
724 usr = self
748 usr = self
725 old = usr.user_data
749 old = usr.user_data
726 old.update(**kwargs)
750 old.update(**kwargs)
727 usr.user_data = old
751 usr.user_data = old
728 Session().add(usr)
752 Session().add(usr)
729 log.debug('updated userdata with ', kwargs)
753 log.debug('updated userdata with ', kwargs)
730
754
731 def update_lastlogin(self):
755 def update_lastlogin(self):
732 """Update user lastlogin"""
756 """Update user lastlogin"""
733 self.last_login = datetime.datetime.now()
757 self.last_login = datetime.datetime.now()
734 Session().add(self)
758 Session().add(self)
735 log.debug('updated user %s lastlogin', self.username)
759 log.debug('updated user %s lastlogin', self.username)
736
760
737 def update_lastactivity(self):
761 def update_lastactivity(self):
738 """Update user lastactivity"""
762 """Update user lastactivity"""
739 usr = self
763 usr = self
740 old = usr.user_data
764 old = usr.user_data
741 old.update({'last_activity': time.time()})
765 old.update({'last_activity': time.time()})
742 usr.user_data = old
766 usr.user_data = old
743 Session().add(usr)
767 Session().add(usr)
744 log.debug('updated user %s lastactivity', usr.username)
768 log.debug('updated user %s lastactivity', usr.username)
745
769
746 def update_password(self, new_password, change_api_key=False):
770 def update_password(self, new_password, change_api_key=False):
747 from rhodecode.lib.auth import get_crypt_password,generate_auth_token
771 from rhodecode.lib.auth import get_crypt_password,generate_auth_token
748
772
749 self.password = get_crypt_password(new_password)
773 self.password = get_crypt_password(new_password)
750 if change_api_key:
774 if change_api_key:
751 self.api_key = generate_auth_token(self.username)
775 self.api_key = generate_auth_token(self.username)
752 Session().add(self)
776 Session().add(self)
753
777
754 @classmethod
778 @classmethod
755 def get_first_admin(cls):
779 def get_first_admin(cls):
756 user = User.query().filter(User.admin == True).first()
780 user = User.query().filter(User.admin == True).first()
757 if user is None:
781 if user is None:
758 raise Exception('Missing administrative account!')
782 raise Exception('Missing administrative account!')
759 return user
783 return user
760
784
761 @classmethod
785 @classmethod
762 def get_all_super_admins(cls):
786 def get_all_super_admins(cls):
763 """
787 """
764 Returns all admin accounts sorted by username
788 Returns all admin accounts sorted by username
765 """
789 """
766 return User.query().filter(User.admin == true())\
790 return User.query().filter(User.admin == true())\
767 .order_by(User.username.asc()).all()
791 .order_by(User.username.asc()).all()
768
792
769 @classmethod
793 @classmethod
770 def get_default_user(cls, cache=False):
794 def get_default_user(cls, cache=False):
771 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
795 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
772 if user is None:
796 if user is None:
773 raise Exception('Missing default account!')
797 raise Exception('Missing default account!')
774 return user
798 return user
775
799
776 def _get_default_perms(self, user, suffix=''):
800 def _get_default_perms(self, user, suffix=''):
777 from rhodecode.model.permission import PermissionModel
801 from rhodecode.model.permission import PermissionModel
778 return PermissionModel().get_default_perms(user.user_perms, suffix)
802 return PermissionModel().get_default_perms(user.user_perms, suffix)
779
803
780 def get_default_perms(self, suffix=''):
804 def get_default_perms(self, suffix=''):
781 return self._get_default_perms(self, suffix)
805 return self._get_default_perms(self, suffix)
782
806
783 def get_api_data(self, include_secrets=False, details='full'):
807 def get_api_data(self, include_secrets=False, details='full'):
784 """
808 """
785 Common function for generating user related data for API
809 Common function for generating user related data for API
786
810
787 :param include_secrets: By default secrets in the API data will be replaced
811 :param include_secrets: By default secrets in the API data will be replaced
788 by a placeholder value to prevent exposing this data by accident. In case
812 by a placeholder value to prevent exposing this data by accident. In case
789 this data shall be exposed, set this flag to ``True``.
813 this data shall be exposed, set this flag to ``True``.
790
814
791 :param details: details can be 'basic|full' basic gives only a subset of
815 :param details: details can be 'basic|full' basic gives only a subset of
792 the available user information that includes user_id, name and emails.
816 the available user information that includes user_id, name and emails.
793 """
817 """
794 user = self
818 user = self
795 user_data = self.user_data
819 user_data = self.user_data
796 data = {
820 data = {
797 'user_id': user.user_id,
821 'user_id': user.user_id,
798 'username': user.username,
822 'username': user.username,
799 'firstname': user.name,
823 'firstname': user.name,
800 'lastname': user.lastname,
824 'lastname': user.lastname,
801 'email': user.email,
825 'email': user.email,
802 'emails': user.emails,
826 'emails': user.emails,
803 }
827 }
804 if details == 'basic':
828 if details == 'basic':
805 return data
829 return data
806
830
807 api_key_length = 40
831 api_key_length = 40
808 api_key_replacement = '*' * api_key_length
832 api_key_replacement = '*' * api_key_length
809
833
810 extras = {
834 extras = {
811 'api_key': api_key_replacement,
835 'api_key': api_key_replacement,
812 'api_keys': [api_key_replacement],
836 'api_keys': [api_key_replacement],
813 'active': user.active,
837 'active': user.active,
814 'admin': user.admin,
838 'admin': user.admin,
815 'extern_type': user.extern_type,
839 'extern_type': user.extern_type,
816 'extern_name': user.extern_name,
840 'extern_name': user.extern_name,
817 'last_login': user.last_login,
841 'last_login': user.last_login,
818 'ip_addresses': user.ip_addresses,
842 'ip_addresses': user.ip_addresses,
819 'language': user_data.get('language')
843 'language': user_data.get('language')
820 }
844 }
821 data.update(extras)
845 data.update(extras)
822
846
823 if include_secrets:
847 if include_secrets:
824 data['api_key'] = user.api_key
848 data['api_key'] = user.api_key
825 data['api_keys'] = user.auth_tokens
849 data['api_keys'] = user.auth_tokens
826 return data
850 return data
827
851
828 def __json__(self):
852 def __json__(self):
829 data = {
853 data = {
830 'full_name': self.full_name,
854 'full_name': self.full_name,
831 'full_name_or_username': self.full_name_or_username,
855 'full_name_or_username': self.full_name_or_username,
832 'short_contact': self.short_contact,
856 'short_contact': self.short_contact,
833 'full_contact': self.full_contact,
857 'full_contact': self.full_contact,
834 }
858 }
835 data.update(self.get_api_data())
859 data.update(self.get_api_data())
836 return data
860 return data
837
861
838
862
839 class UserApiKeys(Base, BaseModel):
863 class UserApiKeys(Base, BaseModel):
840 __tablename__ = 'user_api_keys'
864 __tablename__ = 'user_api_keys'
841 __table_args__ = (
865 __table_args__ = (
842 Index('uak_api_key_idx', 'api_key'),
866 Index('uak_api_key_idx', 'api_key'),
843 Index('uak_api_key_expires_idx', 'api_key', 'expires'),
867 Index('uak_api_key_expires_idx', 'api_key', 'expires'),
844 UniqueConstraint('api_key'),
868 UniqueConstraint('api_key'),
845 {'extend_existing': True, 'mysql_engine': 'InnoDB',
869 {'extend_existing': True, 'mysql_engine': 'InnoDB',
846 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
870 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
847 )
871 )
848 __mapper_args__ = {}
872 __mapper_args__ = {}
849
873
850 # ApiKey role
874 # ApiKey role
851 ROLE_ALL = 'token_role_all'
875 ROLE_ALL = 'token_role_all'
852 ROLE_HTTP = 'token_role_http'
876 ROLE_HTTP = 'token_role_http'
853 ROLE_VCS = 'token_role_vcs'
877 ROLE_VCS = 'token_role_vcs'
854 ROLE_API = 'token_role_api'
878 ROLE_API = 'token_role_api'
855 ROLE_FEED = 'token_role_feed'
879 ROLE_FEED = 'token_role_feed'
856 ROLES = [ROLE_ALL, ROLE_HTTP, ROLE_VCS, ROLE_API, ROLE_FEED]
880 ROLES = [ROLE_ALL, ROLE_HTTP, ROLE_VCS, ROLE_API, ROLE_FEED]
857
881
858 user_api_key_id = Column("user_api_key_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
882 user_api_key_id = Column("user_api_key_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
859 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
883 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
860 api_key = Column("api_key", String(255), nullable=False, unique=True)
884 api_key = Column("api_key", String(255), nullable=False, unique=True)
861 description = Column('description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
885 description = Column('description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
862 expires = Column('expires', Float(53), nullable=False)
886 expires = Column('expires', Float(53), nullable=False)
863 role = Column('role', String(255), nullable=True)
887 role = Column('role', String(255), nullable=True)
864 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
888 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
865
889
866 user = relationship('User', lazy='joined')
890 user = relationship('User', lazy='joined')
867
891
868 @classmethod
892 @classmethod
869 def _get_role_name(cls, role):
893 def _get_role_name(cls, role):
870 return {
894 return {
871 cls.ROLE_ALL: _('all'),
895 cls.ROLE_ALL: _('all'),
872 cls.ROLE_HTTP: _('http/web interface'),
896 cls.ROLE_HTTP: _('http/web interface'),
873 cls.ROLE_VCS: _('vcs (git/hg protocol)'),
897 cls.ROLE_VCS: _('vcs (git/hg protocol)'),
874 cls.ROLE_API: _('api calls'),
898 cls.ROLE_API: _('api calls'),
875 cls.ROLE_FEED: _('feed access'),
899 cls.ROLE_FEED: _('feed access'),
876 }.get(role, role)
900 }.get(role, role)
877
901
878 @property
902 @property
879 def expired(self):
903 def expired(self):
880 if self.expires == -1:
904 if self.expires == -1:
881 return False
905 return False
882 return time.time() > self.expires
906 return time.time() > self.expires
883
907
884 @property
908 @property
885 def role_humanized(self):
909 def role_humanized(self):
886 return self._get_role_name(self.role)
910 return self._get_role_name(self.role)
887
911
888
912
889 class UserEmailMap(Base, BaseModel):
913 class UserEmailMap(Base, BaseModel):
890 __tablename__ = 'user_email_map'
914 __tablename__ = 'user_email_map'
891 __table_args__ = (
915 __table_args__ = (
892 Index('uem_email_idx', 'email'),
916 Index('uem_email_idx', 'email'),
893 UniqueConstraint('email'),
917 UniqueConstraint('email'),
894 {'extend_existing': True, 'mysql_engine': 'InnoDB',
918 {'extend_existing': True, 'mysql_engine': 'InnoDB',
895 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
919 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
896 )
920 )
897 __mapper_args__ = {}
921 __mapper_args__ = {}
898
922
899 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
923 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
900 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
924 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
901 _email = Column("email", String(255), nullable=True, unique=False, default=None)
925 _email = Column("email", String(255), nullable=True, unique=False, default=None)
902 user = relationship('User', lazy='joined')
926 user = relationship('User', lazy='joined')
903
927
904 @validates('_email')
928 @validates('_email')
905 def validate_email(self, key, email):
929 def validate_email(self, key, email):
906 # check if this email is not main one
930 # check if this email is not main one
907 main_email = Session().query(User).filter(User.email == email).scalar()
931 main_email = Session().query(User).filter(User.email == email).scalar()
908 if main_email is not None:
932 if main_email is not None:
909 raise AttributeError('email %s is present is user table' % email)
933 raise AttributeError('email %s is present is user table' % email)
910 return email
934 return email
911
935
912 @hybrid_property
936 @hybrid_property
913 def email(self):
937 def email(self):
914 return self._email
938 return self._email
915
939
916 @email.setter
940 @email.setter
917 def email(self, val):
941 def email(self, val):
918 self._email = val.lower() if val else None
942 self._email = val.lower() if val else None
919
943
920
944
921 class UserIpMap(Base, BaseModel):
945 class UserIpMap(Base, BaseModel):
922 __tablename__ = 'user_ip_map'
946 __tablename__ = 'user_ip_map'
923 __table_args__ = (
947 __table_args__ = (
924 UniqueConstraint('user_id', 'ip_addr'),
948 UniqueConstraint('user_id', 'ip_addr'),
925 {'extend_existing': True, 'mysql_engine': 'InnoDB',
949 {'extend_existing': True, 'mysql_engine': 'InnoDB',
926 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
950 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
927 )
951 )
928 __mapper_args__ = {}
952 __mapper_args__ = {}
929
953
930 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
954 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
931 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
955 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
932 ip_addr = Column("ip_addr", String(255), nullable=True, unique=False, default=None)
956 ip_addr = Column("ip_addr", String(255), nullable=True, unique=False, default=None)
933 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
957 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
934 description = Column("description", String(10000), nullable=True, unique=None, default=None)
958 description = Column("description", String(10000), nullable=True, unique=None, default=None)
935 user = relationship('User', lazy='joined')
959 user = relationship('User', lazy='joined')
936
960
937 @classmethod
961 @classmethod
938 def _get_ip_range(cls, ip_addr):
962 def _get_ip_range(cls, ip_addr):
939 net = ipaddress.ip_network(ip_addr, strict=False)
963 net = ipaddress.ip_network(ip_addr, strict=False)
940 return [str(net.network_address), str(net.broadcast_address)]
964 return [str(net.network_address), str(net.broadcast_address)]
941
965
942 def __json__(self):
966 def __json__(self):
943 return {
967 return {
944 'ip_addr': self.ip_addr,
968 'ip_addr': self.ip_addr,
945 'ip_range': self._get_ip_range(self.ip_addr),
969 'ip_range': self._get_ip_range(self.ip_addr),
946 }
970 }
947
971
948 def __unicode__(self):
972 def __unicode__(self):
949 return u"<%s('user_id:%s=>%s')>" % (self.__class__.__name__,
973 return u"<%s('user_id:%s=>%s')>" % (self.__class__.__name__,
950 self.user_id, self.ip_addr)
974 self.user_id, self.ip_addr)
951
975
952 class UserLog(Base, BaseModel):
976 class UserLog(Base, BaseModel):
953 __tablename__ = 'user_logs'
977 __tablename__ = 'user_logs'
954 __table_args__ = (
978 __table_args__ = (
955 {'extend_existing': True, 'mysql_engine': 'InnoDB',
979 {'extend_existing': True, 'mysql_engine': 'InnoDB',
956 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
980 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
957 )
981 )
958 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
982 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
959 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
983 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
960 username = Column("username", String(255), nullable=True, unique=None, default=None)
984 username = Column("username", String(255), nullable=True, unique=None, default=None)
961 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
985 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
962 repository_name = Column("repository_name", String(255), nullable=True, unique=None, default=None)
986 repository_name = Column("repository_name", String(255), nullable=True, unique=None, default=None)
963 user_ip = Column("user_ip", String(255), nullable=True, unique=None, default=None)
987 user_ip = Column("user_ip", String(255), nullable=True, unique=None, default=None)
964 action = Column("action", Text().with_variant(Text(1200000), 'mysql'), nullable=True, unique=None, default=None)
988 action = Column("action", Text().with_variant(Text(1200000), 'mysql'), nullable=True, unique=None, default=None)
965 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
989 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
966
990
967 def __unicode__(self):
991 def __unicode__(self):
968 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
992 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
969 self.repository_name,
993 self.repository_name,
970 self.action)
994 self.action)
971
995
972 @property
996 @property
973 def action_as_day(self):
997 def action_as_day(self):
974 return datetime.date(*self.action_date.timetuple()[:3])
998 return datetime.date(*self.action_date.timetuple()[:3])
975
999
976 user = relationship('User')
1000 user = relationship('User')
977 repository = relationship('Repository', cascade='')
1001 repository = relationship('Repository', cascade='')
978
1002
979
1003
980 class UserGroup(Base, BaseModel):
1004 class UserGroup(Base, BaseModel):
981 __tablename__ = 'users_groups'
1005 __tablename__ = 'users_groups'
982 __table_args__ = (
1006 __table_args__ = (
983 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1007 {'extend_existing': True, 'mysql_engine': 'InnoDB',
984 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1008 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
985 )
1009 )
986
1010
987 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1011 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
988 users_group_name = Column("users_group_name", String(255), nullable=False, unique=True, default=None)
1012 users_group_name = Column("users_group_name", String(255), nullable=False, unique=True, default=None)
989 user_group_description = Column("user_group_description", String(10000), nullable=True, unique=None, default=None)
1013 user_group_description = Column("user_group_description", String(10000), nullable=True, unique=None, default=None)
990 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
1014 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
991 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
1015 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
992 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
1016 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
993 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1017 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
994 _group_data = Column("group_data", LargeBinary(), nullable=True) # JSON data
1018 _group_data = Column("group_data", LargeBinary(), nullable=True) # JSON data
995
1019
996 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
1020 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
997 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
1021 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
998 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
1022 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
999 users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
1023 users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
1000 user_user_group_to_perm = relationship('UserUserGroupToPerm', cascade='all')
1024 user_user_group_to_perm = relationship('UserUserGroupToPerm', cascade='all')
1001 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm ', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
1025 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm ', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
1002
1026
1003 user = relationship('User')
1027 user = relationship('User')
1004
1028
1005 @hybrid_property
1029 @hybrid_property
1006 def group_data(self):
1030 def group_data(self):
1007 if not self._group_data:
1031 if not self._group_data:
1008 return {}
1032 return {}
1009
1033
1010 try:
1034 try:
1011 return json.loads(self._group_data)
1035 return json.loads(self._group_data)
1012 except TypeError:
1036 except TypeError:
1013 return {}
1037 return {}
1014
1038
1015 @group_data.setter
1039 @group_data.setter
1016 def group_data(self, val):
1040 def group_data(self, val):
1017 try:
1041 try:
1018 self._group_data = json.dumps(val)
1042 self._group_data = json.dumps(val)
1019 except Exception:
1043 except Exception:
1020 log.error(traceback.format_exc())
1044 log.error(traceback.format_exc())
1021
1045
1022 def __unicode__(self):
1046 def __unicode__(self):
1023 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
1047 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
1024 self.users_group_id,
1048 self.users_group_id,
1025 self.users_group_name)
1049 self.users_group_name)
1026
1050
1027 @classmethod
1051 @classmethod
1028 def get_by_group_name(cls, group_name, cache=False,
1052 def get_by_group_name(cls, group_name, cache=False,
1029 case_insensitive=False):
1053 case_insensitive=False):
1030 if case_insensitive:
1054 if case_insensitive:
1031 q = cls.query().filter(func.lower(cls.users_group_name) ==
1055 q = cls.query().filter(func.lower(cls.users_group_name) ==
1032 func.lower(group_name))
1056 func.lower(group_name))
1033
1057
1034 else:
1058 else:
1035 q = cls.query().filter(cls.users_group_name == group_name)
1059 q = cls.query().filter(cls.users_group_name == group_name)
1036 if cache:
1060 if cache:
1037 q = q.options(FromCache(
1061 q = q.options(FromCache(
1038 "sql_cache_short",
1062 "sql_cache_short",
1039 "get_group_%s" % _hash_key(group_name)))
1063 "get_group_%s" % _hash_key(group_name)))
1040 return q.scalar()
1064 return q.scalar()
1041
1065
1042 @classmethod
1066 @classmethod
1043 def get(cls, user_group_id, cache=False):
1067 def get(cls, user_group_id, cache=False):
1044 user_group = cls.query()
1068 user_group = cls.query()
1045 if cache:
1069 if cache:
1046 user_group = user_group.options(FromCache("sql_cache_short",
1070 user_group = user_group.options(FromCache("sql_cache_short",
1047 "get_users_group_%s" % user_group_id))
1071 "get_users_group_%s" % user_group_id))
1048 return user_group.get(user_group_id)
1072 return user_group.get(user_group_id)
1049
1073
1050 def permissions(self, with_admins=True, with_owner=True):
1074 def permissions(self, with_admins=True, with_owner=True):
1051 q = UserUserGroupToPerm.query().filter(UserUserGroupToPerm.user_group == self)
1075 q = UserUserGroupToPerm.query().filter(UserUserGroupToPerm.user_group == self)
1052 q = q.options(joinedload(UserUserGroupToPerm.user_group),
1076 q = q.options(joinedload(UserUserGroupToPerm.user_group),
1053 joinedload(UserUserGroupToPerm.user),
1077 joinedload(UserUserGroupToPerm.user),
1054 joinedload(UserUserGroupToPerm.permission),)
1078 joinedload(UserUserGroupToPerm.permission),)
1055
1079
1056 # get owners and admins and permissions. We do a trick of re-writing
1080 # get owners and admins and permissions. We do a trick of re-writing
1057 # objects from sqlalchemy to named-tuples due to sqlalchemy session
1081 # objects from sqlalchemy to named-tuples due to sqlalchemy session
1058 # has a global reference and changing one object propagates to all
1082 # has a global reference and changing one object propagates to all
1059 # others. This means if admin is also an owner admin_row that change
1083 # others. This means if admin is also an owner admin_row that change
1060 # would propagate to both objects
1084 # would propagate to both objects
1061 perm_rows = []
1085 perm_rows = []
1062 for _usr in q.all():
1086 for _usr in q.all():
1063 usr = AttributeDict(_usr.user.get_dict())
1087 usr = AttributeDict(_usr.user.get_dict())
1064 usr.permission = _usr.permission.permission_name
1088 usr.permission = _usr.permission.permission_name
1065 perm_rows.append(usr)
1089 perm_rows.append(usr)
1066
1090
1067 # filter the perm rows by 'default' first and then sort them by
1091 # filter the perm rows by 'default' first and then sort them by
1068 # admin,write,read,none permissions sorted again alphabetically in
1092 # admin,write,read,none permissions sorted again alphabetically in
1069 # each group
1093 # each group
1070 perm_rows = sorted(perm_rows, key=display_sort)
1094 perm_rows = sorted(perm_rows, key=display_sort)
1071
1095
1072 _admin_perm = 'usergroup.admin'
1096 _admin_perm = 'usergroup.admin'
1073 owner_row = []
1097 owner_row = []
1074 if with_owner:
1098 if with_owner:
1075 usr = AttributeDict(self.user.get_dict())
1099 usr = AttributeDict(self.user.get_dict())
1076 usr.owner_row = True
1100 usr.owner_row = True
1077 usr.permission = _admin_perm
1101 usr.permission = _admin_perm
1078 owner_row.append(usr)
1102 owner_row.append(usr)
1079
1103
1080 super_admin_rows = []
1104 super_admin_rows = []
1081 if with_admins:
1105 if with_admins:
1082 for usr in User.get_all_super_admins():
1106 for usr in User.get_all_super_admins():
1083 # if this admin is also owner, don't double the record
1107 # if this admin is also owner, don't double the record
1084 if usr.user_id == owner_row[0].user_id:
1108 if usr.user_id == owner_row[0].user_id:
1085 owner_row[0].admin_row = True
1109 owner_row[0].admin_row = True
1086 else:
1110 else:
1087 usr = AttributeDict(usr.get_dict())
1111 usr = AttributeDict(usr.get_dict())
1088 usr.admin_row = True
1112 usr.admin_row = True
1089 usr.permission = _admin_perm
1113 usr.permission = _admin_perm
1090 super_admin_rows.append(usr)
1114 super_admin_rows.append(usr)
1091
1115
1092 return super_admin_rows + owner_row + perm_rows
1116 return super_admin_rows + owner_row + perm_rows
1093
1117
1094 def permission_user_groups(self):
1118 def permission_user_groups(self):
1095 q = UserGroupUserGroupToPerm.query().filter(UserGroupUserGroupToPerm.target_user_group == self)
1119 q = UserGroupUserGroupToPerm.query().filter(UserGroupUserGroupToPerm.target_user_group == self)
1096 q = q.options(joinedload(UserGroupUserGroupToPerm.user_group),
1120 q = q.options(joinedload(UserGroupUserGroupToPerm.user_group),
1097 joinedload(UserGroupUserGroupToPerm.target_user_group),
1121 joinedload(UserGroupUserGroupToPerm.target_user_group),
1098 joinedload(UserGroupUserGroupToPerm.permission),)
1122 joinedload(UserGroupUserGroupToPerm.permission),)
1099
1123
1100 perm_rows = []
1124 perm_rows = []
1101 for _user_group in q.all():
1125 for _user_group in q.all():
1102 usr = AttributeDict(_user_group.user_group.get_dict())
1126 usr = AttributeDict(_user_group.user_group.get_dict())
1103 usr.permission = _user_group.permission.permission_name
1127 usr.permission = _user_group.permission.permission_name
1104 perm_rows.append(usr)
1128 perm_rows.append(usr)
1105
1129
1106 return perm_rows
1130 return perm_rows
1107
1131
1108 def _get_default_perms(self, user_group, suffix=''):
1132 def _get_default_perms(self, user_group, suffix=''):
1109 from rhodecode.model.permission import PermissionModel
1133 from rhodecode.model.permission import PermissionModel
1110 return PermissionModel().get_default_perms(user_group.users_group_to_perm, suffix)
1134 return PermissionModel().get_default_perms(user_group.users_group_to_perm, suffix)
1111
1135
1112 def get_default_perms(self, suffix=''):
1136 def get_default_perms(self, suffix=''):
1113 return self._get_default_perms(self, suffix)
1137 return self._get_default_perms(self, suffix)
1114
1138
1115 def get_api_data(self, with_group_members=True, include_secrets=False):
1139 def get_api_data(self, with_group_members=True, include_secrets=False):
1116 """
1140 """
1117 :param include_secrets: See :meth:`User.get_api_data`, this parameter is
1141 :param include_secrets: See :meth:`User.get_api_data`, this parameter is
1118 basically forwarded.
1142 basically forwarded.
1119
1143
1120 """
1144 """
1121 user_group = self
1145 user_group = self
1122
1146
1123 data = {
1147 data = {
1124 'users_group_id': user_group.users_group_id,
1148 'users_group_id': user_group.users_group_id,
1125 'group_name': user_group.users_group_name,
1149 'group_name': user_group.users_group_name,
1126 'group_description': user_group.user_group_description,
1150 'group_description': user_group.user_group_description,
1127 'active': user_group.users_group_active,
1151 'active': user_group.users_group_active,
1128 'owner': user_group.user.username,
1152 'owner': user_group.user.username,
1129 }
1153 }
1130 if with_group_members:
1154 if with_group_members:
1131 users = []
1155 users = []
1132 for user in user_group.members:
1156 for user in user_group.members:
1133 user = user.user
1157 user = user.user
1134 users.append(user.get_api_data(include_secrets=include_secrets))
1158 users.append(user.get_api_data(include_secrets=include_secrets))
1135 data['users'] = users
1159 data['users'] = users
1136
1160
1137 return data
1161 return data
1138
1162
1139
1163
1140 class UserGroupMember(Base, BaseModel):
1164 class UserGroupMember(Base, BaseModel):
1141 __tablename__ = 'users_groups_members'
1165 __tablename__ = 'users_groups_members'
1142 __table_args__ = (
1166 __table_args__ = (
1143 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1167 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1144 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1168 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1145 )
1169 )
1146
1170
1147 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1171 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1148 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1172 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1149 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1173 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1150
1174
1151 user = relationship('User', lazy='joined')
1175 user = relationship('User', lazy='joined')
1152 users_group = relationship('UserGroup')
1176 users_group = relationship('UserGroup')
1153
1177
1154 def __init__(self, gr_id='', u_id=''):
1178 def __init__(self, gr_id='', u_id=''):
1155 self.users_group_id = gr_id
1179 self.users_group_id = gr_id
1156 self.user_id = u_id
1180 self.user_id = u_id
1157
1181
1158
1182
1159 class RepositoryField(Base, BaseModel):
1183 class RepositoryField(Base, BaseModel):
1160 __tablename__ = 'repositories_fields'
1184 __tablename__ = 'repositories_fields'
1161 __table_args__ = (
1185 __table_args__ = (
1162 UniqueConstraint('repository_id', 'field_key'), # no-multi field
1186 UniqueConstraint('repository_id', 'field_key'), # no-multi field
1163 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1187 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1164 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1188 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1165 )
1189 )
1166 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
1190 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
1167
1191
1168 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1192 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1169 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1193 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1170 field_key = Column("field_key", String(250))
1194 field_key = Column("field_key", String(250))
1171 field_label = Column("field_label", String(1024), nullable=False)
1195 field_label = Column("field_label", String(1024), nullable=False)
1172 field_value = Column("field_value", String(10000), nullable=False)
1196 field_value = Column("field_value", String(10000), nullable=False)
1173 field_desc = Column("field_desc", String(1024), nullable=False)
1197 field_desc = Column("field_desc", String(1024), nullable=False)
1174 field_type = Column("field_type", String(255), nullable=False, unique=None)
1198 field_type = Column("field_type", String(255), nullable=False, unique=None)
1175 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1199 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1176
1200
1177 repository = relationship('Repository')
1201 repository = relationship('Repository')
1178
1202
1179 @property
1203 @property
1180 def field_key_prefixed(self):
1204 def field_key_prefixed(self):
1181 return 'ex_%s' % self.field_key
1205 return 'ex_%s' % self.field_key
1182
1206
1183 @classmethod
1207 @classmethod
1184 def un_prefix_key(cls, key):
1208 def un_prefix_key(cls, key):
1185 if key.startswith(cls.PREFIX):
1209 if key.startswith(cls.PREFIX):
1186 return key[len(cls.PREFIX):]
1210 return key[len(cls.PREFIX):]
1187 return key
1211 return key
1188
1212
1189 @classmethod
1213 @classmethod
1190 def get_by_key_name(cls, key, repo):
1214 def get_by_key_name(cls, key, repo):
1191 row = cls.query()\
1215 row = cls.query()\
1192 .filter(cls.repository == repo)\
1216 .filter(cls.repository == repo)\
1193 .filter(cls.field_key == key).scalar()
1217 .filter(cls.field_key == key).scalar()
1194 return row
1218 return row
1195
1219
1196
1220
1197 class Repository(Base, BaseModel):
1221 class Repository(Base, BaseModel):
1198 __tablename__ = 'repositories'
1222 __tablename__ = 'repositories'
1199 __table_args__ = (
1223 __table_args__ = (
1200 Index('r_repo_name_idx', 'repo_name', mysql_length=255),
1224 Index('r_repo_name_idx', 'repo_name', mysql_length=255),
1201 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1225 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1202 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1226 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1203 )
1227 )
1204 DEFAULT_CLONE_URI = '{scheme}://{user}@{netloc}/{repo}'
1228 DEFAULT_CLONE_URI = '{scheme}://{user}@{netloc}/{repo}'
1205 DEFAULT_CLONE_URI_ID = '{scheme}://{user}@{netloc}/_{repoid}'
1229 DEFAULT_CLONE_URI_ID = '{scheme}://{user}@{netloc}/_{repoid}'
1206
1230
1207 STATE_CREATED = 'repo_state_created'
1231 STATE_CREATED = 'repo_state_created'
1208 STATE_PENDING = 'repo_state_pending'
1232 STATE_PENDING = 'repo_state_pending'
1209 STATE_ERROR = 'repo_state_error'
1233 STATE_ERROR = 'repo_state_error'
1210
1234
1211 LOCK_AUTOMATIC = 'lock_auto'
1235 LOCK_AUTOMATIC = 'lock_auto'
1212 LOCK_API = 'lock_api'
1236 LOCK_API = 'lock_api'
1213 LOCK_WEB = 'lock_web'
1237 LOCK_WEB = 'lock_web'
1214 LOCK_PULL = 'lock_pull'
1238 LOCK_PULL = 'lock_pull'
1215
1239
1216 NAME_SEP = URL_SEP
1240 NAME_SEP = URL_SEP
1217
1241
1218 repo_id = Column(
1242 repo_id = Column(
1219 "repo_id", Integer(), nullable=False, unique=True, default=None,
1243 "repo_id", Integer(), nullable=False, unique=True, default=None,
1220 primary_key=True)
1244 primary_key=True)
1221 _repo_name = Column(
1245 _repo_name = Column(
1222 "repo_name", Text(), nullable=False, default=None)
1246 "repo_name", Text(), nullable=False, default=None)
1223 _repo_name_hash = Column(
1247 _repo_name_hash = Column(
1224 "repo_name_hash", String(255), nullable=False, unique=True)
1248 "repo_name_hash", String(255), nullable=False, unique=True)
1225 repo_state = Column("repo_state", String(255), nullable=True)
1249 repo_state = Column("repo_state", String(255), nullable=True)
1226
1250
1227 clone_uri = Column(
1251 clone_uri = Column(
1228 "clone_uri", EncryptedTextValue(), nullable=True, unique=False,
1252 "clone_uri", EncryptedTextValue(), nullable=True, unique=False,
1229 default=None)
1253 default=None)
1230 repo_type = Column(
1254 repo_type = Column(
1231 "repo_type", String(255), nullable=False, unique=False, default=None)
1255 "repo_type", String(255), nullable=False, unique=False, default=None)
1232 user_id = Column(
1256 user_id = Column(
1233 "user_id", Integer(), ForeignKey('users.user_id'), nullable=False,
1257 "user_id", Integer(), ForeignKey('users.user_id'), nullable=False,
1234 unique=False, default=None)
1258 unique=False, default=None)
1235 private = Column(
1259 private = Column(
1236 "private", Boolean(), nullable=True, unique=None, default=None)
1260 "private", Boolean(), nullable=True, unique=None, default=None)
1237 enable_statistics = Column(
1261 enable_statistics = Column(
1238 "statistics", Boolean(), nullable=True, unique=None, default=True)
1262 "statistics", Boolean(), nullable=True, unique=None, default=True)
1239 enable_downloads = Column(
1263 enable_downloads = Column(
1240 "downloads", Boolean(), nullable=True, unique=None, default=True)
1264 "downloads", Boolean(), nullable=True, unique=None, default=True)
1241 description = Column(
1265 description = Column(
1242 "description", String(10000), nullable=True, unique=None, default=None)
1266 "description", String(10000), nullable=True, unique=None, default=None)
1243 created_on = Column(
1267 created_on = Column(
1244 'created_on', DateTime(timezone=False), nullable=True, unique=None,
1268 'created_on', DateTime(timezone=False), nullable=True, unique=None,
1245 default=datetime.datetime.now)
1269 default=datetime.datetime.now)
1246 updated_on = Column(
1270 updated_on = Column(
1247 'updated_on', DateTime(timezone=False), nullable=True, unique=None,
1271 'updated_on', DateTime(timezone=False), nullable=True, unique=None,
1248 default=datetime.datetime.now)
1272 default=datetime.datetime.now)
1249 _landing_revision = Column(
1273 _landing_revision = Column(
1250 "landing_revision", String(255), nullable=False, unique=False,
1274 "landing_revision", String(255), nullable=False, unique=False,
1251 default=None)
1275 default=None)
1252 enable_locking = Column(
1276 enable_locking = Column(
1253 "enable_locking", Boolean(), nullable=False, unique=None,
1277 "enable_locking", Boolean(), nullable=False, unique=None,
1254 default=False)
1278 default=False)
1255 _locked = Column(
1279 _locked = Column(
1256 "locked", String(255), nullable=True, unique=False, default=None)
1280 "locked", String(255), nullable=True, unique=False, default=None)
1257 _changeset_cache = Column(
1281 _changeset_cache = Column(
1258 "changeset_cache", LargeBinary(), nullable=True) # JSON data
1282 "changeset_cache", LargeBinary(), nullable=True) # JSON data
1259
1283
1260 fork_id = Column(
1284 fork_id = Column(
1261 "fork_id", Integer(), ForeignKey('repositories.repo_id'),
1285 "fork_id", Integer(), ForeignKey('repositories.repo_id'),
1262 nullable=True, unique=False, default=None)
1286 nullable=True, unique=False, default=None)
1263 group_id = Column(
1287 group_id = Column(
1264 "group_id", Integer(), ForeignKey('groups.group_id'), nullable=True,
1288 "group_id", Integer(), ForeignKey('groups.group_id'), nullable=True,
1265 unique=False, default=None)
1289 unique=False, default=None)
1266
1290
1267 user = relationship('User', lazy='joined')
1291 user = relationship('User', lazy='joined')
1268 fork = relationship('Repository', remote_side=repo_id, lazy='joined')
1292 fork = relationship('Repository', remote_side=repo_id, lazy='joined')
1269 group = relationship('RepoGroup', lazy='joined')
1293 group = relationship('RepoGroup', lazy='joined')
1270 repo_to_perm = relationship(
1294 repo_to_perm = relationship(
1271 'UserRepoToPerm', cascade='all',
1295 'UserRepoToPerm', cascade='all',
1272 order_by='UserRepoToPerm.repo_to_perm_id')
1296 order_by='UserRepoToPerm.repo_to_perm_id')
1273 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
1297 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
1274 stats = relationship('Statistics', cascade='all', uselist=False)
1298 stats = relationship('Statistics', cascade='all', uselist=False)
1275
1299
1276 followers = relationship(
1300 followers = relationship(
1277 'UserFollowing',
1301 'UserFollowing',
1278 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
1302 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
1279 cascade='all')
1303 cascade='all')
1280 extra_fields = relationship(
1304 extra_fields = relationship(
1281 'RepositoryField', cascade="all, delete, delete-orphan")
1305 'RepositoryField', cascade="all, delete, delete-orphan")
1282 logs = relationship('UserLog')
1306 logs = relationship('UserLog')
1283 comments = relationship(
1307 comments = relationship(
1284 'ChangesetComment', cascade="all, delete, delete-orphan")
1308 'ChangesetComment', cascade="all, delete, delete-orphan")
1285 pull_requests_source = relationship(
1309 pull_requests_source = relationship(
1286 'PullRequest',
1310 'PullRequest',
1287 primaryjoin='PullRequest.source_repo_id==Repository.repo_id',
1311 primaryjoin='PullRequest.source_repo_id==Repository.repo_id',
1288 cascade="all, delete, delete-orphan")
1312 cascade="all, delete, delete-orphan")
1289 pull_requests_target = relationship(
1313 pull_requests_target = relationship(
1290 'PullRequest',
1314 'PullRequest',
1291 primaryjoin='PullRequest.target_repo_id==Repository.repo_id',
1315 primaryjoin='PullRequest.target_repo_id==Repository.repo_id',
1292 cascade="all, delete, delete-orphan")
1316 cascade="all, delete, delete-orphan")
1293 ui = relationship('RepoRhodeCodeUi', cascade="all")
1317 ui = relationship('RepoRhodeCodeUi', cascade="all")
1294 settings = relationship('RepoRhodeCodeSetting', cascade="all")
1318 settings = relationship('RepoRhodeCodeSetting', cascade="all")
1295
1319
1296 def __unicode__(self):
1320 def __unicode__(self):
1297 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
1321 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
1298 safe_unicode(self.repo_name))
1322 safe_unicode(self.repo_name))
1299
1323
1300 @hybrid_property
1324 @hybrid_property
1301 def landing_rev(self):
1325 def landing_rev(self):
1302 # always should return [rev_type, rev]
1326 # always should return [rev_type, rev]
1303 if self._landing_revision:
1327 if self._landing_revision:
1304 _rev_info = self._landing_revision.split(':')
1328 _rev_info = self._landing_revision.split(':')
1305 if len(_rev_info) < 2:
1329 if len(_rev_info) < 2:
1306 _rev_info.insert(0, 'rev')
1330 _rev_info.insert(0, 'rev')
1307 return [_rev_info[0], _rev_info[1]]
1331 return [_rev_info[0], _rev_info[1]]
1308 return [None, None]
1332 return [None, None]
1309
1333
1310 @landing_rev.setter
1334 @landing_rev.setter
1311 def landing_rev(self, val):
1335 def landing_rev(self, val):
1312 if ':' not in val:
1336 if ':' not in val:
1313 raise ValueError('value must be delimited with `:` and consist '
1337 raise ValueError('value must be delimited with `:` and consist '
1314 'of <rev_type>:<rev>, got %s instead' % val)
1338 'of <rev_type>:<rev>, got %s instead' % val)
1315 self._landing_revision = val
1339 self._landing_revision = val
1316
1340
1317 @hybrid_property
1341 @hybrid_property
1318 def locked(self):
1342 def locked(self):
1319 if self._locked:
1343 if self._locked:
1320 user_id, timelocked, reason = self._locked.split(':')
1344 user_id, timelocked, reason = self._locked.split(':')
1321 lock_values = int(user_id), timelocked, reason
1345 lock_values = int(user_id), timelocked, reason
1322 else:
1346 else:
1323 lock_values = [None, None, None]
1347 lock_values = [None, None, None]
1324 return lock_values
1348 return lock_values
1325
1349
1326 @locked.setter
1350 @locked.setter
1327 def locked(self, val):
1351 def locked(self, val):
1328 if val and isinstance(val, (list, tuple)):
1352 if val and isinstance(val, (list, tuple)):
1329 self._locked = ':'.join(map(str, val))
1353 self._locked = ':'.join(map(str, val))
1330 else:
1354 else:
1331 self._locked = None
1355 self._locked = None
1332
1356
1333 @hybrid_property
1357 @hybrid_property
1334 def changeset_cache(self):
1358 def changeset_cache(self):
1335 from rhodecode.lib.vcs.backends.base import EmptyCommit
1359 from rhodecode.lib.vcs.backends.base import EmptyCommit
1336 dummy = EmptyCommit().__json__()
1360 dummy = EmptyCommit().__json__()
1337 if not self._changeset_cache:
1361 if not self._changeset_cache:
1338 return dummy
1362 return dummy
1339 try:
1363 try:
1340 return json.loads(self._changeset_cache)
1364 return json.loads(self._changeset_cache)
1341 except TypeError:
1365 except TypeError:
1342 return dummy
1366 return dummy
1343 except Exception:
1367 except Exception:
1344 log.error(traceback.format_exc())
1368 log.error(traceback.format_exc())
1345 return dummy
1369 return dummy
1346
1370
1347 @changeset_cache.setter
1371 @changeset_cache.setter
1348 def changeset_cache(self, val):
1372 def changeset_cache(self, val):
1349 try:
1373 try:
1350 self._changeset_cache = json.dumps(val)
1374 self._changeset_cache = json.dumps(val)
1351 except Exception:
1375 except Exception:
1352 log.error(traceback.format_exc())
1376 log.error(traceback.format_exc())
1353
1377
1354 @hybrid_property
1378 @hybrid_property
1355 def repo_name(self):
1379 def repo_name(self):
1356 return self._repo_name
1380 return self._repo_name
1357
1381
1358 @repo_name.setter
1382 @repo_name.setter
1359 def repo_name(self, value):
1383 def repo_name(self, value):
1360 self._repo_name = value
1384 self._repo_name = value
1361 self._repo_name_hash = hashlib.sha1(safe_str(value)).hexdigest()
1385 self._repo_name_hash = hashlib.sha1(safe_str(value)).hexdigest()
1362
1386
1363 @classmethod
1387 @classmethod
1364 def normalize_repo_name(cls, repo_name):
1388 def normalize_repo_name(cls, repo_name):
1365 """
1389 """
1366 Normalizes os specific repo_name to the format internally stored inside
1390 Normalizes os specific repo_name to the format internally stored inside
1367 dabatabase using URL_SEP
1391 database using URL_SEP
1368
1392
1369 :param cls:
1393 :param cls:
1370 :param repo_name:
1394 :param repo_name:
1371 """
1395 """
1372 return cls.NAME_SEP.join(repo_name.split(os.sep))
1396 return cls.NAME_SEP.join(repo_name.split(os.sep))
1373
1397
1374 @classmethod
1398 @classmethod
1375 def get_by_repo_name(cls, repo_name):
1399 def get_by_repo_name(cls, repo_name, cache=False, identity_cache=False):
1376 session = Session()
1400 session = Session()
1377 exist_in_session = []
1378 for (item_cls, pkey), instance in session.identity_map.items():
1379 if cls == item_cls and instance.repo_name == repo_name:
1380 exist_in_session.append(instance)
1381 if exist_in_session:
1382 if len(exist_in_session) == 1:
1383 return exist_in_session[0]
1384 log.exception(
1385 'multiple repos with same name: %r' % exist_in_session)
1386
1387 q = session.query(cls).filter(cls.repo_name == repo_name)
1401 q = session.query(cls).filter(cls.repo_name == repo_name)
1402
1403 if cache:
1404 if identity_cache:
1405 val = cls.identity_cache(session, 'repo_name', repo_name)
1406 if val:
1407 return val
1408 else:
1409 q = q.options(
1410 FromCache("sql_cache_short",
1411 "get_repo_by_name_%s" % _hash_key(repo_name)))
1412
1388 return q.scalar()
1413 return q.scalar()
1389
1414
1390 @classmethod
1415 @classmethod
1391 def get_by_full_path(cls, repo_full_path):
1416 def get_by_full_path(cls, repo_full_path):
1392 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
1417 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
1393 repo_name = cls.normalize_repo_name(repo_name)
1418 repo_name = cls.normalize_repo_name(repo_name)
1394 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
1419 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
1395
1420
1396 @classmethod
1421 @classmethod
1397 def get_repo_forks(cls, repo_id):
1422 def get_repo_forks(cls, repo_id):
1398 return cls.query().filter(Repository.fork_id == repo_id)
1423 return cls.query().filter(Repository.fork_id == repo_id)
1399
1424
1400 @classmethod
1425 @classmethod
1401 def base_path(cls):
1426 def base_path(cls):
1402 """
1427 """
1403 Returns base path when all repos are stored
1428 Returns base path when all repos are stored
1404
1429
1405 :param cls:
1430 :param cls:
1406 """
1431 """
1407 q = Session().query(RhodeCodeUi)\
1432 q = Session().query(RhodeCodeUi)\
1408 .filter(RhodeCodeUi.ui_key == cls.NAME_SEP)
1433 .filter(RhodeCodeUi.ui_key == cls.NAME_SEP)
1409 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
1434 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
1410 return q.one().ui_value
1435 return q.one().ui_value
1411
1436
1412 @classmethod
1437 @classmethod
1413 def is_valid(cls, repo_name):
1438 def is_valid(cls, repo_name):
1414 """
1439 """
1415 returns True if given repo name is a valid filesystem repository
1440 returns True if given repo name is a valid filesystem repository
1416
1441
1417 :param cls:
1442 :param cls:
1418 :param repo_name:
1443 :param repo_name:
1419 """
1444 """
1420 from rhodecode.lib.utils import is_valid_repo
1445 from rhodecode.lib.utils import is_valid_repo
1421
1446
1422 return is_valid_repo(repo_name, cls.base_path())
1447 return is_valid_repo(repo_name, cls.base_path())
1423
1448
1424 @classmethod
1449 @classmethod
1425 def get_all_repos(cls, user_id=Optional(None), group_id=Optional(None),
1450 def get_all_repos(cls, user_id=Optional(None), group_id=Optional(None),
1426 case_insensitive=True):
1451 case_insensitive=True):
1427 q = Repository.query()
1452 q = Repository.query()
1428
1453
1429 if not isinstance(user_id, Optional):
1454 if not isinstance(user_id, Optional):
1430 q = q.filter(Repository.user_id == user_id)
1455 q = q.filter(Repository.user_id == user_id)
1431
1456
1432 if not isinstance(group_id, Optional):
1457 if not isinstance(group_id, Optional):
1433 q = q.filter(Repository.group_id == group_id)
1458 q = q.filter(Repository.group_id == group_id)
1434
1459
1435 if case_insensitive:
1460 if case_insensitive:
1436 q = q.order_by(func.lower(Repository.repo_name))
1461 q = q.order_by(func.lower(Repository.repo_name))
1437 else:
1462 else:
1438 q = q.order_by(Repository.repo_name)
1463 q = q.order_by(Repository.repo_name)
1439 return q.all()
1464 return q.all()
1440
1465
1441 @property
1466 @property
1442 def forks(self):
1467 def forks(self):
1443 """
1468 """
1444 Return forks of this repo
1469 Return forks of this repo
1445 """
1470 """
1446 return Repository.get_repo_forks(self.repo_id)
1471 return Repository.get_repo_forks(self.repo_id)
1447
1472
1448 @property
1473 @property
1449 def parent(self):
1474 def parent(self):
1450 """
1475 """
1451 Returns fork parent
1476 Returns fork parent
1452 """
1477 """
1453 return self.fork
1478 return self.fork
1454
1479
1455 @property
1480 @property
1456 def just_name(self):
1481 def just_name(self):
1457 return self.repo_name.split(self.NAME_SEP)[-1]
1482 return self.repo_name.split(self.NAME_SEP)[-1]
1458
1483
1459 @property
1484 @property
1460 def groups_with_parents(self):
1485 def groups_with_parents(self):
1461 groups = []
1486 groups = []
1462 if self.group is None:
1487 if self.group is None:
1463 return groups
1488 return groups
1464
1489
1465 cur_gr = self.group
1490 cur_gr = self.group
1466 groups.insert(0, cur_gr)
1491 groups.insert(0, cur_gr)
1467 while 1:
1492 while 1:
1468 gr = getattr(cur_gr, 'parent_group', None)
1493 gr = getattr(cur_gr, 'parent_group', None)
1469 cur_gr = cur_gr.parent_group
1494 cur_gr = cur_gr.parent_group
1470 if gr is None:
1495 if gr is None:
1471 break
1496 break
1472 groups.insert(0, gr)
1497 groups.insert(0, gr)
1473
1498
1474 return groups
1499 return groups
1475
1500
1476 @property
1501 @property
1477 def groups_and_repo(self):
1502 def groups_and_repo(self):
1478 return self.groups_with_parents, self
1503 return self.groups_with_parents, self
1479
1504
1480 @LazyProperty
1505 @LazyProperty
1481 def repo_path(self):
1506 def repo_path(self):
1482 """
1507 """
1483 Returns base full path for that repository means where it actually
1508 Returns base full path for that repository means where it actually
1484 exists on a filesystem
1509 exists on a filesystem
1485 """
1510 """
1486 q = Session().query(RhodeCodeUi).filter(
1511 q = Session().query(RhodeCodeUi).filter(
1487 RhodeCodeUi.ui_key == self.NAME_SEP)
1512 RhodeCodeUi.ui_key == self.NAME_SEP)
1488 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
1513 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
1489 return q.one().ui_value
1514 return q.one().ui_value
1490
1515
1491 @property
1516 @property
1492 def repo_full_path(self):
1517 def repo_full_path(self):
1493 p = [self.repo_path]
1518 p = [self.repo_path]
1494 # we need to split the name by / since this is how we store the
1519 # we need to split the name by / since this is how we store the
1495 # names in the database, but that eventually needs to be converted
1520 # names in the database, but that eventually needs to be converted
1496 # into a valid system path
1521 # into a valid system path
1497 p += self.repo_name.split(self.NAME_SEP)
1522 p += self.repo_name.split(self.NAME_SEP)
1498 return os.path.join(*map(safe_unicode, p))
1523 return os.path.join(*map(safe_unicode, p))
1499
1524
1500 @property
1525 @property
1501 def cache_keys(self):
1526 def cache_keys(self):
1502 """
1527 """
1503 Returns associated cache keys for that repo
1528 Returns associated cache keys for that repo
1504 """
1529 """
1505 return CacheKey.query()\
1530 return CacheKey.query()\
1506 .filter(CacheKey.cache_args == self.repo_name)\
1531 .filter(CacheKey.cache_args == self.repo_name)\
1507 .order_by(CacheKey.cache_key)\
1532 .order_by(CacheKey.cache_key)\
1508 .all()
1533 .all()
1509
1534
1510 def get_new_name(self, repo_name):
1535 def get_new_name(self, repo_name):
1511 """
1536 """
1512 returns new full repository name based on assigned group and new new
1537 returns new full repository name based on assigned group and new new
1513
1538
1514 :param group_name:
1539 :param group_name:
1515 """
1540 """
1516 path_prefix = self.group.full_path_splitted if self.group else []
1541 path_prefix = self.group.full_path_splitted if self.group else []
1517 return self.NAME_SEP.join(path_prefix + [repo_name])
1542 return self.NAME_SEP.join(path_prefix + [repo_name])
1518
1543
1519 @property
1544 @property
1520 def _config(self):
1545 def _config(self):
1521 """
1546 """
1522 Returns db based config object.
1547 Returns db based config object.
1523 """
1548 """
1524 from rhodecode.lib.utils import make_db_config
1549 from rhodecode.lib.utils import make_db_config
1525 return make_db_config(clear_session=False, repo=self)
1550 return make_db_config(clear_session=False, repo=self)
1526
1551
1527 def permissions(self, with_admins=True, with_owner=True):
1552 def permissions(self, with_admins=True, with_owner=True):
1528 q = UserRepoToPerm.query().filter(UserRepoToPerm.repository == self)
1553 q = UserRepoToPerm.query().filter(UserRepoToPerm.repository == self)
1529 q = q.options(joinedload(UserRepoToPerm.repository),
1554 q = q.options(joinedload(UserRepoToPerm.repository),
1530 joinedload(UserRepoToPerm.user),
1555 joinedload(UserRepoToPerm.user),
1531 joinedload(UserRepoToPerm.permission),)
1556 joinedload(UserRepoToPerm.permission),)
1532
1557
1533 # get owners and admins and permissions. We do a trick of re-writing
1558 # get owners and admins and permissions. We do a trick of re-writing
1534 # objects from sqlalchemy to named-tuples due to sqlalchemy session
1559 # objects from sqlalchemy to named-tuples due to sqlalchemy session
1535 # has a global reference and changing one object propagates to all
1560 # has a global reference and changing one object propagates to all
1536 # others. This means if admin is also an owner admin_row that change
1561 # others. This means if admin is also an owner admin_row that change
1537 # would propagate to both objects
1562 # would propagate to both objects
1538 perm_rows = []
1563 perm_rows = []
1539 for _usr in q.all():
1564 for _usr in q.all():
1540 usr = AttributeDict(_usr.user.get_dict())
1565 usr = AttributeDict(_usr.user.get_dict())
1541 usr.permission = _usr.permission.permission_name
1566 usr.permission = _usr.permission.permission_name
1542 perm_rows.append(usr)
1567 perm_rows.append(usr)
1543
1568
1544 # filter the perm rows by 'default' first and then sort them by
1569 # filter the perm rows by 'default' first and then sort them by
1545 # admin,write,read,none permissions sorted again alphabetically in
1570 # admin,write,read,none permissions sorted again alphabetically in
1546 # each group
1571 # each group
1547 perm_rows = sorted(perm_rows, key=display_sort)
1572 perm_rows = sorted(perm_rows, key=display_sort)
1548
1573
1549 _admin_perm = 'repository.admin'
1574 _admin_perm = 'repository.admin'
1550 owner_row = []
1575 owner_row = []
1551 if with_owner:
1576 if with_owner:
1552 usr = AttributeDict(self.user.get_dict())
1577 usr = AttributeDict(self.user.get_dict())
1553 usr.owner_row = True
1578 usr.owner_row = True
1554 usr.permission = _admin_perm
1579 usr.permission = _admin_perm
1555 owner_row.append(usr)
1580 owner_row.append(usr)
1556
1581
1557 super_admin_rows = []
1582 super_admin_rows = []
1558 if with_admins:
1583 if with_admins:
1559 for usr in User.get_all_super_admins():
1584 for usr in User.get_all_super_admins():
1560 # if this admin is also owner, don't double the record
1585 # if this admin is also owner, don't double the record
1561 if usr.user_id == owner_row[0].user_id:
1586 if usr.user_id == owner_row[0].user_id:
1562 owner_row[0].admin_row = True
1587 owner_row[0].admin_row = True
1563 else:
1588 else:
1564 usr = AttributeDict(usr.get_dict())
1589 usr = AttributeDict(usr.get_dict())
1565 usr.admin_row = True
1590 usr.admin_row = True
1566 usr.permission = _admin_perm
1591 usr.permission = _admin_perm
1567 super_admin_rows.append(usr)
1592 super_admin_rows.append(usr)
1568
1593
1569 return super_admin_rows + owner_row + perm_rows
1594 return super_admin_rows + owner_row + perm_rows
1570
1595
1571 def permission_user_groups(self):
1596 def permission_user_groups(self):
1572 q = UserGroupRepoToPerm.query().filter(
1597 q = UserGroupRepoToPerm.query().filter(
1573 UserGroupRepoToPerm.repository == self)
1598 UserGroupRepoToPerm.repository == self)
1574 q = q.options(joinedload(UserGroupRepoToPerm.repository),
1599 q = q.options(joinedload(UserGroupRepoToPerm.repository),
1575 joinedload(UserGroupRepoToPerm.users_group),
1600 joinedload(UserGroupRepoToPerm.users_group),
1576 joinedload(UserGroupRepoToPerm.permission),)
1601 joinedload(UserGroupRepoToPerm.permission),)
1577
1602
1578 perm_rows = []
1603 perm_rows = []
1579 for _user_group in q.all():
1604 for _user_group in q.all():
1580 usr = AttributeDict(_user_group.users_group.get_dict())
1605 usr = AttributeDict(_user_group.users_group.get_dict())
1581 usr.permission = _user_group.permission.permission_name
1606 usr.permission = _user_group.permission.permission_name
1582 perm_rows.append(usr)
1607 perm_rows.append(usr)
1583
1608
1584 return perm_rows
1609 return perm_rows
1585
1610
1586 def get_api_data(self, include_secrets=False):
1611 def get_api_data(self, include_secrets=False):
1587 """
1612 """
1588 Common function for generating repo api data
1613 Common function for generating repo api data
1589
1614
1590 :param include_secrets: See :meth:`User.get_api_data`.
1615 :param include_secrets: See :meth:`User.get_api_data`.
1591
1616
1592 """
1617 """
1593 # TODO: mikhail: Here there is an anti-pattern, we probably need to
1618 # TODO: mikhail: Here there is an anti-pattern, we probably need to
1594 # move this methods on models level.
1619 # move this methods on models level.
1595 from rhodecode.model.settings import SettingsModel
1620 from rhodecode.model.settings import SettingsModel
1596
1621
1597 repo = self
1622 repo = self
1598 _user_id, _time, _reason = self.locked
1623 _user_id, _time, _reason = self.locked
1599
1624
1600 data = {
1625 data = {
1601 'repo_id': repo.repo_id,
1626 'repo_id': repo.repo_id,
1602 'repo_name': repo.repo_name,
1627 'repo_name': repo.repo_name,
1603 'repo_type': repo.repo_type,
1628 'repo_type': repo.repo_type,
1604 'clone_uri': repo.clone_uri or '',
1629 'clone_uri': repo.clone_uri or '',
1605 'private': repo.private,
1630 'private': repo.private,
1606 'created_on': repo.created_on,
1631 'created_on': repo.created_on,
1607 'description': repo.description,
1632 'description': repo.description,
1608 'landing_rev': repo.landing_rev,
1633 'landing_rev': repo.landing_rev,
1609 'owner': repo.user.username,
1634 'owner': repo.user.username,
1610 'fork_of': repo.fork.repo_name if repo.fork else None,
1635 'fork_of': repo.fork.repo_name if repo.fork else None,
1611 'enable_statistics': repo.enable_statistics,
1636 'enable_statistics': repo.enable_statistics,
1612 'enable_locking': repo.enable_locking,
1637 'enable_locking': repo.enable_locking,
1613 'enable_downloads': repo.enable_downloads,
1638 'enable_downloads': repo.enable_downloads,
1614 'last_changeset': repo.changeset_cache,
1639 'last_changeset': repo.changeset_cache,
1615 'locked_by': User.get(_user_id).get_api_data(
1640 'locked_by': User.get(_user_id).get_api_data(
1616 include_secrets=include_secrets) if _user_id else None,
1641 include_secrets=include_secrets) if _user_id else None,
1617 'locked_date': time_to_datetime(_time) if _time else None,
1642 'locked_date': time_to_datetime(_time) if _time else None,
1618 'lock_reason': _reason if _reason else None,
1643 'lock_reason': _reason if _reason else None,
1619 }
1644 }
1620
1645
1621 # TODO: mikhail: should be per-repo settings here
1646 # TODO: mikhail: should be per-repo settings here
1622 rc_config = SettingsModel().get_all_settings()
1647 rc_config = SettingsModel().get_all_settings()
1623 repository_fields = str2bool(
1648 repository_fields = str2bool(
1624 rc_config.get('rhodecode_repository_fields'))
1649 rc_config.get('rhodecode_repository_fields'))
1625 if repository_fields:
1650 if repository_fields:
1626 for f in self.extra_fields:
1651 for f in self.extra_fields:
1627 data[f.field_key_prefixed] = f.field_value
1652 data[f.field_key_prefixed] = f.field_value
1628
1653
1629 return data
1654 return data
1630
1655
1631 @classmethod
1656 @classmethod
1632 def lock(cls, repo, user_id, lock_time=None, lock_reason=None):
1657 def lock(cls, repo, user_id, lock_time=None, lock_reason=None):
1633 if not lock_time:
1658 if not lock_time:
1634 lock_time = time.time()
1659 lock_time = time.time()
1635 if not lock_reason:
1660 if not lock_reason:
1636 lock_reason = cls.LOCK_AUTOMATIC
1661 lock_reason = cls.LOCK_AUTOMATIC
1637 repo.locked = [user_id, lock_time, lock_reason]
1662 repo.locked = [user_id, lock_time, lock_reason]
1638 Session().add(repo)
1663 Session().add(repo)
1639 Session().commit()
1664 Session().commit()
1640
1665
1641 @classmethod
1666 @classmethod
1642 def unlock(cls, repo):
1667 def unlock(cls, repo):
1643 repo.locked = None
1668 repo.locked = None
1644 Session().add(repo)
1669 Session().add(repo)
1645 Session().commit()
1670 Session().commit()
1646
1671
1647 @classmethod
1672 @classmethod
1648 def getlock(cls, repo):
1673 def getlock(cls, repo):
1649 return repo.locked
1674 return repo.locked
1650
1675
1651 def is_user_lock(self, user_id):
1676 def is_user_lock(self, user_id):
1652 if self.lock[0]:
1677 if self.lock[0]:
1653 lock_user_id = safe_int(self.lock[0])
1678 lock_user_id = safe_int(self.lock[0])
1654 user_id = safe_int(user_id)
1679 user_id = safe_int(user_id)
1655 # both are ints, and they are equal
1680 # both are ints, and they are equal
1656 return all([lock_user_id, user_id]) and lock_user_id == user_id
1681 return all([lock_user_id, user_id]) and lock_user_id == user_id
1657
1682
1658 return False
1683 return False
1659
1684
1660 def get_locking_state(self, action, user_id, only_when_enabled=True):
1685 def get_locking_state(self, action, user_id, only_when_enabled=True):
1661 """
1686 """
1662 Checks locking on this repository, if locking is enabled and lock is
1687 Checks locking on this repository, if locking is enabled and lock is
1663 present returns a tuple of make_lock, locked, locked_by.
1688 present returns a tuple of make_lock, locked, locked_by.
1664 make_lock can have 3 states None (do nothing) True, make lock
1689 make_lock can have 3 states None (do nothing) True, make lock
1665 False release lock, This value is later propagated to hooks, which
1690 False release lock, This value is later propagated to hooks, which
1666 do the locking. Think about this as signals passed to hooks what to do.
1691 do the locking. Think about this as signals passed to hooks what to do.
1667
1692
1668 """
1693 """
1669 # TODO: johbo: This is part of the business logic and should be moved
1694 # TODO: johbo: This is part of the business logic and should be moved
1670 # into the RepositoryModel.
1695 # into the RepositoryModel.
1671
1696
1672 if action not in ('push', 'pull'):
1697 if action not in ('push', 'pull'):
1673 raise ValueError("Invalid action value: %s" % repr(action))
1698 raise ValueError("Invalid action value: %s" % repr(action))
1674
1699
1675 # defines if locked error should be thrown to user
1700 # defines if locked error should be thrown to user
1676 currently_locked = False
1701 currently_locked = False
1677 # defines if new lock should be made, tri-state
1702 # defines if new lock should be made, tri-state
1678 make_lock = None
1703 make_lock = None
1679 repo = self
1704 repo = self
1680 user = User.get(user_id)
1705 user = User.get(user_id)
1681
1706
1682 lock_info = repo.locked
1707 lock_info = repo.locked
1683
1708
1684 if repo and (repo.enable_locking or not only_when_enabled):
1709 if repo and (repo.enable_locking or not only_when_enabled):
1685 if action == 'push':
1710 if action == 'push':
1686 # check if it's already locked !, if it is compare users
1711 # check if it's already locked !, if it is compare users
1687 locked_by_user_id = lock_info[0]
1712 locked_by_user_id = lock_info[0]
1688 if user.user_id == locked_by_user_id:
1713 if user.user_id == locked_by_user_id:
1689 log.debug(
1714 log.debug(
1690 'Got `push` action from user %s, now unlocking', user)
1715 'Got `push` action from user %s, now unlocking', user)
1691 # unlock if we have push from user who locked
1716 # unlock if we have push from user who locked
1692 make_lock = False
1717 make_lock = False
1693 else:
1718 else:
1694 # we're not the same user who locked, ban with
1719 # we're not the same user who locked, ban with
1695 # code defined in settings (default is 423 HTTP Locked) !
1720 # code defined in settings (default is 423 HTTP Locked) !
1696 log.debug('Repo %s is currently locked by %s', repo, user)
1721 log.debug('Repo %s is currently locked by %s', repo, user)
1697 currently_locked = True
1722 currently_locked = True
1698 elif action == 'pull':
1723 elif action == 'pull':
1699 # [0] user [1] date
1724 # [0] user [1] date
1700 if lock_info[0] and lock_info[1]:
1725 if lock_info[0] and lock_info[1]:
1701 log.debug('Repo %s is currently locked by %s', repo, user)
1726 log.debug('Repo %s is currently locked by %s', repo, user)
1702 currently_locked = True
1727 currently_locked = True
1703 else:
1728 else:
1704 log.debug('Setting lock on repo %s by %s', repo, user)
1729 log.debug('Setting lock on repo %s by %s', repo, user)
1705 make_lock = True
1730 make_lock = True
1706
1731
1707 else:
1732 else:
1708 log.debug('Repository %s do not have locking enabled', repo)
1733 log.debug('Repository %s do not have locking enabled', repo)
1709
1734
1710 log.debug('FINAL locking values make_lock:%s,locked:%s,locked_by:%s',
1735 log.debug('FINAL locking values make_lock:%s,locked:%s,locked_by:%s',
1711 make_lock, currently_locked, lock_info)
1736 make_lock, currently_locked, lock_info)
1712
1737
1713 from rhodecode.lib.auth import HasRepoPermissionAny
1738 from rhodecode.lib.auth import HasRepoPermissionAny
1714 perm_check = HasRepoPermissionAny('repository.write', 'repository.admin')
1739 perm_check = HasRepoPermissionAny('repository.write', 'repository.admin')
1715 if make_lock and not perm_check(repo_name=repo.repo_name, user=user):
1740 if make_lock and not perm_check(repo_name=repo.repo_name, user=user):
1716 # if we don't have at least write permission we cannot make a lock
1741 # if we don't have at least write permission we cannot make a lock
1717 log.debug('lock state reset back to FALSE due to lack '
1742 log.debug('lock state reset back to FALSE due to lack '
1718 'of at least read permission')
1743 'of at least read permission')
1719 make_lock = False
1744 make_lock = False
1720
1745
1721 return make_lock, currently_locked, lock_info
1746 return make_lock, currently_locked, lock_info
1722
1747
1723 @property
1748 @property
1724 def last_db_change(self):
1749 def last_db_change(self):
1725 return self.updated_on
1750 return self.updated_on
1726
1751
1727 @property
1752 @property
1728 def clone_uri_hidden(self):
1753 def clone_uri_hidden(self):
1729 clone_uri = self.clone_uri
1754 clone_uri = self.clone_uri
1730 if clone_uri:
1755 if clone_uri:
1731 import urlobject
1756 import urlobject
1732 url_obj = urlobject.URLObject(self.clone_uri)
1757 url_obj = urlobject.URLObject(self.clone_uri)
1733 if url_obj.password:
1758 if url_obj.password:
1734 clone_uri = url_obj.with_password('*****')
1759 clone_uri = url_obj.with_password('*****')
1735 return clone_uri
1760 return clone_uri
1736
1761
1737 def clone_url(self, **override):
1762 def clone_url(self, **override):
1738 qualified_home_url = url('home', qualified=True)
1763 qualified_home_url = url('home', qualified=True)
1739
1764
1740 uri_tmpl = None
1765 uri_tmpl = None
1741 if 'with_id' in override:
1766 if 'with_id' in override:
1742 uri_tmpl = self.DEFAULT_CLONE_URI_ID
1767 uri_tmpl = self.DEFAULT_CLONE_URI_ID
1743 del override['with_id']
1768 del override['with_id']
1744
1769
1745 if 'uri_tmpl' in override:
1770 if 'uri_tmpl' in override:
1746 uri_tmpl = override['uri_tmpl']
1771 uri_tmpl = override['uri_tmpl']
1747 del override['uri_tmpl']
1772 del override['uri_tmpl']
1748
1773
1749 # we didn't override our tmpl from **overrides
1774 # we didn't override our tmpl from **overrides
1750 if not uri_tmpl:
1775 if not uri_tmpl:
1751 uri_tmpl = self.DEFAULT_CLONE_URI
1776 uri_tmpl = self.DEFAULT_CLONE_URI
1752 try:
1777 try:
1753 from pylons import tmpl_context as c
1778 from pylons import tmpl_context as c
1754 uri_tmpl = c.clone_uri_tmpl
1779 uri_tmpl = c.clone_uri_tmpl
1755 except Exception:
1780 except Exception:
1756 # in any case if we call this outside of request context,
1781 # in any case if we call this outside of request context,
1757 # ie, not having tmpl_context set up
1782 # ie, not having tmpl_context set up
1758 pass
1783 pass
1759
1784
1760 return get_clone_url(uri_tmpl=uri_tmpl,
1785 return get_clone_url(uri_tmpl=uri_tmpl,
1761 qualifed_home_url=qualified_home_url,
1786 qualifed_home_url=qualified_home_url,
1762 repo_name=self.repo_name,
1787 repo_name=self.repo_name,
1763 repo_id=self.repo_id, **override)
1788 repo_id=self.repo_id, **override)
1764
1789
1765 def set_state(self, state):
1790 def set_state(self, state):
1766 self.repo_state = state
1791 self.repo_state = state
1767 Session().add(self)
1792 Session().add(self)
1768 #==========================================================================
1793 #==========================================================================
1769 # SCM PROPERTIES
1794 # SCM PROPERTIES
1770 #==========================================================================
1795 #==========================================================================
1771
1796
1772 def get_commit(self, commit_id=None, commit_idx=None, pre_load=None):
1797 def get_commit(self, commit_id=None, commit_idx=None, pre_load=None):
1773 return get_commit_safe(
1798 return get_commit_safe(
1774 self.scm_instance(), commit_id, commit_idx, pre_load=pre_load)
1799 self.scm_instance(), commit_id, commit_idx, pre_load=pre_load)
1775
1800
1776 def get_changeset(self, rev=None, pre_load=None):
1801 def get_changeset(self, rev=None, pre_load=None):
1777 warnings.warn("Use get_commit", DeprecationWarning)
1802 warnings.warn("Use get_commit", DeprecationWarning)
1778 commit_id = None
1803 commit_id = None
1779 commit_idx = None
1804 commit_idx = None
1780 if isinstance(rev, basestring):
1805 if isinstance(rev, basestring):
1781 commit_id = rev
1806 commit_id = rev
1782 else:
1807 else:
1783 commit_idx = rev
1808 commit_idx = rev
1784 return self.get_commit(commit_id=commit_id, commit_idx=commit_idx,
1809 return self.get_commit(commit_id=commit_id, commit_idx=commit_idx,
1785 pre_load=pre_load)
1810 pre_load=pre_load)
1786
1811
1787 def get_landing_commit(self):
1812 def get_landing_commit(self):
1788 """
1813 """
1789 Returns landing commit, or if that doesn't exist returns the tip
1814 Returns landing commit, or if that doesn't exist returns the tip
1790 """
1815 """
1791 _rev_type, _rev = self.landing_rev
1816 _rev_type, _rev = self.landing_rev
1792 commit = self.get_commit(_rev)
1817 commit = self.get_commit(_rev)
1793 if isinstance(commit, EmptyCommit):
1818 if isinstance(commit, EmptyCommit):
1794 return self.get_commit()
1819 return self.get_commit()
1795 return commit
1820 return commit
1796
1821
1797 def update_commit_cache(self, cs_cache=None, config=None):
1822 def update_commit_cache(self, cs_cache=None, config=None):
1798 """
1823 """
1799 Update cache of last changeset for repository, keys should be::
1824 Update cache of last changeset for repository, keys should be::
1800
1825
1801 short_id
1826 short_id
1802 raw_id
1827 raw_id
1803 revision
1828 revision
1804 parents
1829 parents
1805 message
1830 message
1806 date
1831 date
1807 author
1832 author
1808
1833
1809 :param cs_cache:
1834 :param cs_cache:
1810 """
1835 """
1811 from rhodecode.lib.vcs.backends.base import BaseChangeset
1836 from rhodecode.lib.vcs.backends.base import BaseChangeset
1812 if cs_cache is None:
1837 if cs_cache is None:
1813 # use no-cache version here
1838 # use no-cache version here
1814 scm_repo = self.scm_instance(cache=False, config=config)
1839 scm_repo = self.scm_instance(cache=False, config=config)
1815 if scm_repo:
1840 if scm_repo:
1816 cs_cache = scm_repo.get_commit(
1841 cs_cache = scm_repo.get_commit(
1817 pre_load=["author", "date", "message", "parents"])
1842 pre_load=["author", "date", "message", "parents"])
1818 else:
1843 else:
1819 cs_cache = EmptyCommit()
1844 cs_cache = EmptyCommit()
1820
1845
1821 if isinstance(cs_cache, BaseChangeset):
1846 if isinstance(cs_cache, BaseChangeset):
1822 cs_cache = cs_cache.__json__()
1847 cs_cache = cs_cache.__json__()
1823
1848
1824 def is_outdated(new_cs_cache):
1849 def is_outdated(new_cs_cache):
1825 if new_cs_cache['raw_id'] != self.changeset_cache['raw_id']:
1850 if new_cs_cache['raw_id'] != self.changeset_cache['raw_id']:
1826 return True
1851 return True
1827 return False
1852 return False
1828
1853
1829 # check if we have maybe already latest cached revision
1854 # check if we have maybe already latest cached revision
1830 if is_outdated(cs_cache) or not self.changeset_cache:
1855 if is_outdated(cs_cache) or not self.changeset_cache:
1831 _default = datetime.datetime.fromtimestamp(0)
1856 _default = datetime.datetime.fromtimestamp(0)
1832 last_change = cs_cache.get('date') or _default
1857 last_change = cs_cache.get('date') or _default
1833 log.debug('updated repo %s with new cs cache %s',
1858 log.debug('updated repo %s with new cs cache %s',
1834 self.repo_name, cs_cache)
1859 self.repo_name, cs_cache)
1835 self.updated_on = last_change
1860 self.updated_on = last_change
1836 self.changeset_cache = cs_cache
1861 self.changeset_cache = cs_cache
1837 Session().add(self)
1862 Session().add(self)
1838 Session().commit()
1863 Session().commit()
1839 else:
1864 else:
1840 log.debug('Skipping update_commit_cache for repo:`%s` '
1865 log.debug('Skipping update_commit_cache for repo:`%s` '
1841 'commit already with latest changes', self.repo_name)
1866 'commit already with latest changes', self.repo_name)
1842
1867
1843 @property
1868 @property
1844 def tip(self):
1869 def tip(self):
1845 return self.get_commit('tip')
1870 return self.get_commit('tip')
1846
1871
1847 @property
1872 @property
1848 def author(self):
1873 def author(self):
1849 return self.tip.author
1874 return self.tip.author
1850
1875
1851 @property
1876 @property
1852 def last_change(self):
1877 def last_change(self):
1853 return self.scm_instance().last_change
1878 return self.scm_instance().last_change
1854
1879
1855 def get_comments(self, revisions=None):
1880 def get_comments(self, revisions=None):
1856 """
1881 """
1857 Returns comments for this repository grouped by revisions
1882 Returns comments for this repository grouped by revisions
1858
1883
1859 :param revisions: filter query by revisions only
1884 :param revisions: filter query by revisions only
1860 """
1885 """
1861 cmts = ChangesetComment.query()\
1886 cmts = ChangesetComment.query()\
1862 .filter(ChangesetComment.repo == self)
1887 .filter(ChangesetComment.repo == self)
1863 if revisions:
1888 if revisions:
1864 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
1889 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
1865 grouped = collections.defaultdict(list)
1890 grouped = collections.defaultdict(list)
1866 for cmt in cmts.all():
1891 for cmt in cmts.all():
1867 grouped[cmt.revision].append(cmt)
1892 grouped[cmt.revision].append(cmt)
1868 return grouped
1893 return grouped
1869
1894
1870 def statuses(self, revisions=None):
1895 def statuses(self, revisions=None):
1871 """
1896 """
1872 Returns statuses for this repository
1897 Returns statuses for this repository
1873
1898
1874 :param revisions: list of revisions to get statuses for
1899 :param revisions: list of revisions to get statuses for
1875 """
1900 """
1876 statuses = ChangesetStatus.query()\
1901 statuses = ChangesetStatus.query()\
1877 .filter(ChangesetStatus.repo == self)\
1902 .filter(ChangesetStatus.repo == self)\
1878 .filter(ChangesetStatus.version == 0)
1903 .filter(ChangesetStatus.version == 0)
1879
1904
1880 if revisions:
1905 if revisions:
1881 # Try doing the filtering in chunks to avoid hitting limits
1906 # Try doing the filtering in chunks to avoid hitting limits
1882 size = 500
1907 size = 500
1883 status_results = []
1908 status_results = []
1884 for chunk in xrange(0, len(revisions), size):
1909 for chunk in xrange(0, len(revisions), size):
1885 status_results += statuses.filter(
1910 status_results += statuses.filter(
1886 ChangesetStatus.revision.in_(
1911 ChangesetStatus.revision.in_(
1887 revisions[chunk: chunk+size])
1912 revisions[chunk: chunk+size])
1888 ).all()
1913 ).all()
1889 else:
1914 else:
1890 status_results = statuses.all()
1915 status_results = statuses.all()
1891
1916
1892 grouped = {}
1917 grouped = {}
1893
1918
1894 # maybe we have open new pullrequest without a status?
1919 # maybe we have open new pullrequest without a status?
1895 stat = ChangesetStatus.STATUS_UNDER_REVIEW
1920 stat = ChangesetStatus.STATUS_UNDER_REVIEW
1896 status_lbl = ChangesetStatus.get_status_lbl(stat)
1921 status_lbl = ChangesetStatus.get_status_lbl(stat)
1897 for pr in PullRequest.query().filter(PullRequest.source_repo == self).all():
1922 for pr in PullRequest.query().filter(PullRequest.source_repo == self).all():
1898 for rev in pr.revisions:
1923 for rev in pr.revisions:
1899 pr_id = pr.pull_request_id
1924 pr_id = pr.pull_request_id
1900 pr_repo = pr.target_repo.repo_name
1925 pr_repo = pr.target_repo.repo_name
1901 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
1926 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
1902
1927
1903 for stat in status_results:
1928 for stat in status_results:
1904 pr_id = pr_repo = None
1929 pr_id = pr_repo = None
1905 if stat.pull_request:
1930 if stat.pull_request:
1906 pr_id = stat.pull_request.pull_request_id
1931 pr_id = stat.pull_request.pull_request_id
1907 pr_repo = stat.pull_request.target_repo.repo_name
1932 pr_repo = stat.pull_request.target_repo.repo_name
1908 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
1933 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
1909 pr_id, pr_repo]
1934 pr_id, pr_repo]
1910 return grouped
1935 return grouped
1911
1936
1912 # ==========================================================================
1937 # ==========================================================================
1913 # SCM CACHE INSTANCE
1938 # SCM CACHE INSTANCE
1914 # ==========================================================================
1939 # ==========================================================================
1915
1940
1916 def scm_instance(self, **kwargs):
1941 def scm_instance(self, **kwargs):
1917 import rhodecode
1942 import rhodecode
1918
1943
1919 # Passing a config will not hit the cache currently only used
1944 # Passing a config will not hit the cache currently only used
1920 # for repo2dbmapper
1945 # for repo2dbmapper
1921 config = kwargs.pop('config', None)
1946 config = kwargs.pop('config', None)
1922 cache = kwargs.pop('cache', None)
1947 cache = kwargs.pop('cache', None)
1923 full_cache = str2bool(rhodecode.CONFIG.get('vcs_full_cache'))
1948 full_cache = str2bool(rhodecode.CONFIG.get('vcs_full_cache'))
1924 # if cache is NOT defined use default global, else we have a full
1949 # if cache is NOT defined use default global, else we have a full
1925 # control over cache behaviour
1950 # control over cache behaviour
1926 if cache is None and full_cache and not config:
1951 if cache is None and full_cache and not config:
1927 return self._get_instance_cached()
1952 return self._get_instance_cached()
1928 return self._get_instance(cache=bool(cache), config=config)
1953 return self._get_instance(cache=bool(cache), config=config)
1929
1954
1930 def _get_instance_cached(self):
1955 def _get_instance_cached(self):
1931 @cache_region('long_term')
1956 @cache_region('long_term')
1932 def _get_repo(cache_key):
1957 def _get_repo(cache_key):
1933 return self._get_instance()
1958 return self._get_instance()
1934
1959
1935 invalidator_context = CacheKey.repo_context_cache(
1960 invalidator_context = CacheKey.repo_context_cache(
1936 _get_repo, self.repo_name, None)
1961 _get_repo, self.repo_name, None)
1937
1962
1938 with invalidator_context as context:
1963 with invalidator_context as context:
1939 context.invalidate()
1964 context.invalidate()
1940 repo = context.compute()
1965 repo = context.compute()
1941
1966
1942 return repo
1967 return repo
1943
1968
1944 def _get_instance(self, cache=True, config=None):
1969 def _get_instance(self, cache=True, config=None):
1945 repo_full_path = self.repo_full_path
1970 repo_full_path = self.repo_full_path
1946 try:
1971 try:
1947 vcs_alias = get_scm(repo_full_path)[0]
1972 vcs_alias = get_scm(repo_full_path)[0]
1948 log.debug(
1973 log.debug(
1949 'Creating instance of %s repository from %s',
1974 'Creating instance of %s repository from %s',
1950 vcs_alias, repo_full_path)
1975 vcs_alias, repo_full_path)
1951 backend = get_backend(vcs_alias)
1976 backend = get_backend(vcs_alias)
1952 except VCSError:
1977 except VCSError:
1953 log.exception(
1978 log.exception(
1954 'Perhaps this repository is in db and not in '
1979 'Perhaps this repository is in db and not in '
1955 'filesystem run rescan repositories with '
1980 'filesystem run rescan repositories with '
1956 '"destroy old data" option from admin panel')
1981 '"destroy old data" option from admin panel')
1957 return
1982 return
1958
1983
1959 config = config or self._config
1984 config = config or self._config
1960 custom_wire = {
1985 custom_wire = {
1961 'cache': cache # controls the vcs.remote cache
1986 'cache': cache # controls the vcs.remote cache
1962 }
1987 }
1963 repo = backend(
1988 repo = backend(
1964 safe_str(repo_full_path), config=config, create=False,
1989 safe_str(repo_full_path), config=config, create=False,
1965 with_wire=custom_wire)
1990 with_wire=custom_wire)
1966
1991
1967 return repo
1992 return repo
1968
1993
1969 def __json__(self):
1994 def __json__(self):
1970 return {'landing_rev': self.landing_rev}
1995 return {'landing_rev': self.landing_rev}
1971
1996
1972 def get_dict(self):
1997 def get_dict(self):
1973
1998
1974 # Since we transformed `repo_name` to a hybrid property, we need to
1999 # Since we transformed `repo_name` to a hybrid property, we need to
1975 # keep compatibility with the code which uses `repo_name` field.
2000 # keep compatibility with the code which uses `repo_name` field.
1976
2001
1977 result = super(Repository, self).get_dict()
2002 result = super(Repository, self).get_dict()
1978 result['repo_name'] = result.pop('_repo_name', None)
2003 result['repo_name'] = result.pop('_repo_name', None)
1979 return result
2004 return result
1980
2005
1981
2006
1982 class RepoGroup(Base, BaseModel):
2007 class RepoGroup(Base, BaseModel):
1983 __tablename__ = 'groups'
2008 __tablename__ = 'groups'
1984 __table_args__ = (
2009 __table_args__ = (
1985 UniqueConstraint('group_name', 'group_parent_id'),
2010 UniqueConstraint('group_name', 'group_parent_id'),
1986 CheckConstraint('group_id != group_parent_id'),
2011 CheckConstraint('group_id != group_parent_id'),
1987 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2012 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1988 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
2013 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1989 )
2014 )
1990 __mapper_args__ = {'order_by': 'group_name'}
2015 __mapper_args__ = {'order_by': 'group_name'}
1991
2016
1992 CHOICES_SEPARATOR = '/' # used to generate select2 choices for nested groups
2017 CHOICES_SEPARATOR = '/' # used to generate select2 choices for nested groups
1993
2018
1994 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2019 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1995 group_name = Column("group_name", String(255), nullable=False, unique=True, default=None)
2020 group_name = Column("group_name", String(255), nullable=False, unique=True, default=None)
1996 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
2021 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
1997 group_description = Column("group_description", String(10000), nullable=True, unique=None, default=None)
2022 group_description = Column("group_description", String(10000), nullable=True, unique=None, default=None)
1998 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
2023 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
1999 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
2024 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
2000 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
2025 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
2001
2026
2002 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
2027 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
2003 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
2028 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
2004 parent_group = relationship('RepoGroup', remote_side=group_id)
2029 parent_group = relationship('RepoGroup', remote_side=group_id)
2005 user = relationship('User')
2030 user = relationship('User')
2006
2031
2007 def __init__(self, group_name='', parent_group=None):
2032 def __init__(self, group_name='', parent_group=None):
2008 self.group_name = group_name
2033 self.group_name = group_name
2009 self.parent_group = parent_group
2034 self.parent_group = parent_group
2010
2035
2011 def __unicode__(self):
2036 def __unicode__(self):
2012 return u"<%s('id:%s:%s')>" % (self.__class__.__name__, self.group_id,
2037 return u"<%s('id:%s:%s')>" % (self.__class__.__name__, self.group_id,
2013 self.group_name)
2038 self.group_name)
2014
2039
2015 @classmethod
2040 @classmethod
2016 def _generate_choice(cls, repo_group):
2041 def _generate_choice(cls, repo_group):
2017 from webhelpers.html import literal as _literal
2042 from webhelpers.html import literal as _literal
2018 _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k))
2043 _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k))
2019 return repo_group.group_id, _name(repo_group.full_path_splitted)
2044 return repo_group.group_id, _name(repo_group.full_path_splitted)
2020
2045
2021 @classmethod
2046 @classmethod
2022 def groups_choices(cls, groups=None, show_empty_group=True):
2047 def groups_choices(cls, groups=None, show_empty_group=True):
2023 if not groups:
2048 if not groups:
2024 groups = cls.query().all()
2049 groups = cls.query().all()
2025
2050
2026 repo_groups = []
2051 repo_groups = []
2027 if show_empty_group:
2052 if show_empty_group:
2028 repo_groups = [('-1', u'-- %s --' % _('No parent'))]
2053 repo_groups = [('-1', u'-- %s --' % _('No parent'))]
2029
2054
2030 repo_groups.extend([cls._generate_choice(x) for x in groups])
2055 repo_groups.extend([cls._generate_choice(x) for x in groups])
2031
2056
2032 repo_groups = sorted(
2057 repo_groups = sorted(
2033 repo_groups, key=lambda t: t[1].split(cls.CHOICES_SEPARATOR)[0])
2058 repo_groups, key=lambda t: t[1].split(cls.CHOICES_SEPARATOR)[0])
2034 return repo_groups
2059 return repo_groups
2035
2060
2036 @classmethod
2061 @classmethod
2037 def url_sep(cls):
2062 def url_sep(cls):
2038 return URL_SEP
2063 return URL_SEP
2039
2064
2040 @classmethod
2065 @classmethod
2041 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
2066 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
2042 if case_insensitive:
2067 if case_insensitive:
2043 gr = cls.query().filter(func.lower(cls.group_name)
2068 gr = cls.query().filter(func.lower(cls.group_name)
2044 == func.lower(group_name))
2069 == func.lower(group_name))
2045 else:
2070 else:
2046 gr = cls.query().filter(cls.group_name == group_name)
2071 gr = cls.query().filter(cls.group_name == group_name)
2047 if cache:
2072 if cache:
2048 gr = gr.options(FromCache(
2073 gr = gr.options(FromCache(
2049 "sql_cache_short",
2074 "sql_cache_short",
2050 "get_group_%s" % _hash_key(group_name)))
2075 "get_group_%s" % _hash_key(group_name)))
2051 return gr.scalar()
2076 return gr.scalar()
2052
2077
2053 @classmethod
2078 @classmethod
2054 def get_all_repo_groups(cls, user_id=Optional(None), group_id=Optional(None),
2079 def get_all_repo_groups(cls, user_id=Optional(None), group_id=Optional(None),
2055 case_insensitive=True):
2080 case_insensitive=True):
2056 q = RepoGroup.query()
2081 q = RepoGroup.query()
2057
2082
2058 if not isinstance(user_id, Optional):
2083 if not isinstance(user_id, Optional):
2059 q = q.filter(RepoGroup.user_id == user_id)
2084 q = q.filter(RepoGroup.user_id == user_id)
2060
2085
2061 if not isinstance(group_id, Optional):
2086 if not isinstance(group_id, Optional):
2062 q = q.filter(RepoGroup.group_parent_id == group_id)
2087 q = q.filter(RepoGroup.group_parent_id == group_id)
2063
2088
2064 if case_insensitive:
2089 if case_insensitive:
2065 q = q.order_by(func.lower(RepoGroup.group_name))
2090 q = q.order_by(func.lower(RepoGroup.group_name))
2066 else:
2091 else:
2067 q = q.order_by(RepoGroup.group_name)
2092 q = q.order_by(RepoGroup.group_name)
2068 return q.all()
2093 return q.all()
2069
2094
2070 @property
2095 @property
2071 def parents(self):
2096 def parents(self):
2072 parents_recursion_limit = 10
2097 parents_recursion_limit = 10
2073 groups = []
2098 groups = []
2074 if self.parent_group is None:
2099 if self.parent_group is None:
2075 return groups
2100 return groups
2076 cur_gr = self.parent_group
2101 cur_gr = self.parent_group
2077 groups.insert(0, cur_gr)
2102 groups.insert(0, cur_gr)
2078 cnt = 0
2103 cnt = 0
2079 while 1:
2104 while 1:
2080 cnt += 1
2105 cnt += 1
2081 gr = getattr(cur_gr, 'parent_group', None)
2106 gr = getattr(cur_gr, 'parent_group', None)
2082 cur_gr = cur_gr.parent_group
2107 cur_gr = cur_gr.parent_group
2083 if gr is None:
2108 if gr is None:
2084 break
2109 break
2085 if cnt == parents_recursion_limit:
2110 if cnt == parents_recursion_limit:
2086 # this will prevent accidental infinit loops
2111 # this will prevent accidental infinit loops
2087 log.error(('more than %s parents found for group %s, stopping '
2112 log.error(('more than %s parents found for group %s, stopping '
2088 'recursive parent fetching' % (parents_recursion_limit, self)))
2113 'recursive parent fetching' % (parents_recursion_limit, self)))
2089 break
2114 break
2090
2115
2091 groups.insert(0, gr)
2116 groups.insert(0, gr)
2092 return groups
2117 return groups
2093
2118
2094 @property
2119 @property
2095 def children(self):
2120 def children(self):
2096 return RepoGroup.query().filter(RepoGroup.parent_group == self)
2121 return RepoGroup.query().filter(RepoGroup.parent_group == self)
2097
2122
2098 @property
2123 @property
2099 def name(self):
2124 def name(self):
2100 return self.group_name.split(RepoGroup.url_sep())[-1]
2125 return self.group_name.split(RepoGroup.url_sep())[-1]
2101
2126
2102 @property
2127 @property
2103 def full_path(self):
2128 def full_path(self):
2104 return self.group_name
2129 return self.group_name
2105
2130
2106 @property
2131 @property
2107 def full_path_splitted(self):
2132 def full_path_splitted(self):
2108 return self.group_name.split(RepoGroup.url_sep())
2133 return self.group_name.split(RepoGroup.url_sep())
2109
2134
2110 @property
2135 @property
2111 def repositories(self):
2136 def repositories(self):
2112 return Repository.query()\
2137 return Repository.query()\
2113 .filter(Repository.group == self)\
2138 .filter(Repository.group == self)\
2114 .order_by(Repository.repo_name)
2139 .order_by(Repository.repo_name)
2115
2140
2116 @property
2141 @property
2117 def repositories_recursive_count(self):
2142 def repositories_recursive_count(self):
2118 cnt = self.repositories.count()
2143 cnt = self.repositories.count()
2119
2144
2120 def children_count(group):
2145 def children_count(group):
2121 cnt = 0
2146 cnt = 0
2122 for child in group.children:
2147 for child in group.children:
2123 cnt += child.repositories.count()
2148 cnt += child.repositories.count()
2124 cnt += children_count(child)
2149 cnt += children_count(child)
2125 return cnt
2150 return cnt
2126
2151
2127 return cnt + children_count(self)
2152 return cnt + children_count(self)
2128
2153
2129 def _recursive_objects(self, include_repos=True):
2154 def _recursive_objects(self, include_repos=True):
2130 all_ = []
2155 all_ = []
2131
2156
2132 def _get_members(root_gr):
2157 def _get_members(root_gr):
2133 if include_repos:
2158 if include_repos:
2134 for r in root_gr.repositories:
2159 for r in root_gr.repositories:
2135 all_.append(r)
2160 all_.append(r)
2136 childs = root_gr.children.all()
2161 childs = root_gr.children.all()
2137 if childs:
2162 if childs:
2138 for gr in childs:
2163 for gr in childs:
2139 all_.append(gr)
2164 all_.append(gr)
2140 _get_members(gr)
2165 _get_members(gr)
2141
2166
2142 _get_members(self)
2167 _get_members(self)
2143 return [self] + all_
2168 return [self] + all_
2144
2169
2145 def recursive_groups_and_repos(self):
2170 def recursive_groups_and_repos(self):
2146 """
2171 """
2147 Recursive return all groups, with repositories in those groups
2172 Recursive return all groups, with repositories in those groups
2148 """
2173 """
2149 return self._recursive_objects()
2174 return self._recursive_objects()
2150
2175
2151 def recursive_groups(self):
2176 def recursive_groups(self):
2152 """
2177 """
2153 Returns all children groups for this group including children of children
2178 Returns all children groups for this group including children of children
2154 """
2179 """
2155 return self._recursive_objects(include_repos=False)
2180 return self._recursive_objects(include_repos=False)
2156
2181
2157 def get_new_name(self, group_name):
2182 def get_new_name(self, group_name):
2158 """
2183 """
2159 returns new full group name based on parent and new name
2184 returns new full group name based on parent and new name
2160
2185
2161 :param group_name:
2186 :param group_name:
2162 """
2187 """
2163 path_prefix = (self.parent_group.full_path_splitted if
2188 path_prefix = (self.parent_group.full_path_splitted if
2164 self.parent_group else [])
2189 self.parent_group else [])
2165 return RepoGroup.url_sep().join(path_prefix + [group_name])
2190 return RepoGroup.url_sep().join(path_prefix + [group_name])
2166
2191
2167 def permissions(self, with_admins=True, with_owner=True):
2192 def permissions(self, with_admins=True, with_owner=True):
2168 q = UserRepoGroupToPerm.query().filter(UserRepoGroupToPerm.group == self)
2193 q = UserRepoGroupToPerm.query().filter(UserRepoGroupToPerm.group == self)
2169 q = q.options(joinedload(UserRepoGroupToPerm.group),
2194 q = q.options(joinedload(UserRepoGroupToPerm.group),
2170 joinedload(UserRepoGroupToPerm.user),
2195 joinedload(UserRepoGroupToPerm.user),
2171 joinedload(UserRepoGroupToPerm.permission),)
2196 joinedload(UserRepoGroupToPerm.permission),)
2172
2197
2173 # get owners and admins and permissions. We do a trick of re-writing
2198 # get owners and admins and permissions. We do a trick of re-writing
2174 # objects from sqlalchemy to named-tuples due to sqlalchemy session
2199 # objects from sqlalchemy to named-tuples due to sqlalchemy session
2175 # has a global reference and changing one object propagates to all
2200 # has a global reference and changing one object propagates to all
2176 # others. This means if admin is also an owner admin_row that change
2201 # others. This means if admin is also an owner admin_row that change
2177 # would propagate to both objects
2202 # would propagate to both objects
2178 perm_rows = []
2203 perm_rows = []
2179 for _usr in q.all():
2204 for _usr in q.all():
2180 usr = AttributeDict(_usr.user.get_dict())
2205 usr = AttributeDict(_usr.user.get_dict())
2181 usr.permission = _usr.permission.permission_name
2206 usr.permission = _usr.permission.permission_name
2182 perm_rows.append(usr)
2207 perm_rows.append(usr)
2183
2208
2184 # filter the perm rows by 'default' first and then sort them by
2209 # filter the perm rows by 'default' first and then sort them by
2185 # admin,write,read,none permissions sorted again alphabetically in
2210 # admin,write,read,none permissions sorted again alphabetically in
2186 # each group
2211 # each group
2187 perm_rows = sorted(perm_rows, key=display_sort)
2212 perm_rows = sorted(perm_rows, key=display_sort)
2188
2213
2189 _admin_perm = 'group.admin'
2214 _admin_perm = 'group.admin'
2190 owner_row = []
2215 owner_row = []
2191 if with_owner:
2216 if with_owner:
2192 usr = AttributeDict(self.user.get_dict())
2217 usr = AttributeDict(self.user.get_dict())
2193 usr.owner_row = True
2218 usr.owner_row = True
2194 usr.permission = _admin_perm
2219 usr.permission = _admin_perm
2195 owner_row.append(usr)
2220 owner_row.append(usr)
2196
2221
2197 super_admin_rows = []
2222 super_admin_rows = []
2198 if with_admins:
2223 if with_admins:
2199 for usr in User.get_all_super_admins():
2224 for usr in User.get_all_super_admins():
2200 # if this admin is also owner, don't double the record
2225 # if this admin is also owner, don't double the record
2201 if usr.user_id == owner_row[0].user_id:
2226 if usr.user_id == owner_row[0].user_id:
2202 owner_row[0].admin_row = True
2227 owner_row[0].admin_row = True
2203 else:
2228 else:
2204 usr = AttributeDict(usr.get_dict())
2229 usr = AttributeDict(usr.get_dict())
2205 usr.admin_row = True
2230 usr.admin_row = True
2206 usr.permission = _admin_perm
2231 usr.permission = _admin_perm
2207 super_admin_rows.append(usr)
2232 super_admin_rows.append(usr)
2208
2233
2209 return super_admin_rows + owner_row + perm_rows
2234 return super_admin_rows + owner_row + perm_rows
2210
2235
2211 def permission_user_groups(self):
2236 def permission_user_groups(self):
2212 q = UserGroupRepoGroupToPerm.query().filter(UserGroupRepoGroupToPerm.group == self)
2237 q = UserGroupRepoGroupToPerm.query().filter(UserGroupRepoGroupToPerm.group == self)
2213 q = q.options(joinedload(UserGroupRepoGroupToPerm.group),
2238 q = q.options(joinedload(UserGroupRepoGroupToPerm.group),
2214 joinedload(UserGroupRepoGroupToPerm.users_group),
2239 joinedload(UserGroupRepoGroupToPerm.users_group),
2215 joinedload(UserGroupRepoGroupToPerm.permission),)
2240 joinedload(UserGroupRepoGroupToPerm.permission),)
2216
2241
2217 perm_rows = []
2242 perm_rows = []
2218 for _user_group in q.all():
2243 for _user_group in q.all():
2219 usr = AttributeDict(_user_group.users_group.get_dict())
2244 usr = AttributeDict(_user_group.users_group.get_dict())
2220 usr.permission = _user_group.permission.permission_name
2245 usr.permission = _user_group.permission.permission_name
2221 perm_rows.append(usr)
2246 perm_rows.append(usr)
2222
2247
2223 return perm_rows
2248 return perm_rows
2224
2249
2225 def get_api_data(self):
2250 def get_api_data(self):
2226 """
2251 """
2227 Common function for generating api data
2252 Common function for generating api data
2228
2253
2229 """
2254 """
2230 group = self
2255 group = self
2231 data = {
2256 data = {
2232 'group_id': group.group_id,
2257 'group_id': group.group_id,
2233 'group_name': group.group_name,
2258 'group_name': group.group_name,
2234 'group_description': group.group_description,
2259 'group_description': group.group_description,
2235 'parent_group': group.parent_group.group_name if group.parent_group else None,
2260 'parent_group': group.parent_group.group_name if group.parent_group else None,
2236 'repositories': [x.repo_name for x in group.repositories],
2261 'repositories': [x.repo_name for x in group.repositories],
2237 'owner': group.user.username,
2262 'owner': group.user.username,
2238 }
2263 }
2239 return data
2264 return data
2240
2265
2241
2266
2242 class Permission(Base, BaseModel):
2267 class Permission(Base, BaseModel):
2243 __tablename__ = 'permissions'
2268 __tablename__ = 'permissions'
2244 __table_args__ = (
2269 __table_args__ = (
2245 Index('p_perm_name_idx', 'permission_name'),
2270 Index('p_perm_name_idx', 'permission_name'),
2246 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2271 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2247 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
2272 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
2248 )
2273 )
2249 PERMS = [
2274 PERMS = [
2250 ('hg.admin', _('RhodeCode Super Administrator')),
2275 ('hg.admin', _('RhodeCode Super Administrator')),
2251
2276
2252 ('repository.none', _('Repository no access')),
2277 ('repository.none', _('Repository no access')),
2253 ('repository.read', _('Repository read access')),
2278 ('repository.read', _('Repository read access')),
2254 ('repository.write', _('Repository write access')),
2279 ('repository.write', _('Repository write access')),
2255 ('repository.admin', _('Repository admin access')),
2280 ('repository.admin', _('Repository admin access')),
2256
2281
2257 ('group.none', _('Repository group no access')),
2282 ('group.none', _('Repository group no access')),
2258 ('group.read', _('Repository group read access')),
2283 ('group.read', _('Repository group read access')),
2259 ('group.write', _('Repository group write access')),
2284 ('group.write', _('Repository group write access')),
2260 ('group.admin', _('Repository group admin access')),
2285 ('group.admin', _('Repository group admin access')),
2261
2286
2262 ('usergroup.none', _('User group no access')),
2287 ('usergroup.none', _('User group no access')),
2263 ('usergroup.read', _('User group read access')),
2288 ('usergroup.read', _('User group read access')),
2264 ('usergroup.write', _('User group write access')),
2289 ('usergroup.write', _('User group write access')),
2265 ('usergroup.admin', _('User group admin access')),
2290 ('usergroup.admin', _('User group admin access')),
2266
2291
2267 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
2292 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
2268 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
2293 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
2269
2294
2270 ('hg.usergroup.create.false', _('User Group creation disabled')),
2295 ('hg.usergroup.create.false', _('User Group creation disabled')),
2271 ('hg.usergroup.create.true', _('User Group creation enabled')),
2296 ('hg.usergroup.create.true', _('User Group creation enabled')),
2272
2297
2273 ('hg.create.none', _('Repository creation disabled')),
2298 ('hg.create.none', _('Repository creation disabled')),
2274 ('hg.create.repository', _('Repository creation enabled')),
2299 ('hg.create.repository', _('Repository creation enabled')),
2275 ('hg.create.write_on_repogroup.true', _('Repository creation enabled with write permission to a repository group')),
2300 ('hg.create.write_on_repogroup.true', _('Repository creation enabled with write permission to a repository group')),
2276 ('hg.create.write_on_repogroup.false', _('Repository creation disabled with write permission to a repository group')),
2301 ('hg.create.write_on_repogroup.false', _('Repository creation disabled with write permission to a repository group')),
2277
2302
2278 ('hg.fork.none', _('Repository forking disabled')),
2303 ('hg.fork.none', _('Repository forking disabled')),
2279 ('hg.fork.repository', _('Repository forking enabled')),
2304 ('hg.fork.repository', _('Repository forking enabled')),
2280
2305
2281 ('hg.register.none', _('Registration disabled')),
2306 ('hg.register.none', _('Registration disabled')),
2282 ('hg.register.manual_activate', _('User Registration with manual account activation')),
2307 ('hg.register.manual_activate', _('User Registration with manual account activation')),
2283 ('hg.register.auto_activate', _('User Registration with automatic account activation')),
2308 ('hg.register.auto_activate', _('User Registration with automatic account activation')),
2284
2309
2285 ('hg.extern_activate.manual', _('Manual activation of external account')),
2310 ('hg.extern_activate.manual', _('Manual activation of external account')),
2286 ('hg.extern_activate.auto', _('Automatic activation of external account')),
2311 ('hg.extern_activate.auto', _('Automatic activation of external account')),
2287
2312
2288 ('hg.inherit_default_perms.false', _('Inherit object permissions from default user disabled')),
2313 ('hg.inherit_default_perms.false', _('Inherit object permissions from default user disabled')),
2289 ('hg.inherit_default_perms.true', _('Inherit object permissions from default user enabled')),
2314 ('hg.inherit_default_perms.true', _('Inherit object permissions from default user enabled')),
2290 ]
2315 ]
2291
2316
2292 # definition of system default permissions for DEFAULT user
2317 # definition of system default permissions for DEFAULT user
2293 DEFAULT_USER_PERMISSIONS = [
2318 DEFAULT_USER_PERMISSIONS = [
2294 'repository.read',
2319 'repository.read',
2295 'group.read',
2320 'group.read',
2296 'usergroup.read',
2321 'usergroup.read',
2297 'hg.create.repository',
2322 'hg.create.repository',
2298 'hg.repogroup.create.false',
2323 'hg.repogroup.create.false',
2299 'hg.usergroup.create.false',
2324 'hg.usergroup.create.false',
2300 'hg.create.write_on_repogroup.true',
2325 'hg.create.write_on_repogroup.true',
2301 'hg.fork.repository',
2326 'hg.fork.repository',
2302 'hg.register.manual_activate',
2327 'hg.register.manual_activate',
2303 'hg.extern_activate.auto',
2328 'hg.extern_activate.auto',
2304 'hg.inherit_default_perms.true',
2329 'hg.inherit_default_perms.true',
2305 ]
2330 ]
2306
2331
2307 # defines which permissions are more important higher the more important
2332 # defines which permissions are more important higher the more important
2308 # Weight defines which permissions are more important.
2333 # Weight defines which permissions are more important.
2309 # The higher number the more important.
2334 # The higher number the more important.
2310 PERM_WEIGHTS = {
2335 PERM_WEIGHTS = {
2311 'repository.none': 0,
2336 'repository.none': 0,
2312 'repository.read': 1,
2337 'repository.read': 1,
2313 'repository.write': 3,
2338 'repository.write': 3,
2314 'repository.admin': 4,
2339 'repository.admin': 4,
2315
2340
2316 'group.none': 0,
2341 'group.none': 0,
2317 'group.read': 1,
2342 'group.read': 1,
2318 'group.write': 3,
2343 'group.write': 3,
2319 'group.admin': 4,
2344 'group.admin': 4,
2320
2345
2321 'usergroup.none': 0,
2346 'usergroup.none': 0,
2322 'usergroup.read': 1,
2347 'usergroup.read': 1,
2323 'usergroup.write': 3,
2348 'usergroup.write': 3,
2324 'usergroup.admin': 4,
2349 'usergroup.admin': 4,
2325
2350
2326 'hg.repogroup.create.false': 0,
2351 'hg.repogroup.create.false': 0,
2327 'hg.repogroup.create.true': 1,
2352 'hg.repogroup.create.true': 1,
2328
2353
2329 'hg.usergroup.create.false': 0,
2354 'hg.usergroup.create.false': 0,
2330 'hg.usergroup.create.true': 1,
2355 'hg.usergroup.create.true': 1,
2331
2356
2332 'hg.fork.none': 0,
2357 'hg.fork.none': 0,
2333 'hg.fork.repository': 1,
2358 'hg.fork.repository': 1,
2334 'hg.create.none': 0,
2359 'hg.create.none': 0,
2335 'hg.create.repository': 1
2360 'hg.create.repository': 1
2336 }
2361 }
2337
2362
2338 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2363 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2339 permission_name = Column("permission_name", String(255), nullable=True, unique=None, default=None)
2364 permission_name = Column("permission_name", String(255), nullable=True, unique=None, default=None)
2340 permission_longname = Column("permission_longname", String(255), nullable=True, unique=None, default=None)
2365 permission_longname = Column("permission_longname", String(255), nullable=True, unique=None, default=None)
2341
2366
2342 def __unicode__(self):
2367 def __unicode__(self):
2343 return u"<%s('%s:%s')>" % (
2368 return u"<%s('%s:%s')>" % (
2344 self.__class__.__name__, self.permission_id, self.permission_name
2369 self.__class__.__name__, self.permission_id, self.permission_name
2345 )
2370 )
2346
2371
2347 @classmethod
2372 @classmethod
2348 def get_by_key(cls, key):
2373 def get_by_key(cls, key):
2349 return cls.query().filter(cls.permission_name == key).scalar()
2374 return cls.query().filter(cls.permission_name == key).scalar()
2350
2375
2351 @classmethod
2376 @classmethod
2352 def get_default_repo_perms(cls, user_id, repo_id=None):
2377 def get_default_repo_perms(cls, user_id, repo_id=None):
2353 q = Session().query(UserRepoToPerm, Repository, Permission)\
2378 q = Session().query(UserRepoToPerm, Repository, Permission)\
2354 .join((Permission, UserRepoToPerm.permission_id == Permission.permission_id))\
2379 .join((Permission, UserRepoToPerm.permission_id == Permission.permission_id))\
2355 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
2380 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
2356 .filter(UserRepoToPerm.user_id == user_id)
2381 .filter(UserRepoToPerm.user_id == user_id)
2357 if repo_id:
2382 if repo_id:
2358 q = q.filter(UserRepoToPerm.repository_id == repo_id)
2383 q = q.filter(UserRepoToPerm.repository_id == repo_id)
2359 return q.all()
2384 return q.all()
2360
2385
2361 @classmethod
2386 @classmethod
2362 def get_default_repo_perms_from_user_group(cls, user_id, repo_id=None):
2387 def get_default_repo_perms_from_user_group(cls, user_id, repo_id=None):
2363 q = Session().query(UserGroupRepoToPerm, Repository, Permission)\
2388 q = Session().query(UserGroupRepoToPerm, Repository, Permission)\
2364 .join(
2389 .join(
2365 Permission,
2390 Permission,
2366 UserGroupRepoToPerm.permission_id == Permission.permission_id)\
2391 UserGroupRepoToPerm.permission_id == Permission.permission_id)\
2367 .join(
2392 .join(
2368 Repository,
2393 Repository,
2369 UserGroupRepoToPerm.repository_id == Repository.repo_id)\
2394 UserGroupRepoToPerm.repository_id == Repository.repo_id)\
2370 .join(
2395 .join(
2371 UserGroup,
2396 UserGroup,
2372 UserGroupRepoToPerm.users_group_id ==
2397 UserGroupRepoToPerm.users_group_id ==
2373 UserGroup.users_group_id)\
2398 UserGroup.users_group_id)\
2374 .join(
2399 .join(
2375 UserGroupMember,
2400 UserGroupMember,
2376 UserGroupRepoToPerm.users_group_id ==
2401 UserGroupRepoToPerm.users_group_id ==
2377 UserGroupMember.users_group_id)\
2402 UserGroupMember.users_group_id)\
2378 .filter(
2403 .filter(
2379 UserGroupMember.user_id == user_id,
2404 UserGroupMember.user_id == user_id,
2380 UserGroup.users_group_active == true())
2405 UserGroup.users_group_active == true())
2381 if repo_id:
2406 if repo_id:
2382 q = q.filter(UserGroupRepoToPerm.repository_id == repo_id)
2407 q = q.filter(UserGroupRepoToPerm.repository_id == repo_id)
2383 return q.all()
2408 return q.all()
2384
2409
2385 @classmethod
2410 @classmethod
2386 def get_default_group_perms(cls, user_id, repo_group_id=None):
2411 def get_default_group_perms(cls, user_id, repo_group_id=None):
2387 q = Session().query(UserRepoGroupToPerm, RepoGroup, Permission)\
2412 q = Session().query(UserRepoGroupToPerm, RepoGroup, Permission)\
2388 .join((Permission, UserRepoGroupToPerm.permission_id == Permission.permission_id))\
2413 .join((Permission, UserRepoGroupToPerm.permission_id == Permission.permission_id))\
2389 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
2414 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
2390 .filter(UserRepoGroupToPerm.user_id == user_id)
2415 .filter(UserRepoGroupToPerm.user_id == user_id)
2391 if repo_group_id:
2416 if repo_group_id:
2392 q = q.filter(UserRepoGroupToPerm.group_id == repo_group_id)
2417 q = q.filter(UserRepoGroupToPerm.group_id == repo_group_id)
2393 return q.all()
2418 return q.all()
2394
2419
2395 @classmethod
2420 @classmethod
2396 def get_default_group_perms_from_user_group(
2421 def get_default_group_perms_from_user_group(
2397 cls, user_id, repo_group_id=None):
2422 cls, user_id, repo_group_id=None):
2398 q = Session().query(UserGroupRepoGroupToPerm, RepoGroup, Permission)\
2423 q = Session().query(UserGroupRepoGroupToPerm, RepoGroup, Permission)\
2399 .join(
2424 .join(
2400 Permission,
2425 Permission,
2401 UserGroupRepoGroupToPerm.permission_id ==
2426 UserGroupRepoGroupToPerm.permission_id ==
2402 Permission.permission_id)\
2427 Permission.permission_id)\
2403 .join(
2428 .join(
2404 RepoGroup,
2429 RepoGroup,
2405 UserGroupRepoGroupToPerm.group_id == RepoGroup.group_id)\
2430 UserGroupRepoGroupToPerm.group_id == RepoGroup.group_id)\
2406 .join(
2431 .join(
2407 UserGroup,
2432 UserGroup,
2408 UserGroupRepoGroupToPerm.users_group_id ==
2433 UserGroupRepoGroupToPerm.users_group_id ==
2409 UserGroup.users_group_id)\
2434 UserGroup.users_group_id)\
2410 .join(
2435 .join(
2411 UserGroupMember,
2436 UserGroupMember,
2412 UserGroupRepoGroupToPerm.users_group_id ==
2437 UserGroupRepoGroupToPerm.users_group_id ==
2413 UserGroupMember.users_group_id)\
2438 UserGroupMember.users_group_id)\
2414 .filter(
2439 .filter(
2415 UserGroupMember.user_id == user_id,
2440 UserGroupMember.user_id == user_id,
2416 UserGroup.users_group_active == true())
2441 UserGroup.users_group_active == true())
2417 if repo_group_id:
2442 if repo_group_id:
2418 q = q.filter(UserGroupRepoGroupToPerm.group_id == repo_group_id)
2443 q = q.filter(UserGroupRepoGroupToPerm.group_id == repo_group_id)
2419 return q.all()
2444 return q.all()
2420
2445
2421 @classmethod
2446 @classmethod
2422 def get_default_user_group_perms(cls, user_id, user_group_id=None):
2447 def get_default_user_group_perms(cls, user_id, user_group_id=None):
2423 q = Session().query(UserUserGroupToPerm, UserGroup, Permission)\
2448 q = Session().query(UserUserGroupToPerm, UserGroup, Permission)\
2424 .join((Permission, UserUserGroupToPerm.permission_id == Permission.permission_id))\
2449 .join((Permission, UserUserGroupToPerm.permission_id == Permission.permission_id))\
2425 .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id))\
2450 .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id))\
2426 .filter(UserUserGroupToPerm.user_id == user_id)
2451 .filter(UserUserGroupToPerm.user_id == user_id)
2427 if user_group_id:
2452 if user_group_id:
2428 q = q.filter(UserUserGroupToPerm.user_group_id == user_group_id)
2453 q = q.filter(UserUserGroupToPerm.user_group_id == user_group_id)
2429 return q.all()
2454 return q.all()
2430
2455
2431 @classmethod
2456 @classmethod
2432 def get_default_user_group_perms_from_user_group(
2457 def get_default_user_group_perms_from_user_group(
2433 cls, user_id, user_group_id=None):
2458 cls, user_id, user_group_id=None):
2434 TargetUserGroup = aliased(UserGroup, name='target_user_group')
2459 TargetUserGroup = aliased(UserGroup, name='target_user_group')
2435 q = Session().query(UserGroupUserGroupToPerm, UserGroup, Permission)\
2460 q = Session().query(UserGroupUserGroupToPerm, UserGroup, Permission)\
2436 .join(
2461 .join(
2437 Permission,
2462 Permission,
2438 UserGroupUserGroupToPerm.permission_id ==
2463 UserGroupUserGroupToPerm.permission_id ==
2439 Permission.permission_id)\
2464 Permission.permission_id)\
2440 .join(
2465 .join(
2441 TargetUserGroup,
2466 TargetUserGroup,
2442 UserGroupUserGroupToPerm.target_user_group_id ==
2467 UserGroupUserGroupToPerm.target_user_group_id ==
2443 TargetUserGroup.users_group_id)\
2468 TargetUserGroup.users_group_id)\
2444 .join(
2469 .join(
2445 UserGroup,
2470 UserGroup,
2446 UserGroupUserGroupToPerm.user_group_id ==
2471 UserGroupUserGroupToPerm.user_group_id ==
2447 UserGroup.users_group_id)\
2472 UserGroup.users_group_id)\
2448 .join(
2473 .join(
2449 UserGroupMember,
2474 UserGroupMember,
2450 UserGroupUserGroupToPerm.user_group_id ==
2475 UserGroupUserGroupToPerm.user_group_id ==
2451 UserGroupMember.users_group_id)\
2476 UserGroupMember.users_group_id)\
2452 .filter(
2477 .filter(
2453 UserGroupMember.user_id == user_id,
2478 UserGroupMember.user_id == user_id,
2454 UserGroup.users_group_active == true())
2479 UserGroup.users_group_active == true())
2455 if user_group_id:
2480 if user_group_id:
2456 q = q.filter(
2481 q = q.filter(
2457 UserGroupUserGroupToPerm.user_group_id == user_group_id)
2482 UserGroupUserGroupToPerm.user_group_id == user_group_id)
2458
2483
2459 return q.all()
2484 return q.all()
2460
2485
2461
2486
2462 class UserRepoToPerm(Base, BaseModel):
2487 class UserRepoToPerm(Base, BaseModel):
2463 __tablename__ = 'repo_to_perm'
2488 __tablename__ = 'repo_to_perm'
2464 __table_args__ = (
2489 __table_args__ = (
2465 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
2490 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
2466 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2491 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2467 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2492 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2468 )
2493 )
2469 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2494 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2470 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
2495 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
2471 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
2496 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
2472 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
2497 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
2473
2498
2474 user = relationship('User')
2499 user = relationship('User')
2475 repository = relationship('Repository')
2500 repository = relationship('Repository')
2476 permission = relationship('Permission')
2501 permission = relationship('Permission')
2477
2502
2478 @classmethod
2503 @classmethod
2479 def create(cls, user, repository, permission):
2504 def create(cls, user, repository, permission):
2480 n = cls()
2505 n = cls()
2481 n.user = user
2506 n.user = user
2482 n.repository = repository
2507 n.repository = repository
2483 n.permission = permission
2508 n.permission = permission
2484 Session().add(n)
2509 Session().add(n)
2485 return n
2510 return n
2486
2511
2487 def __unicode__(self):
2512 def __unicode__(self):
2488 return u'<%s => %s >' % (self.user, self.repository)
2513 return u'<%s => %s >' % (self.user, self.repository)
2489
2514
2490
2515
2491 class UserUserGroupToPerm(Base, BaseModel):
2516 class UserUserGroupToPerm(Base, BaseModel):
2492 __tablename__ = 'user_user_group_to_perm'
2517 __tablename__ = 'user_user_group_to_perm'
2493 __table_args__ = (
2518 __table_args__ = (
2494 UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
2519 UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
2495 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2520 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2496 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2521 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2497 )
2522 )
2498 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2523 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2499 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
2524 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
2500 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
2525 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
2501 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
2526 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
2502
2527
2503 user = relationship('User')
2528 user = relationship('User')
2504 user_group = relationship('UserGroup')
2529 user_group = relationship('UserGroup')
2505 permission = relationship('Permission')
2530 permission = relationship('Permission')
2506
2531
2507 @classmethod
2532 @classmethod
2508 def create(cls, user, user_group, permission):
2533 def create(cls, user, user_group, permission):
2509 n = cls()
2534 n = cls()
2510 n.user = user
2535 n.user = user
2511 n.user_group = user_group
2536 n.user_group = user_group
2512 n.permission = permission
2537 n.permission = permission
2513 Session().add(n)
2538 Session().add(n)
2514 return n
2539 return n
2515
2540
2516 def __unicode__(self):
2541 def __unicode__(self):
2517 return u'<%s => %s >' % (self.user, self.user_group)
2542 return u'<%s => %s >' % (self.user, self.user_group)
2518
2543
2519
2544
2520 class UserToPerm(Base, BaseModel):
2545 class UserToPerm(Base, BaseModel):
2521 __tablename__ = 'user_to_perm'
2546 __tablename__ = 'user_to_perm'
2522 __table_args__ = (
2547 __table_args__ = (
2523 UniqueConstraint('user_id', 'permission_id'),
2548 UniqueConstraint('user_id', 'permission_id'),
2524 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2549 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2525 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2550 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2526 )
2551 )
2527 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2552 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2528 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
2553 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
2529 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
2554 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
2530
2555
2531 user = relationship('User')
2556 user = relationship('User')
2532 permission = relationship('Permission', lazy='joined')
2557 permission = relationship('Permission', lazy='joined')
2533
2558
2534 def __unicode__(self):
2559 def __unicode__(self):
2535 return u'<%s => %s >' % (self.user, self.permission)
2560 return u'<%s => %s >' % (self.user, self.permission)
2536
2561
2537
2562
2538 class UserGroupRepoToPerm(Base, BaseModel):
2563 class UserGroupRepoToPerm(Base, BaseModel):
2539 __tablename__ = 'users_group_repo_to_perm'
2564 __tablename__ = 'users_group_repo_to_perm'
2540 __table_args__ = (
2565 __table_args__ = (
2541 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
2566 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
2542 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2567 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2543 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2568 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2544 )
2569 )
2545 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2570 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2546 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
2571 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
2547 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
2572 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
2548 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
2573 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
2549
2574
2550 users_group = relationship('UserGroup')
2575 users_group = relationship('UserGroup')
2551 permission = relationship('Permission')
2576 permission = relationship('Permission')
2552 repository = relationship('Repository')
2577 repository = relationship('Repository')
2553
2578
2554 @classmethod
2579 @classmethod
2555 def create(cls, users_group, repository, permission):
2580 def create(cls, users_group, repository, permission):
2556 n = cls()
2581 n = cls()
2557 n.users_group = users_group
2582 n.users_group = users_group
2558 n.repository = repository
2583 n.repository = repository
2559 n.permission = permission
2584 n.permission = permission
2560 Session().add(n)
2585 Session().add(n)
2561 return n
2586 return n
2562
2587
2563 def __unicode__(self):
2588 def __unicode__(self):
2564 return u'<UserGroupRepoToPerm:%s => %s >' % (self.users_group, self.repository)
2589 return u'<UserGroupRepoToPerm:%s => %s >' % (self.users_group, self.repository)
2565
2590
2566
2591
2567 class UserGroupUserGroupToPerm(Base, BaseModel):
2592 class UserGroupUserGroupToPerm(Base, BaseModel):
2568 __tablename__ = 'user_group_user_group_to_perm'
2593 __tablename__ = 'user_group_user_group_to_perm'
2569 __table_args__ = (
2594 __table_args__ = (
2570 UniqueConstraint('target_user_group_id', 'user_group_id', 'permission_id'),
2595 UniqueConstraint('target_user_group_id', 'user_group_id', 'permission_id'),
2571 CheckConstraint('target_user_group_id != user_group_id'),
2596 CheckConstraint('target_user_group_id != user_group_id'),
2572 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2597 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2573 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2598 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2574 )
2599 )
2575 user_group_user_group_to_perm_id = Column("user_group_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2600 user_group_user_group_to_perm_id = Column("user_group_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2576 target_user_group_id = Column("target_user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
2601 target_user_group_id = Column("target_user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
2577 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
2602 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
2578 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
2603 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
2579
2604
2580 target_user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
2605 target_user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
2581 user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
2606 user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
2582 permission = relationship('Permission')
2607 permission = relationship('Permission')
2583
2608
2584 @classmethod
2609 @classmethod
2585 def create(cls, target_user_group, user_group, permission):
2610 def create(cls, target_user_group, user_group, permission):
2586 n = cls()
2611 n = cls()
2587 n.target_user_group = target_user_group
2612 n.target_user_group = target_user_group
2588 n.user_group = user_group
2613 n.user_group = user_group
2589 n.permission = permission
2614 n.permission = permission
2590 Session().add(n)
2615 Session().add(n)
2591 return n
2616 return n
2592
2617
2593 def __unicode__(self):
2618 def __unicode__(self):
2594 return u'<UserGroupUserGroup:%s => %s >' % (self.target_user_group, self.user_group)
2619 return u'<UserGroupUserGroup:%s => %s >' % (self.target_user_group, self.user_group)
2595
2620
2596
2621
2597 class UserGroupToPerm(Base, BaseModel):
2622 class UserGroupToPerm(Base, BaseModel):
2598 __tablename__ = 'users_group_to_perm'
2623 __tablename__ = 'users_group_to_perm'
2599 __table_args__ = (
2624 __table_args__ = (
2600 UniqueConstraint('users_group_id', 'permission_id',),
2625 UniqueConstraint('users_group_id', 'permission_id',),
2601 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2626 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2602 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2627 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2603 )
2628 )
2604 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2629 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2605 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
2630 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
2606 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
2631 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
2607
2632
2608 users_group = relationship('UserGroup')
2633 users_group = relationship('UserGroup')
2609 permission = relationship('Permission')
2634 permission = relationship('Permission')
2610
2635
2611
2636
2612 class UserRepoGroupToPerm(Base, BaseModel):
2637 class UserRepoGroupToPerm(Base, BaseModel):
2613 __tablename__ = 'user_repo_group_to_perm'
2638 __tablename__ = 'user_repo_group_to_perm'
2614 __table_args__ = (
2639 __table_args__ = (
2615 UniqueConstraint('user_id', 'group_id', 'permission_id'),
2640 UniqueConstraint('user_id', 'group_id', 'permission_id'),
2616 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2641 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2617 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2642 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2618 )
2643 )
2619
2644
2620 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2645 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2621 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
2646 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
2622 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
2647 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
2623 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
2648 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
2624
2649
2625 user = relationship('User')
2650 user = relationship('User')
2626 group = relationship('RepoGroup')
2651 group = relationship('RepoGroup')
2627 permission = relationship('Permission')
2652 permission = relationship('Permission')
2628
2653
2629 @classmethod
2654 @classmethod
2630 def create(cls, user, repository_group, permission):
2655 def create(cls, user, repository_group, permission):
2631 n = cls()
2656 n = cls()
2632 n.user = user
2657 n.user = user
2633 n.group = repository_group
2658 n.group = repository_group
2634 n.permission = permission
2659 n.permission = permission
2635 Session().add(n)
2660 Session().add(n)
2636 return n
2661 return n
2637
2662
2638
2663
2639 class UserGroupRepoGroupToPerm(Base, BaseModel):
2664 class UserGroupRepoGroupToPerm(Base, BaseModel):
2640 __tablename__ = 'users_group_repo_group_to_perm'
2665 __tablename__ = 'users_group_repo_group_to_perm'
2641 __table_args__ = (
2666 __table_args__ = (
2642 UniqueConstraint('users_group_id', 'group_id'),
2667 UniqueConstraint('users_group_id', 'group_id'),
2643 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2668 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2644 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2669 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2645 )
2670 )
2646
2671
2647 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)
2672 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)
2648 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
2673 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
2649 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
2674 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
2650 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
2675 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
2651
2676
2652 users_group = relationship('UserGroup')
2677 users_group = relationship('UserGroup')
2653 permission = relationship('Permission')
2678 permission = relationship('Permission')
2654 group = relationship('RepoGroup')
2679 group = relationship('RepoGroup')
2655
2680
2656 @classmethod
2681 @classmethod
2657 def create(cls, user_group, repository_group, permission):
2682 def create(cls, user_group, repository_group, permission):
2658 n = cls()
2683 n = cls()
2659 n.users_group = user_group
2684 n.users_group = user_group
2660 n.group = repository_group
2685 n.group = repository_group
2661 n.permission = permission
2686 n.permission = permission
2662 Session().add(n)
2687 Session().add(n)
2663 return n
2688 return n
2664
2689
2665 def __unicode__(self):
2690 def __unicode__(self):
2666 return u'<UserGroupRepoGroupToPerm:%s => %s >' % (self.users_group, self.group)
2691 return u'<UserGroupRepoGroupToPerm:%s => %s >' % (self.users_group, self.group)
2667
2692
2668
2693
2669 class Statistics(Base, BaseModel):
2694 class Statistics(Base, BaseModel):
2670 __tablename__ = 'statistics'
2695 __tablename__ = 'statistics'
2671 __table_args__ = (
2696 __table_args__ = (
2672 UniqueConstraint('repository_id'),
2697 UniqueConstraint('repository_id'),
2673 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2698 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2674 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2699 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2675 )
2700 )
2676 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2701 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2677 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
2702 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
2678 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
2703 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
2679 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
2704 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
2680 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
2705 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
2681 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
2706 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
2682
2707
2683 repository = relationship('Repository', single_parent=True)
2708 repository = relationship('Repository', single_parent=True)
2684
2709
2685
2710
2686 class UserFollowing(Base, BaseModel):
2711 class UserFollowing(Base, BaseModel):
2687 __tablename__ = 'user_followings'
2712 __tablename__ = 'user_followings'
2688 __table_args__ = (
2713 __table_args__ = (
2689 UniqueConstraint('user_id', 'follows_repository_id'),
2714 UniqueConstraint('user_id', 'follows_repository_id'),
2690 UniqueConstraint('user_id', 'follows_user_id'),
2715 UniqueConstraint('user_id', 'follows_user_id'),
2691 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2716 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2692 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2717 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2693 )
2718 )
2694
2719
2695 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2720 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2696 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
2721 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
2697 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
2722 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
2698 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
2723 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
2699 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
2724 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
2700
2725
2701 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
2726 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
2702
2727
2703 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
2728 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
2704 follows_repository = relationship('Repository', order_by='Repository.repo_name')
2729 follows_repository = relationship('Repository', order_by='Repository.repo_name')
2705
2730
2706 @classmethod
2731 @classmethod
2707 def get_repo_followers(cls, repo_id):
2732 def get_repo_followers(cls, repo_id):
2708 return cls.query().filter(cls.follows_repo_id == repo_id)
2733 return cls.query().filter(cls.follows_repo_id == repo_id)
2709
2734
2710
2735
2711 class CacheKey(Base, BaseModel):
2736 class CacheKey(Base, BaseModel):
2712 __tablename__ = 'cache_invalidation'
2737 __tablename__ = 'cache_invalidation'
2713 __table_args__ = (
2738 __table_args__ = (
2714 UniqueConstraint('cache_key'),
2739 UniqueConstraint('cache_key'),
2715 Index('key_idx', 'cache_key'),
2740 Index('key_idx', 'cache_key'),
2716 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2741 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2717 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
2742 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
2718 )
2743 )
2719 CACHE_TYPE_ATOM = 'ATOM'
2744 CACHE_TYPE_ATOM = 'ATOM'
2720 CACHE_TYPE_RSS = 'RSS'
2745 CACHE_TYPE_RSS = 'RSS'
2721 CACHE_TYPE_README = 'README'
2746 CACHE_TYPE_README = 'README'
2722
2747
2723 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2748 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2724 cache_key = Column("cache_key", String(255), nullable=True, unique=None, default=None)
2749 cache_key = Column("cache_key", String(255), nullable=True, unique=None, default=None)
2725 cache_args = Column("cache_args", String(255), nullable=True, unique=None, default=None)
2750 cache_args = Column("cache_args", String(255), nullable=True, unique=None, default=None)
2726 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
2751 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
2727
2752
2728 def __init__(self, cache_key, cache_args=''):
2753 def __init__(self, cache_key, cache_args=''):
2729 self.cache_key = cache_key
2754 self.cache_key = cache_key
2730 self.cache_args = cache_args
2755 self.cache_args = cache_args
2731 self.cache_active = False
2756 self.cache_active = False
2732
2757
2733 def __unicode__(self):
2758 def __unicode__(self):
2734 return u"<%s('%s:%s[%s]')>" % (
2759 return u"<%s('%s:%s[%s]')>" % (
2735 self.__class__.__name__,
2760 self.__class__.__name__,
2736 self.cache_id, self.cache_key, self.cache_active)
2761 self.cache_id, self.cache_key, self.cache_active)
2737
2762
2738 def _cache_key_partition(self):
2763 def _cache_key_partition(self):
2739 prefix, repo_name, suffix = self.cache_key.partition(self.cache_args)
2764 prefix, repo_name, suffix = self.cache_key.partition(self.cache_args)
2740 return prefix, repo_name, suffix
2765 return prefix, repo_name, suffix
2741
2766
2742 def get_prefix(self):
2767 def get_prefix(self):
2743 """
2768 """
2744 Try to extract prefix from existing cache key. The key could consist
2769 Try to extract prefix from existing cache key. The key could consist
2745 of prefix, repo_name, suffix
2770 of prefix, repo_name, suffix
2746 """
2771 """
2747 # this returns prefix, repo_name, suffix
2772 # this returns prefix, repo_name, suffix
2748 return self._cache_key_partition()[0]
2773 return self._cache_key_partition()[0]
2749
2774
2750 def get_suffix(self):
2775 def get_suffix(self):
2751 """
2776 """
2752 get suffix that might have been used in _get_cache_key to
2777 get suffix that might have been used in _get_cache_key to
2753 generate self.cache_key. Only used for informational purposes
2778 generate self.cache_key. Only used for informational purposes
2754 in repo_edit.html.
2779 in repo_edit.html.
2755 """
2780 """
2756 # prefix, repo_name, suffix
2781 # prefix, repo_name, suffix
2757 return self._cache_key_partition()[2]
2782 return self._cache_key_partition()[2]
2758
2783
2759 @classmethod
2784 @classmethod
2760 def delete_all_cache(cls):
2785 def delete_all_cache(cls):
2761 """
2786 """
2762 Delete all cache keys from database.
2787 Delete all cache keys from database.
2763 Should only be run when all instances are down and all entries
2788 Should only be run when all instances are down and all entries
2764 thus stale.
2789 thus stale.
2765 """
2790 """
2766 cls.query().delete()
2791 cls.query().delete()
2767 Session().commit()
2792 Session().commit()
2768
2793
2769 @classmethod
2794 @classmethod
2770 def get_cache_key(cls, repo_name, cache_type):
2795 def get_cache_key(cls, repo_name, cache_type):
2771 """
2796 """
2772
2797
2773 Generate a cache key for this process of RhodeCode instance.
2798 Generate a cache key for this process of RhodeCode instance.
2774 Prefix most likely will be process id or maybe explicitly set
2799 Prefix most likely will be process id or maybe explicitly set
2775 instance_id from .ini file.
2800 instance_id from .ini file.
2776 """
2801 """
2777 import rhodecode
2802 import rhodecode
2778 prefix = safe_unicode(rhodecode.CONFIG.get('instance_id') or '')
2803 prefix = safe_unicode(rhodecode.CONFIG.get('instance_id') or '')
2779
2804
2780 repo_as_unicode = safe_unicode(repo_name)
2805 repo_as_unicode = safe_unicode(repo_name)
2781 key = u'{}_{}'.format(repo_as_unicode, cache_type) \
2806 key = u'{}_{}'.format(repo_as_unicode, cache_type) \
2782 if cache_type else repo_as_unicode
2807 if cache_type else repo_as_unicode
2783
2808
2784 return u'{}{}'.format(prefix, key)
2809 return u'{}{}'.format(prefix, key)
2785
2810
2786 @classmethod
2811 @classmethod
2787 def set_invalidate(cls, repo_name, delete=False):
2812 def set_invalidate(cls, repo_name, delete=False):
2788 """
2813 """
2789 Mark all caches of a repo as invalid in the database.
2814 Mark all caches of a repo as invalid in the database.
2790 """
2815 """
2791
2816
2792 try:
2817 try:
2793 qry = Session().query(cls).filter(cls.cache_args == repo_name)
2818 qry = Session().query(cls).filter(cls.cache_args == repo_name)
2794 if delete:
2819 if delete:
2795 log.debug('cache objects deleted for repo %s',
2820 log.debug('cache objects deleted for repo %s',
2796 safe_str(repo_name))
2821 safe_str(repo_name))
2797 qry.delete()
2822 qry.delete()
2798 else:
2823 else:
2799 log.debug('cache objects marked as invalid for repo %s',
2824 log.debug('cache objects marked as invalid for repo %s',
2800 safe_str(repo_name))
2825 safe_str(repo_name))
2801 qry.update({"cache_active": False})
2826 qry.update({"cache_active": False})
2802
2827
2803 Session().commit()
2828 Session().commit()
2804 except Exception:
2829 except Exception:
2805 log.exception(
2830 log.exception(
2806 'Cache key invalidation failed for repository %s',
2831 'Cache key invalidation failed for repository %s',
2807 safe_str(repo_name))
2832 safe_str(repo_name))
2808 Session().rollback()
2833 Session().rollback()
2809
2834
2810 @classmethod
2835 @classmethod
2811 def get_active_cache(cls, cache_key):
2836 def get_active_cache(cls, cache_key):
2812 inv_obj = cls.query().filter(cls.cache_key == cache_key).scalar()
2837 inv_obj = cls.query().filter(cls.cache_key == cache_key).scalar()
2813 if inv_obj:
2838 if inv_obj:
2814 return inv_obj
2839 return inv_obj
2815 return None
2840 return None
2816
2841
2817 @classmethod
2842 @classmethod
2818 def repo_context_cache(cls, compute_func, repo_name, cache_type):
2843 def repo_context_cache(cls, compute_func, repo_name, cache_type):
2819 """
2844 """
2820 @cache_region('long_term')
2845 @cache_region('long_term')
2821 def _heavy_calculation(cache_key):
2846 def _heavy_calculation(cache_key):
2822 return 'result'
2847 return 'result'
2823
2848
2824 cache_context = CacheKey.repo_context_cache(
2849 cache_context = CacheKey.repo_context_cache(
2825 _heavy_calculation, repo_name, cache_type)
2850 _heavy_calculation, repo_name, cache_type)
2826
2851
2827 with cache_context as context:
2852 with cache_context as context:
2828 context.invalidate()
2853 context.invalidate()
2829 computed = context.compute()
2854 computed = context.compute()
2830
2855
2831 assert computed == 'result'
2856 assert computed == 'result'
2832 """
2857 """
2833 from rhodecode.lib import caches
2858 from rhodecode.lib import caches
2834 return caches.InvalidationContext(compute_func, repo_name, cache_type)
2859 return caches.InvalidationContext(compute_func, repo_name, cache_type)
2835
2860
2836
2861
2837 class ChangesetComment(Base, BaseModel):
2862 class ChangesetComment(Base, BaseModel):
2838 __tablename__ = 'changeset_comments'
2863 __tablename__ = 'changeset_comments'
2839 __table_args__ = (
2864 __table_args__ = (
2840 Index('cc_revision_idx', 'revision'),
2865 Index('cc_revision_idx', 'revision'),
2841 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2866 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2842 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
2867 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
2843 )
2868 )
2844
2869
2845 COMMENT_OUTDATED = u'comment_outdated'
2870 COMMENT_OUTDATED = u'comment_outdated'
2846
2871
2847 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
2872 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
2848 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
2873 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
2849 revision = Column('revision', String(40), nullable=True)
2874 revision = Column('revision', String(40), nullable=True)
2850 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
2875 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
2851 pull_request_version_id = Column("pull_request_version_id", Integer(), ForeignKey('pull_request_versions.pull_request_version_id'), nullable=True)
2876 pull_request_version_id = Column("pull_request_version_id", Integer(), ForeignKey('pull_request_versions.pull_request_version_id'), nullable=True)
2852 line_no = Column('line_no', Unicode(10), nullable=True)
2877 line_no = Column('line_no', Unicode(10), nullable=True)
2853 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
2878 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
2854 f_path = Column('f_path', Unicode(1000), nullable=True)
2879 f_path = Column('f_path', Unicode(1000), nullable=True)
2855 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
2880 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
2856 text = Column('text', UnicodeText().with_variant(UnicodeText(25000), 'mysql'), nullable=False)
2881 text = Column('text', UnicodeText().with_variant(UnicodeText(25000), 'mysql'), nullable=False)
2857 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
2882 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
2858 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
2883 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
2859 renderer = Column('renderer', Unicode(64), nullable=True)
2884 renderer = Column('renderer', Unicode(64), nullable=True)
2860 display_state = Column('display_state', Unicode(128), nullable=True)
2885 display_state = Column('display_state', Unicode(128), nullable=True)
2861
2886
2862 author = relationship('User', lazy='joined')
2887 author = relationship('User', lazy='joined')
2863 repo = relationship('Repository')
2888 repo = relationship('Repository')
2864 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
2889 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
2865 pull_request = relationship('PullRequest', lazy='joined')
2890 pull_request = relationship('PullRequest', lazy='joined')
2866 pull_request_version = relationship('PullRequestVersion')
2891 pull_request_version = relationship('PullRequestVersion')
2867
2892
2868 @classmethod
2893 @classmethod
2869 def get_users(cls, revision=None, pull_request_id=None):
2894 def get_users(cls, revision=None, pull_request_id=None):
2870 """
2895 """
2871 Returns user associated with this ChangesetComment. ie those
2896 Returns user associated with this ChangesetComment. ie those
2872 who actually commented
2897 who actually commented
2873
2898
2874 :param cls:
2899 :param cls:
2875 :param revision:
2900 :param revision:
2876 """
2901 """
2877 q = Session().query(User)\
2902 q = Session().query(User)\
2878 .join(ChangesetComment.author)
2903 .join(ChangesetComment.author)
2879 if revision:
2904 if revision:
2880 q = q.filter(cls.revision == revision)
2905 q = q.filter(cls.revision == revision)
2881 elif pull_request_id:
2906 elif pull_request_id:
2882 q = q.filter(cls.pull_request_id == pull_request_id)
2907 q = q.filter(cls.pull_request_id == pull_request_id)
2883 return q.all()
2908 return q.all()
2884
2909
2885 def render(self, mentions=False):
2910 def render(self, mentions=False):
2886 from rhodecode.lib import helpers as h
2911 from rhodecode.lib import helpers as h
2887 return h.render(self.text, renderer=self.renderer, mentions=mentions)
2912 return h.render(self.text, renderer=self.renderer, mentions=mentions)
2888
2913
2889 def __repr__(self):
2914 def __repr__(self):
2890 if self.comment_id:
2915 if self.comment_id:
2891 return '<DB:ChangesetComment #%s>' % self.comment_id
2916 return '<DB:ChangesetComment #%s>' % self.comment_id
2892 else:
2917 else:
2893 return '<DB:ChangesetComment at %#x>' % id(self)
2918 return '<DB:ChangesetComment at %#x>' % id(self)
2894
2919
2895
2920
2896 class ChangesetStatus(Base, BaseModel):
2921 class ChangesetStatus(Base, BaseModel):
2897 __tablename__ = 'changeset_statuses'
2922 __tablename__ = 'changeset_statuses'
2898 __table_args__ = (
2923 __table_args__ = (
2899 Index('cs_revision_idx', 'revision'),
2924 Index('cs_revision_idx', 'revision'),
2900 Index('cs_version_idx', 'version'),
2925 Index('cs_version_idx', 'version'),
2901 UniqueConstraint('repo_id', 'revision', 'version'),
2926 UniqueConstraint('repo_id', 'revision', 'version'),
2902 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2927 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2903 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2928 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2904 )
2929 )
2905 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
2930 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
2906 STATUS_APPROVED = 'approved'
2931 STATUS_APPROVED = 'approved'
2907 STATUS_REJECTED = 'rejected'
2932 STATUS_REJECTED = 'rejected'
2908 STATUS_UNDER_REVIEW = 'under_review'
2933 STATUS_UNDER_REVIEW = 'under_review'
2909
2934
2910 STATUSES = [
2935 STATUSES = [
2911 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
2936 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
2912 (STATUS_APPROVED, _("Approved")),
2937 (STATUS_APPROVED, _("Approved")),
2913 (STATUS_REJECTED, _("Rejected")),
2938 (STATUS_REJECTED, _("Rejected")),
2914 (STATUS_UNDER_REVIEW, _("Under Review")),
2939 (STATUS_UNDER_REVIEW, _("Under Review")),
2915 ]
2940 ]
2916
2941
2917 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
2942 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
2918 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
2943 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
2919 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
2944 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
2920 revision = Column('revision', String(40), nullable=False)
2945 revision = Column('revision', String(40), nullable=False)
2921 status = Column('status', String(128), nullable=False, default=DEFAULT)
2946 status = Column('status', String(128), nullable=False, default=DEFAULT)
2922 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
2947 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
2923 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
2948 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
2924 version = Column('version', Integer(), nullable=False, default=0)
2949 version = Column('version', Integer(), nullable=False, default=0)
2925 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
2950 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
2926
2951
2927 author = relationship('User', lazy='joined')
2952 author = relationship('User', lazy='joined')
2928 repo = relationship('Repository')
2953 repo = relationship('Repository')
2929 comment = relationship('ChangesetComment', lazy='joined')
2954 comment = relationship('ChangesetComment', lazy='joined')
2930 pull_request = relationship('PullRequest', lazy='joined')
2955 pull_request = relationship('PullRequest', lazy='joined')
2931
2956
2932 def __unicode__(self):
2957 def __unicode__(self):
2933 return u"<%s('%s[%s]:%s')>" % (
2958 return u"<%s('%s[%s]:%s')>" % (
2934 self.__class__.__name__,
2959 self.__class__.__name__,
2935 self.status, self.version, self.author
2960 self.status, self.version, self.author
2936 )
2961 )
2937
2962
2938 @classmethod
2963 @classmethod
2939 def get_status_lbl(cls, value):
2964 def get_status_lbl(cls, value):
2940 return dict(cls.STATUSES).get(value)
2965 return dict(cls.STATUSES).get(value)
2941
2966
2942 @property
2967 @property
2943 def status_lbl(self):
2968 def status_lbl(self):
2944 return ChangesetStatus.get_status_lbl(self.status)
2969 return ChangesetStatus.get_status_lbl(self.status)
2945
2970
2946
2971
2947 class _PullRequestBase(BaseModel):
2972 class _PullRequestBase(BaseModel):
2948 """
2973 """
2949 Common attributes of pull request and version entries.
2974 Common attributes of pull request and version entries.
2950 """
2975 """
2951
2976
2952 # .status values
2977 # .status values
2953 STATUS_NEW = u'new'
2978 STATUS_NEW = u'new'
2954 STATUS_OPEN = u'open'
2979 STATUS_OPEN = u'open'
2955 STATUS_CLOSED = u'closed'
2980 STATUS_CLOSED = u'closed'
2956
2981
2957 title = Column('title', Unicode(255), nullable=True)
2982 title = Column('title', Unicode(255), nullable=True)
2958 description = Column(
2983 description = Column(
2959 'description', UnicodeText().with_variant(UnicodeText(10240), 'mysql'),
2984 'description', UnicodeText().with_variant(UnicodeText(10240), 'mysql'),
2960 nullable=True)
2985 nullable=True)
2961 # new/open/closed status of pull request (not approve/reject/etc)
2986 # new/open/closed status of pull request (not approve/reject/etc)
2962 status = Column('status', Unicode(255), nullable=False, default=STATUS_NEW)
2987 status = Column('status', Unicode(255), nullable=False, default=STATUS_NEW)
2963 created_on = Column(
2988 created_on = Column(
2964 'created_on', DateTime(timezone=False), nullable=False,
2989 'created_on', DateTime(timezone=False), nullable=False,
2965 default=datetime.datetime.now)
2990 default=datetime.datetime.now)
2966 updated_on = Column(
2991 updated_on = Column(
2967 'updated_on', DateTime(timezone=False), nullable=False,
2992 'updated_on', DateTime(timezone=False), nullable=False,
2968 default=datetime.datetime.now)
2993 default=datetime.datetime.now)
2969
2994
2970 @declared_attr
2995 @declared_attr
2971 def user_id(cls):
2996 def user_id(cls):
2972 return Column(
2997 return Column(
2973 "user_id", Integer(), ForeignKey('users.user_id'), nullable=False,
2998 "user_id", Integer(), ForeignKey('users.user_id'), nullable=False,
2974 unique=None)
2999 unique=None)
2975
3000
2976 # 500 revisions max
3001 # 500 revisions max
2977 _revisions = Column(
3002 _revisions = Column(
2978 'revisions', UnicodeText().with_variant(UnicodeText(20500), 'mysql'))
3003 'revisions', UnicodeText().with_variant(UnicodeText(20500), 'mysql'))
2979
3004
2980 @declared_attr
3005 @declared_attr
2981 def source_repo_id(cls):
3006 def source_repo_id(cls):
2982 # TODO: dan: rename column to source_repo_id
3007 # TODO: dan: rename column to source_repo_id
2983 return Column(
3008 return Column(
2984 'org_repo_id', Integer(), ForeignKey('repositories.repo_id'),
3009 'org_repo_id', Integer(), ForeignKey('repositories.repo_id'),
2985 nullable=False)
3010 nullable=False)
2986
3011
2987 source_ref = Column('org_ref', Unicode(255), nullable=False)
3012 source_ref = Column('org_ref', Unicode(255), nullable=False)
2988
3013
2989 @declared_attr
3014 @declared_attr
2990 def target_repo_id(cls):
3015 def target_repo_id(cls):
2991 # TODO: dan: rename column to target_repo_id
3016 # TODO: dan: rename column to target_repo_id
2992 return Column(
3017 return Column(
2993 'other_repo_id', Integer(), ForeignKey('repositories.repo_id'),
3018 'other_repo_id', Integer(), ForeignKey('repositories.repo_id'),
2994 nullable=False)
3019 nullable=False)
2995
3020
2996 target_ref = Column('other_ref', Unicode(255), nullable=False)
3021 target_ref = Column('other_ref', Unicode(255), nullable=False)
2997
3022
2998 # TODO: dan: rename column to last_merge_source_rev
3023 # TODO: dan: rename column to last_merge_source_rev
2999 _last_merge_source_rev = Column(
3024 _last_merge_source_rev = Column(
3000 'last_merge_org_rev', String(40), nullable=True)
3025 'last_merge_org_rev', String(40), nullable=True)
3001 # TODO: dan: rename column to last_merge_target_rev
3026 # TODO: dan: rename column to last_merge_target_rev
3002 _last_merge_target_rev = Column(
3027 _last_merge_target_rev = Column(
3003 'last_merge_other_rev', String(40), nullable=True)
3028 'last_merge_other_rev', String(40), nullable=True)
3004 _last_merge_status = Column('merge_status', Integer(), nullable=True)
3029 _last_merge_status = Column('merge_status', Integer(), nullable=True)
3005 merge_rev = Column('merge_rev', String(40), nullable=True)
3030 merge_rev = Column('merge_rev', String(40), nullable=True)
3006
3031
3007 @hybrid_property
3032 @hybrid_property
3008 def revisions(self):
3033 def revisions(self):
3009 return self._revisions.split(':') if self._revisions else []
3034 return self._revisions.split(':') if self._revisions else []
3010
3035
3011 @revisions.setter
3036 @revisions.setter
3012 def revisions(self, val):
3037 def revisions(self, val):
3013 self._revisions = ':'.join(val)
3038 self._revisions = ':'.join(val)
3014
3039
3015 @declared_attr
3040 @declared_attr
3016 def author(cls):
3041 def author(cls):
3017 return relationship('User', lazy='joined')
3042 return relationship('User', lazy='joined')
3018
3043
3019 @declared_attr
3044 @declared_attr
3020 def source_repo(cls):
3045 def source_repo(cls):
3021 return relationship(
3046 return relationship(
3022 'Repository',
3047 'Repository',
3023 primaryjoin='%s.source_repo_id==Repository.repo_id' % cls.__name__)
3048 primaryjoin='%s.source_repo_id==Repository.repo_id' % cls.__name__)
3024
3049
3025 @property
3050 @property
3026 def source_ref_parts(self):
3051 def source_ref_parts(self):
3027 refs = self.source_ref.split(':')
3052 refs = self.source_ref.split(':')
3028 return Reference(refs[0], refs[1], refs[2])
3053 return Reference(refs[0], refs[1], refs[2])
3029
3054
3030 @declared_attr
3055 @declared_attr
3031 def target_repo(cls):
3056 def target_repo(cls):
3032 return relationship(
3057 return relationship(
3033 'Repository',
3058 'Repository',
3034 primaryjoin='%s.target_repo_id==Repository.repo_id' % cls.__name__)
3059 primaryjoin='%s.target_repo_id==Repository.repo_id' % cls.__name__)
3035
3060
3036 @property
3061 @property
3037 def target_ref_parts(self):
3062 def target_ref_parts(self):
3038 refs = self.target_ref.split(':')
3063 refs = self.target_ref.split(':')
3039 return Reference(refs[0], refs[1], refs[2])
3064 return Reference(refs[0], refs[1], refs[2])
3040
3065
3041
3066
3042 class PullRequest(Base, _PullRequestBase):
3067 class PullRequest(Base, _PullRequestBase):
3043 __tablename__ = 'pull_requests'
3068 __tablename__ = 'pull_requests'
3044 __table_args__ = (
3069 __table_args__ = (
3045 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3070 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3046 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
3071 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
3047 )
3072 )
3048
3073
3049 pull_request_id = Column(
3074 pull_request_id = Column(
3050 'pull_request_id', Integer(), nullable=False, primary_key=True)
3075 'pull_request_id', Integer(), nullable=False, primary_key=True)
3051
3076
3052 def __repr__(self):
3077 def __repr__(self):
3053 if self.pull_request_id:
3078 if self.pull_request_id:
3054 return '<DB:PullRequest #%s>' % self.pull_request_id
3079 return '<DB:PullRequest #%s>' % self.pull_request_id
3055 else:
3080 else:
3056 return '<DB:PullRequest at %#x>' % id(self)
3081 return '<DB:PullRequest at %#x>' % id(self)
3057
3082
3058 reviewers = relationship('PullRequestReviewers',
3083 reviewers = relationship('PullRequestReviewers',
3059 cascade="all, delete, delete-orphan")
3084 cascade="all, delete, delete-orphan")
3060 statuses = relationship('ChangesetStatus')
3085 statuses = relationship('ChangesetStatus')
3061 comments = relationship('ChangesetComment',
3086 comments = relationship('ChangesetComment',
3062 cascade="all, delete, delete-orphan")
3087 cascade="all, delete, delete-orphan")
3063 versions = relationship('PullRequestVersion',
3088 versions = relationship('PullRequestVersion',
3064 cascade="all, delete, delete-orphan")
3089 cascade="all, delete, delete-orphan")
3065
3090
3066 def is_closed(self):
3091 def is_closed(self):
3067 return self.status == self.STATUS_CLOSED
3092 return self.status == self.STATUS_CLOSED
3068
3093
3069 def get_api_data(self):
3094 def get_api_data(self):
3070 from rhodecode.model.pull_request import PullRequestModel
3095 from rhodecode.model.pull_request import PullRequestModel
3071 pull_request = self
3096 pull_request = self
3072 merge_status = PullRequestModel().merge_status(pull_request)
3097 merge_status = PullRequestModel().merge_status(pull_request)
3073 data = {
3098 data = {
3074 'pull_request_id': pull_request.pull_request_id,
3099 'pull_request_id': pull_request.pull_request_id,
3075 'url': url('pullrequest_show',
3100 'url': url('pullrequest_show',
3076 repo_name=pull_request.target_repo.repo_name,
3101 repo_name=pull_request.target_repo.repo_name,
3077 pull_request_id=pull_request.pull_request_id,
3102 pull_request_id=pull_request.pull_request_id,
3078 qualified=True),
3103 qualified=True),
3079 'title': pull_request.title,
3104 'title': pull_request.title,
3080 'description': pull_request.description,
3105 'description': pull_request.description,
3081 'status': pull_request.status,
3106 'status': pull_request.status,
3082 'created_on': pull_request.created_on,
3107 'created_on': pull_request.created_on,
3083 'updated_on': pull_request.updated_on,
3108 'updated_on': pull_request.updated_on,
3084 'commit_ids': pull_request.revisions,
3109 'commit_ids': pull_request.revisions,
3085 'review_status': pull_request.calculated_review_status(),
3110 'review_status': pull_request.calculated_review_status(),
3086 'mergeable': {
3111 'mergeable': {
3087 'status': merge_status[0],
3112 'status': merge_status[0],
3088 'message': unicode(merge_status[1]),
3113 'message': unicode(merge_status[1]),
3089 },
3114 },
3090 'source': {
3115 'source': {
3091 'clone_url': pull_request.source_repo.clone_url(),
3116 'clone_url': pull_request.source_repo.clone_url(),
3092 'repository': pull_request.source_repo.repo_name,
3117 'repository': pull_request.source_repo.repo_name,
3093 'reference': {
3118 'reference': {
3094 'name': pull_request.source_ref_parts.name,
3119 'name': pull_request.source_ref_parts.name,
3095 'type': pull_request.source_ref_parts.type,
3120 'type': pull_request.source_ref_parts.type,
3096 'commit_id': pull_request.source_ref_parts.commit_id,
3121 'commit_id': pull_request.source_ref_parts.commit_id,
3097 },
3122 },
3098 },
3123 },
3099 'target': {
3124 'target': {
3100 'clone_url': pull_request.target_repo.clone_url(),
3125 'clone_url': pull_request.target_repo.clone_url(),
3101 'repository': pull_request.target_repo.repo_name,
3126 'repository': pull_request.target_repo.repo_name,
3102 'reference': {
3127 'reference': {
3103 'name': pull_request.target_ref_parts.name,
3128 'name': pull_request.target_ref_parts.name,
3104 'type': pull_request.target_ref_parts.type,
3129 'type': pull_request.target_ref_parts.type,
3105 'commit_id': pull_request.target_ref_parts.commit_id,
3130 'commit_id': pull_request.target_ref_parts.commit_id,
3106 },
3131 },
3107 },
3132 },
3108 'author': pull_request.author.get_api_data(include_secrets=False,
3133 'author': pull_request.author.get_api_data(include_secrets=False,
3109 details='basic'),
3134 details='basic'),
3110 'reviewers': [
3135 'reviewers': [
3111 {
3136 {
3112 'user': reviewer.get_api_data(include_secrets=False,
3137 'user': reviewer.get_api_data(include_secrets=False,
3113 details='basic'),
3138 details='basic'),
3114 'review_status': st[0][1].status if st else 'not_reviewed',
3139 'review_status': st[0][1].status if st else 'not_reviewed',
3115 }
3140 }
3116 for reviewer, st in pull_request.reviewers_statuses()
3141 for reviewer, st in pull_request.reviewers_statuses()
3117 ]
3142 ]
3118 }
3143 }
3119
3144
3120 return data
3145 return data
3121
3146
3122 def __json__(self):
3147 def __json__(self):
3123 return {
3148 return {
3124 'revisions': self.revisions,
3149 'revisions': self.revisions,
3125 }
3150 }
3126
3151
3127 def calculated_review_status(self):
3152 def calculated_review_status(self):
3128 # TODO: anderson: 13.05.15 Used only on templates/my_account_pullrequests.html
3153 # TODO: anderson: 13.05.15 Used only on templates/my_account_pullrequests.html
3129 # because it's tricky on how to use ChangesetStatusModel from there
3154 # because it's tricky on how to use ChangesetStatusModel from there
3130 warnings.warn("Use calculated_review_status from ChangesetStatusModel", DeprecationWarning)
3155 warnings.warn("Use calculated_review_status from ChangesetStatusModel", DeprecationWarning)
3131 from rhodecode.model.changeset_status import ChangesetStatusModel
3156 from rhodecode.model.changeset_status import ChangesetStatusModel
3132 return ChangesetStatusModel().calculated_review_status(self)
3157 return ChangesetStatusModel().calculated_review_status(self)
3133
3158
3134 def reviewers_statuses(self):
3159 def reviewers_statuses(self):
3135 warnings.warn("Use reviewers_statuses from ChangesetStatusModel", DeprecationWarning)
3160 warnings.warn("Use reviewers_statuses from ChangesetStatusModel", DeprecationWarning)
3136 from rhodecode.model.changeset_status import ChangesetStatusModel
3161 from rhodecode.model.changeset_status import ChangesetStatusModel
3137 return ChangesetStatusModel().reviewers_statuses(self)
3162 return ChangesetStatusModel().reviewers_statuses(self)
3138
3163
3139
3164
3140 class PullRequestVersion(Base, _PullRequestBase):
3165 class PullRequestVersion(Base, _PullRequestBase):
3141 __tablename__ = 'pull_request_versions'
3166 __tablename__ = 'pull_request_versions'
3142 __table_args__ = (
3167 __table_args__ = (
3143 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3168 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3144 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
3169 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
3145 )
3170 )
3146
3171
3147 pull_request_version_id = Column(
3172 pull_request_version_id = Column(
3148 'pull_request_version_id', Integer(), nullable=False, primary_key=True)
3173 'pull_request_version_id', Integer(), nullable=False, primary_key=True)
3149 pull_request_id = Column(
3174 pull_request_id = Column(
3150 'pull_request_id', Integer(),
3175 'pull_request_id', Integer(),
3151 ForeignKey('pull_requests.pull_request_id'), nullable=False)
3176 ForeignKey('pull_requests.pull_request_id'), nullable=False)
3152 pull_request = relationship('PullRequest')
3177 pull_request = relationship('PullRequest')
3153
3178
3154 def __repr__(self):
3179 def __repr__(self):
3155 if self.pull_request_version_id:
3180 if self.pull_request_version_id:
3156 return '<DB:PullRequestVersion #%s>' % self.pull_request_version_id
3181 return '<DB:PullRequestVersion #%s>' % self.pull_request_version_id
3157 else:
3182 else:
3158 return '<DB:PullRequestVersion at %#x>' % id(self)
3183 return '<DB:PullRequestVersion at %#x>' % id(self)
3159
3184
3160
3185
3161 class PullRequestReviewers(Base, BaseModel):
3186 class PullRequestReviewers(Base, BaseModel):
3162 __tablename__ = 'pull_request_reviewers'
3187 __tablename__ = 'pull_request_reviewers'
3163 __table_args__ = (
3188 __table_args__ = (
3164 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3189 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3165 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
3190 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
3166 )
3191 )
3167
3192
3168 def __init__(self, user=None, pull_request=None):
3193 def __init__(self, user=None, pull_request=None):
3169 self.user = user
3194 self.user = user
3170 self.pull_request = pull_request
3195 self.pull_request = pull_request
3171
3196
3172 pull_requests_reviewers_id = Column(
3197 pull_requests_reviewers_id = Column(
3173 'pull_requests_reviewers_id', Integer(), nullable=False,
3198 'pull_requests_reviewers_id', Integer(), nullable=False,
3174 primary_key=True)
3199 primary_key=True)
3175 pull_request_id = Column(
3200 pull_request_id = Column(
3176 "pull_request_id", Integer(),
3201 "pull_request_id", Integer(),
3177 ForeignKey('pull_requests.pull_request_id'), nullable=False)
3202 ForeignKey('pull_requests.pull_request_id'), nullable=False)
3178 user_id = Column(
3203 user_id = Column(
3179 "user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
3204 "user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
3180
3205
3181 user = relationship('User')
3206 user = relationship('User')
3182 pull_request = relationship('PullRequest')
3207 pull_request = relationship('PullRequest')
3183
3208
3184
3209
3185 class Notification(Base, BaseModel):
3210 class Notification(Base, BaseModel):
3186 __tablename__ = 'notifications'
3211 __tablename__ = 'notifications'
3187 __table_args__ = (
3212 __table_args__ = (
3188 Index('notification_type_idx', 'type'),
3213 Index('notification_type_idx', 'type'),
3189 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3214 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3190 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
3215 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
3191 )
3216 )
3192
3217
3193 TYPE_CHANGESET_COMMENT = u'cs_comment'
3218 TYPE_CHANGESET_COMMENT = u'cs_comment'
3194 TYPE_MESSAGE = u'message'
3219 TYPE_MESSAGE = u'message'
3195 TYPE_MENTION = u'mention'
3220 TYPE_MENTION = u'mention'
3196 TYPE_REGISTRATION = u'registration'
3221 TYPE_REGISTRATION = u'registration'
3197 TYPE_PULL_REQUEST = u'pull_request'
3222 TYPE_PULL_REQUEST = u'pull_request'
3198 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
3223 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
3199
3224
3200 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
3225 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
3201 subject = Column('subject', Unicode(512), nullable=True)
3226 subject = Column('subject', Unicode(512), nullable=True)
3202 body = Column('body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True)
3227 body = Column('body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True)
3203 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
3228 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
3204 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
3229 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
3205 type_ = Column('type', Unicode(255))
3230 type_ = Column('type', Unicode(255))
3206
3231
3207 created_by_user = relationship('User')
3232 created_by_user = relationship('User')
3208 notifications_to_users = relationship('UserNotification', lazy='joined',
3233 notifications_to_users = relationship('UserNotification', lazy='joined',
3209 cascade="all, delete, delete-orphan")
3234 cascade="all, delete, delete-orphan")
3210
3235
3211 @property
3236 @property
3212 def recipients(self):
3237 def recipients(self):
3213 return [x.user for x in UserNotification.query()\
3238 return [x.user for x in UserNotification.query()\
3214 .filter(UserNotification.notification == self)\
3239 .filter(UserNotification.notification == self)\
3215 .order_by(UserNotification.user_id.asc()).all()]
3240 .order_by(UserNotification.user_id.asc()).all()]
3216
3241
3217 @classmethod
3242 @classmethod
3218 def create(cls, created_by, subject, body, recipients, type_=None):
3243 def create(cls, created_by, subject, body, recipients, type_=None):
3219 if type_ is None:
3244 if type_ is None:
3220 type_ = Notification.TYPE_MESSAGE
3245 type_ = Notification.TYPE_MESSAGE
3221
3246
3222 notification = cls()
3247 notification = cls()
3223 notification.created_by_user = created_by
3248 notification.created_by_user = created_by
3224 notification.subject = subject
3249 notification.subject = subject
3225 notification.body = body
3250 notification.body = body
3226 notification.type_ = type_
3251 notification.type_ = type_
3227 notification.created_on = datetime.datetime.now()
3252 notification.created_on = datetime.datetime.now()
3228
3253
3229 for u in recipients:
3254 for u in recipients:
3230 assoc = UserNotification()
3255 assoc = UserNotification()
3231 assoc.notification = notification
3256 assoc.notification = notification
3232
3257
3233 # if created_by is inside recipients mark his notification
3258 # if created_by is inside recipients mark his notification
3234 # as read
3259 # as read
3235 if u.user_id == created_by.user_id:
3260 if u.user_id == created_by.user_id:
3236 assoc.read = True
3261 assoc.read = True
3237
3262
3238 u.notifications.append(assoc)
3263 u.notifications.append(assoc)
3239 Session().add(notification)
3264 Session().add(notification)
3240
3265
3241 return notification
3266 return notification
3242
3267
3243 @property
3268 @property
3244 def description(self):
3269 def description(self):
3245 from rhodecode.model.notification import NotificationModel
3270 from rhodecode.model.notification import NotificationModel
3246 return NotificationModel().make_description(self)
3271 return NotificationModel().make_description(self)
3247
3272
3248
3273
3249 class UserNotification(Base, BaseModel):
3274 class UserNotification(Base, BaseModel):
3250 __tablename__ = 'user_to_notification'
3275 __tablename__ = 'user_to_notification'
3251 __table_args__ = (
3276 __table_args__ = (
3252 UniqueConstraint('user_id', 'notification_id'),
3277 UniqueConstraint('user_id', 'notification_id'),
3253 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3278 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3254 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
3279 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
3255 )
3280 )
3256 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
3281 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
3257 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
3282 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
3258 read = Column('read', Boolean, default=False)
3283 read = Column('read', Boolean, default=False)
3259 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
3284 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
3260
3285
3261 user = relationship('User', lazy="joined")
3286 user = relationship('User', lazy="joined")
3262 notification = relationship('Notification', lazy="joined",
3287 notification = relationship('Notification', lazy="joined",
3263 order_by=lambda: Notification.created_on.desc(),)
3288 order_by=lambda: Notification.created_on.desc(),)
3264
3289
3265 def mark_as_read(self):
3290 def mark_as_read(self):
3266 self.read = True
3291 self.read = True
3267 Session().add(self)
3292 Session().add(self)
3268
3293
3269
3294
3270 class Gist(Base, BaseModel):
3295 class Gist(Base, BaseModel):
3271 __tablename__ = 'gists'
3296 __tablename__ = 'gists'
3272 __table_args__ = (
3297 __table_args__ = (
3273 Index('g_gist_access_id_idx', 'gist_access_id'),
3298 Index('g_gist_access_id_idx', 'gist_access_id'),
3274 Index('g_created_on_idx', 'created_on'),
3299 Index('g_created_on_idx', 'created_on'),
3275 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3300 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3276 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
3301 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
3277 )
3302 )
3278 GIST_PUBLIC = u'public'
3303 GIST_PUBLIC = u'public'
3279 GIST_PRIVATE = u'private'
3304 GIST_PRIVATE = u'private'
3280 DEFAULT_FILENAME = u'gistfile1.txt'
3305 DEFAULT_FILENAME = u'gistfile1.txt'
3281
3306
3282 ACL_LEVEL_PUBLIC = u'acl_public'
3307 ACL_LEVEL_PUBLIC = u'acl_public'
3283 ACL_LEVEL_PRIVATE = u'acl_private'
3308 ACL_LEVEL_PRIVATE = u'acl_private'
3284
3309
3285 gist_id = Column('gist_id', Integer(), primary_key=True)
3310 gist_id = Column('gist_id', Integer(), primary_key=True)
3286 gist_access_id = Column('gist_access_id', Unicode(250))
3311 gist_access_id = Column('gist_access_id', Unicode(250))
3287 gist_description = Column('gist_description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
3312 gist_description = Column('gist_description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
3288 gist_owner = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=True)
3313 gist_owner = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=True)
3289 gist_expires = Column('gist_expires', Float(53), nullable=False)
3314 gist_expires = Column('gist_expires', Float(53), nullable=False)
3290 gist_type = Column('gist_type', Unicode(128), nullable=False)
3315 gist_type = Column('gist_type', Unicode(128), nullable=False)
3291 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
3316 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
3292 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
3317 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
3293 acl_level = Column('acl_level', Unicode(128), nullable=True)
3318 acl_level = Column('acl_level', Unicode(128), nullable=True)
3294
3319
3295 owner = relationship('User')
3320 owner = relationship('User')
3296
3321
3297 def __repr__(self):
3322 def __repr__(self):
3298 return '<Gist:[%s]%s>' % (self.gist_type, self.gist_access_id)
3323 return '<Gist:[%s]%s>' % (self.gist_type, self.gist_access_id)
3299
3324
3300 @classmethod
3325 @classmethod
3301 def get_or_404(cls, id_):
3326 def get_or_404(cls, id_):
3302 res = cls.query().filter(cls.gist_access_id == id_).scalar()
3327 res = cls.query().filter(cls.gist_access_id == id_).scalar()
3303 if not res:
3328 if not res:
3304 raise HTTPNotFound
3329 raise HTTPNotFound
3305 return res
3330 return res
3306
3331
3307 @classmethod
3332 @classmethod
3308 def get_by_access_id(cls, gist_access_id):
3333 def get_by_access_id(cls, gist_access_id):
3309 return cls.query().filter(cls.gist_access_id == gist_access_id).scalar()
3334 return cls.query().filter(cls.gist_access_id == gist_access_id).scalar()
3310
3335
3311 def gist_url(self):
3336 def gist_url(self):
3312 import rhodecode
3337 import rhodecode
3313 alias_url = rhodecode.CONFIG.get('gist_alias_url')
3338 alias_url = rhodecode.CONFIG.get('gist_alias_url')
3314 if alias_url:
3339 if alias_url:
3315 return alias_url.replace('{gistid}', self.gist_access_id)
3340 return alias_url.replace('{gistid}', self.gist_access_id)
3316
3341
3317 return url('gist', gist_id=self.gist_access_id, qualified=True)
3342 return url('gist', gist_id=self.gist_access_id, qualified=True)
3318
3343
3319 @classmethod
3344 @classmethod
3320 def base_path(cls):
3345 def base_path(cls):
3321 """
3346 """
3322 Returns base path when all gists are stored
3347 Returns base path when all gists are stored
3323
3348
3324 :param cls:
3349 :param cls:
3325 """
3350 """
3326 from rhodecode.model.gist import GIST_STORE_LOC
3351 from rhodecode.model.gist import GIST_STORE_LOC
3327 q = Session().query(RhodeCodeUi)\
3352 q = Session().query(RhodeCodeUi)\
3328 .filter(RhodeCodeUi.ui_key == URL_SEP)
3353 .filter(RhodeCodeUi.ui_key == URL_SEP)
3329 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
3354 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
3330 return os.path.join(q.one().ui_value, GIST_STORE_LOC)
3355 return os.path.join(q.one().ui_value, GIST_STORE_LOC)
3331
3356
3332 def get_api_data(self):
3357 def get_api_data(self):
3333 """
3358 """
3334 Common function for generating gist related data for API
3359 Common function for generating gist related data for API
3335 """
3360 """
3336 gist = self
3361 gist = self
3337 data = {
3362 data = {
3338 'gist_id': gist.gist_id,
3363 'gist_id': gist.gist_id,
3339 'type': gist.gist_type,
3364 'type': gist.gist_type,
3340 'access_id': gist.gist_access_id,
3365 'access_id': gist.gist_access_id,
3341 'description': gist.gist_description,
3366 'description': gist.gist_description,
3342 'url': gist.gist_url(),
3367 'url': gist.gist_url(),
3343 'expires': gist.gist_expires,
3368 'expires': gist.gist_expires,
3344 'created_on': gist.created_on,
3369 'created_on': gist.created_on,
3345 'modified_at': gist.modified_at,
3370 'modified_at': gist.modified_at,
3346 'content': None,
3371 'content': None,
3347 'acl_level': gist.acl_level,
3372 'acl_level': gist.acl_level,
3348 }
3373 }
3349 return data
3374 return data
3350
3375
3351 def __json__(self):
3376 def __json__(self):
3352 data = dict(
3377 data = dict(
3353 )
3378 )
3354 data.update(self.get_api_data())
3379 data.update(self.get_api_data())
3355 return data
3380 return data
3356 # SCM functions
3381 # SCM functions
3357
3382
3358 def scm_instance(self, **kwargs):
3383 def scm_instance(self, **kwargs):
3359 from rhodecode.lib.vcs import get_repo
3384 from rhodecode.lib.vcs import get_repo
3360 base_path = self.base_path()
3385 base_path = self.base_path()
3361 return get_repo(os.path.join(*map(safe_str,
3386 return get_repo(os.path.join(*map(safe_str,
3362 [base_path, self.gist_access_id])))
3387 [base_path, self.gist_access_id])))
3363
3388
3364
3389
3365 class DbMigrateVersion(Base, BaseModel):
3390 class DbMigrateVersion(Base, BaseModel):
3366 __tablename__ = 'db_migrate_version'
3391 __tablename__ = 'db_migrate_version'
3367 __table_args__ = (
3392 __table_args__ = (
3368 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3393 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3369 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
3394 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
3370 )
3395 )
3371 repository_id = Column('repository_id', String(250), primary_key=True)
3396 repository_id = Column('repository_id', String(250), primary_key=True)
3372 repository_path = Column('repository_path', Text)
3397 repository_path = Column('repository_path', Text)
3373 version = Column('version', Integer)
3398 version = Column('version', Integer)
3374
3399
3375
3400
3376 class ExternalIdentity(Base, BaseModel):
3401 class ExternalIdentity(Base, BaseModel):
3377 __tablename__ = 'external_identities'
3402 __tablename__ = 'external_identities'
3378 __table_args__ = (
3403 __table_args__ = (
3379 Index('local_user_id_idx', 'local_user_id'),
3404 Index('local_user_id_idx', 'local_user_id'),
3380 Index('external_id_idx', 'external_id'),
3405 Index('external_id_idx', 'external_id'),
3381 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3406 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3382 'mysql_charset': 'utf8'})
3407 'mysql_charset': 'utf8'})
3383
3408
3384 external_id = Column('external_id', Unicode(255), default=u'',
3409 external_id = Column('external_id', Unicode(255), default=u'',
3385 primary_key=True)
3410 primary_key=True)
3386 external_username = Column('external_username', Unicode(1024), default=u'')
3411 external_username = Column('external_username', Unicode(1024), default=u'')
3387 local_user_id = Column('local_user_id', Integer(),
3412 local_user_id = Column('local_user_id', Integer(),
3388 ForeignKey('users.user_id'), primary_key=True)
3413 ForeignKey('users.user_id'), primary_key=True)
3389 provider_name = Column('provider_name', Unicode(255), default=u'',
3414 provider_name = Column('provider_name', Unicode(255), default=u'',
3390 primary_key=True)
3415 primary_key=True)
3391 access_token = Column('access_token', String(1024), default=u'')
3416 access_token = Column('access_token', String(1024), default=u'')
3392 alt_token = Column('alt_token', String(1024), default=u'')
3417 alt_token = Column('alt_token', String(1024), default=u'')
3393 token_secret = Column('token_secret', String(1024), default=u'')
3418 token_secret = Column('token_secret', String(1024), default=u'')
3394
3419
3395 @classmethod
3420 @classmethod
3396 def by_external_id_and_provider(cls, external_id, provider_name,
3421 def by_external_id_and_provider(cls, external_id, provider_name,
3397 local_user_id=None):
3422 local_user_id=None):
3398 """
3423 """
3399 Returns ExternalIdentity instance based on search params
3424 Returns ExternalIdentity instance based on search params
3400
3425
3401 :param external_id:
3426 :param external_id:
3402 :param provider_name:
3427 :param provider_name:
3403 :return: ExternalIdentity
3428 :return: ExternalIdentity
3404 """
3429 """
3405 query = cls.query()
3430 query = cls.query()
3406 query = query.filter(cls.external_id == external_id)
3431 query = query.filter(cls.external_id == external_id)
3407 query = query.filter(cls.provider_name == provider_name)
3432 query = query.filter(cls.provider_name == provider_name)
3408 if local_user_id:
3433 if local_user_id:
3409 query = query.filter(cls.local_user_id == local_user_id)
3434 query = query.filter(cls.local_user_id == local_user_id)
3410 return query.first()
3435 return query.first()
3411
3436
3412 @classmethod
3437 @classmethod
3413 def user_by_external_id_and_provider(cls, external_id, provider_name):
3438 def user_by_external_id_and_provider(cls, external_id, provider_name):
3414 """
3439 """
3415 Returns User instance based on search params
3440 Returns User instance based on search params
3416
3441
3417 :param external_id:
3442 :param external_id:
3418 :param provider_name:
3443 :param provider_name:
3419 :return: User
3444 :return: User
3420 """
3445 """
3421 query = User.query()
3446 query = User.query()
3422 query = query.filter(cls.external_id == external_id)
3447 query = query.filter(cls.external_id == external_id)
3423 query = query.filter(cls.provider_name == provider_name)
3448 query = query.filter(cls.provider_name == provider_name)
3424 query = query.filter(User.user_id == cls.local_user_id)
3449 query = query.filter(User.user_id == cls.local_user_id)
3425 return query.first()
3450 return query.first()
3426
3451
3427 @classmethod
3452 @classmethod
3428 def by_local_user_id(cls, local_user_id):
3453 def by_local_user_id(cls, local_user_id):
3429 """
3454 """
3430 Returns all tokens for user
3455 Returns all tokens for user
3431
3456
3432 :param local_user_id:
3457 :param local_user_id:
3433 :return: ExternalIdentity
3458 :return: ExternalIdentity
3434 """
3459 """
3435 query = cls.query()
3460 query = cls.query()
3436 query = query.filter(cls.local_user_id == local_user_id)
3461 query = query.filter(cls.local_user_id == local_user_id)
3437 return query
3462 return query
General Comments 0
You need to be logged in to leave comments. Login now