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