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