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