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