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