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