##// END OF EJS Templates
Implemented cache-map on main page to save executing select...
marcink -
r2352:90e06f53 beta
parent child Browse files
Show More
@@ -1,1293 +1,1338 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 from collections import defaultdict
30 from collections import defaultdict
31
31
32 from sqlalchemy import *
32 from sqlalchemy import *
33 from sqlalchemy.ext.hybrid import hybrid_property
33 from sqlalchemy.ext.hybrid import hybrid_property
34 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
34 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
35 from beaker.cache import cache_region, region_invalidate
35 from beaker.cache import cache_region, region_invalidate
36
36
37 from rhodecode.lib.vcs import get_backend
37 from rhodecode.lib.vcs import get_backend
38 from rhodecode.lib.vcs.utils.helpers import get_scm
38 from rhodecode.lib.vcs.utils.helpers import get_scm
39 from rhodecode.lib.vcs.exceptions import VCSError
39 from rhodecode.lib.vcs.exceptions import VCSError
40 from rhodecode.lib.vcs.utils.lazy import LazyProperty
40 from rhodecode.lib.vcs.utils.lazy import LazyProperty
41
41
42 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
42 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
43 safe_unicode
43 safe_unicode
44 from rhodecode.lib.compat import json
44 from rhodecode.lib.compat import json
45 from rhodecode.lib.caching_query import FromCache
45 from rhodecode.lib.caching_query import FromCache
46
46
47 from rhodecode.model.meta import Base, Session
47 from rhodecode.model.meta import Base, Session
48 import hashlib
48 import hashlib
49
49
50 URL_SEP = '/'
50 URL_SEP = '/'
51 log = logging.getLogger(__name__)
51 log = logging.getLogger(__name__)
52
52
53 #==============================================================================
53 #==============================================================================
54 # BASE CLASSES
54 # BASE CLASSES
55 #==============================================================================
55 #==============================================================================
56
56
57 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
57 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
58
58
59
59
60 class ModelSerializer(json.JSONEncoder):
60 class ModelSerializer(json.JSONEncoder):
61 """
61 """
62 Simple Serializer for JSON,
62 Simple Serializer for JSON,
63
63
64 usage::
64 usage::
65
65
66 to make object customized for serialization implement a __json__
66 to make object customized for serialization implement a __json__
67 method that will return a dict for serialization into json
67 method that will return a dict for serialization into json
68
68
69 example::
69 example::
70
70
71 class Task(object):
71 class Task(object):
72
72
73 def __init__(self, name, value):
73 def __init__(self, name, value):
74 self.name = name
74 self.name = name
75 self.value = value
75 self.value = value
76
76
77 def __json__(self):
77 def __json__(self):
78 return dict(name=self.name,
78 return dict(name=self.name,
79 value=self.value)
79 value=self.value)
80
80
81 """
81 """
82
82
83 def default(self, obj):
83 def default(self, obj):
84
84
85 if hasattr(obj, '__json__'):
85 if hasattr(obj, '__json__'):
86 return obj.__json__()
86 return obj.__json__()
87 else:
87 else:
88 return json.JSONEncoder.default(self, obj)
88 return json.JSONEncoder.default(self, obj)
89
89
90
90
91 class BaseModel(object):
91 class BaseModel(object):
92 """
92 """
93 Base Model for all classess
93 Base Model for all classess
94 """
94 """
95
95
96 @classmethod
96 @classmethod
97 def _get_keys(cls):
97 def _get_keys(cls):
98 """return column names for this model """
98 """return column names for this model """
99 return class_mapper(cls).c.keys()
99 return class_mapper(cls).c.keys()
100
100
101 def get_dict(self):
101 def get_dict(self):
102 """
102 """
103 return dict with keys and values corresponding
103 return dict with keys and values corresponding
104 to this model data """
104 to this model data """
105
105
106 d = {}
106 d = {}
107 for k in self._get_keys():
107 for k in self._get_keys():
108 d[k] = getattr(self, k)
108 d[k] = getattr(self, k)
109
109
110 # also use __json__() if present to get additional fields
110 # also use __json__() if present to get additional fields
111 for k, val in getattr(self, '__json__', lambda: {})().iteritems():
111 for k, val in getattr(self, '__json__', lambda: {})().iteritems():
112 d[k] = val
112 d[k] = val
113 return d
113 return d
114
114
115 def get_appstruct(self):
115 def get_appstruct(self):
116 """return list with keys and values tupples corresponding
116 """return list with keys and values tupples corresponding
117 to this model data """
117 to this model data """
118
118
119 l = []
119 l = []
120 for k in self._get_keys():
120 for k in self._get_keys():
121 l.append((k, getattr(self, k),))
121 l.append((k, getattr(self, k),))
122 return l
122 return l
123
123
124 def populate_obj(self, populate_dict):
124 def populate_obj(self, populate_dict):
125 """populate model with data from given populate_dict"""
125 """populate model with data from given populate_dict"""
126
126
127 for k in self._get_keys():
127 for k in self._get_keys():
128 if k in populate_dict:
128 if k in populate_dict:
129 setattr(self, k, populate_dict[k])
129 setattr(self, k, populate_dict[k])
130
130
131 @classmethod
131 @classmethod
132 def query(cls):
132 def query(cls):
133 return Session.query(cls)
133 return Session.query(cls)
134
134
135 @classmethod
135 @classmethod
136 def get(cls, id_):
136 def get(cls, id_):
137 if id_:
137 if id_:
138 return cls.query().get(id_)
138 return cls.query().get(id_)
139
139
140 @classmethod
140 @classmethod
141 def getAll(cls):
141 def getAll(cls):
142 return cls.query().all()
142 return cls.query().all()
143
143
144 @classmethod
144 @classmethod
145 def delete(cls, id_):
145 def delete(cls, id_):
146 obj = cls.query().get(id_)
146 obj = cls.query().get(id_)
147 Session.delete(obj)
147 Session.delete(obj)
148
148
149 def __repr__(self):
149 def __repr__(self):
150 if hasattr(self, '__unicode__'):
150 if hasattr(self, '__unicode__'):
151 # python repr needs to return str
151 # python repr needs to return str
152 return safe_str(self.__unicode__())
152 return safe_str(self.__unicode__())
153 return '<DB:%s>' % (self.__class__.__name__)
153 return '<DB:%s>' % (self.__class__.__name__)
154
154
155 class RhodeCodeSetting(Base, BaseModel):
155 class RhodeCodeSetting(Base, BaseModel):
156 __tablename__ = 'rhodecode_settings'
156 __tablename__ = 'rhodecode_settings'
157 __table_args__ = (
157 __table_args__ = (
158 UniqueConstraint('app_settings_name'),
158 UniqueConstraint('app_settings_name'),
159 {'extend_existing': True, 'mysql_engine':'InnoDB',
159 {'extend_existing': True, 'mysql_engine':'InnoDB',
160 'mysql_charset': 'utf8'}
160 'mysql_charset': 'utf8'}
161 )
161 )
162 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
162 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
163 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
163 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
164 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
164 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
165
165
166 def __init__(self, k='', v=''):
166 def __init__(self, k='', v=''):
167 self.app_settings_name = k
167 self.app_settings_name = k
168 self.app_settings_value = v
168 self.app_settings_value = v
169
169
170 @validates('_app_settings_value')
170 @validates('_app_settings_value')
171 def validate_settings_value(self, key, val):
171 def validate_settings_value(self, key, val):
172 assert type(val) == unicode
172 assert type(val) == unicode
173 return val
173 return val
174
174
175 @hybrid_property
175 @hybrid_property
176 def app_settings_value(self):
176 def app_settings_value(self):
177 v = self._app_settings_value
177 v = self._app_settings_value
178 if self.app_settings_name == 'ldap_active':
178 if self.app_settings_name == 'ldap_active':
179 v = str2bool(v)
179 v = str2bool(v)
180 return v
180 return v
181
181
182 @app_settings_value.setter
182 @app_settings_value.setter
183 def app_settings_value(self, val):
183 def app_settings_value(self, val):
184 """
184 """
185 Setter that will always make sure we use unicode in app_settings_value
185 Setter that will always make sure we use unicode in app_settings_value
186
186
187 :param val:
187 :param val:
188 """
188 """
189 self._app_settings_value = safe_unicode(val)
189 self._app_settings_value = safe_unicode(val)
190
190
191 def __unicode__(self):
191 def __unicode__(self):
192 return u"<%s('%s:%s')>" % (
192 return u"<%s('%s:%s')>" % (
193 self.__class__.__name__,
193 self.__class__.__name__,
194 self.app_settings_name, self.app_settings_value
194 self.app_settings_name, self.app_settings_value
195 )
195 )
196
196
197 @classmethod
197 @classmethod
198 def get_by_name(cls, ldap_key):
198 def get_by_name(cls, ldap_key):
199 return cls.query()\
199 return cls.query()\
200 .filter(cls.app_settings_name == ldap_key).scalar()
200 .filter(cls.app_settings_name == ldap_key).scalar()
201
201
202 @classmethod
202 @classmethod
203 def get_app_settings(cls, cache=False):
203 def get_app_settings(cls, cache=False):
204
204
205 ret = cls.query()
205 ret = cls.query()
206
206
207 if cache:
207 if cache:
208 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
208 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
209
209
210 if not ret:
210 if not ret:
211 raise Exception('Could not get application settings !')
211 raise Exception('Could not get application settings !')
212 settings = {}
212 settings = {}
213 for each in ret:
213 for each in ret:
214 settings['rhodecode_' + each.app_settings_name] = \
214 settings['rhodecode_' + each.app_settings_name] = \
215 each.app_settings_value
215 each.app_settings_value
216
216
217 return settings
217 return settings
218
218
219 @classmethod
219 @classmethod
220 def get_ldap_settings(cls, cache=False):
220 def get_ldap_settings(cls, cache=False):
221 ret = cls.query()\
221 ret = cls.query()\
222 .filter(cls.app_settings_name.startswith('ldap_')).all()
222 .filter(cls.app_settings_name.startswith('ldap_')).all()
223 fd = {}
223 fd = {}
224 for row in ret:
224 for row in ret:
225 fd.update({row.app_settings_name:row.app_settings_value})
225 fd.update({row.app_settings_name:row.app_settings_value})
226
226
227 return fd
227 return fd
228
228
229
229
230 class RhodeCodeUi(Base, BaseModel):
230 class RhodeCodeUi(Base, BaseModel):
231 __tablename__ = 'rhodecode_ui'
231 __tablename__ = 'rhodecode_ui'
232 __table_args__ = (
232 __table_args__ = (
233 UniqueConstraint('ui_key'),
233 UniqueConstraint('ui_key'),
234 {'extend_existing': True, 'mysql_engine':'InnoDB',
234 {'extend_existing': True, 'mysql_engine':'InnoDB',
235 'mysql_charset': 'utf8'}
235 'mysql_charset': 'utf8'}
236 )
236 )
237
237
238 HOOK_UPDATE = 'changegroup.update'
238 HOOK_UPDATE = 'changegroup.update'
239 HOOK_REPO_SIZE = 'changegroup.repo_size'
239 HOOK_REPO_SIZE = 'changegroup.repo_size'
240 HOOK_PUSH = 'pretxnchangegroup.push_logger'
240 HOOK_PUSH = 'pretxnchangegroup.push_logger'
241 HOOK_PULL = 'preoutgoing.pull_logger'
241 HOOK_PULL = 'preoutgoing.pull_logger'
242
242
243 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
243 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
244 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
244 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
245 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
245 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
246 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
246 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
247 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
247 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
248
248
249 @classmethod
249 @classmethod
250 def get_by_key(cls, key):
250 def get_by_key(cls, key):
251 return cls.query().filter(cls.ui_key == key)
251 return cls.query().filter(cls.ui_key == key)
252
252
253 @classmethod
253 @classmethod
254 def get_builtin_hooks(cls):
254 def get_builtin_hooks(cls):
255 q = cls.query()
255 q = cls.query()
256 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
256 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
257 cls.HOOK_REPO_SIZE,
257 cls.HOOK_REPO_SIZE,
258 cls.HOOK_PUSH, cls.HOOK_PULL]))
258 cls.HOOK_PUSH, cls.HOOK_PULL]))
259 return q.all()
259 return q.all()
260
260
261 @classmethod
261 @classmethod
262 def get_custom_hooks(cls):
262 def get_custom_hooks(cls):
263 q = cls.query()
263 q = cls.query()
264 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
264 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
265 cls.HOOK_REPO_SIZE,
265 cls.HOOK_REPO_SIZE,
266 cls.HOOK_PUSH, cls.HOOK_PULL]))
266 cls.HOOK_PUSH, cls.HOOK_PULL]))
267 q = q.filter(cls.ui_section == 'hooks')
267 q = q.filter(cls.ui_section == 'hooks')
268 return q.all()
268 return q.all()
269
269
270 @classmethod
270 @classmethod
271 def create_or_update_hook(cls, key, val):
271 def create_or_update_hook(cls, key, val):
272 new_ui = cls.get_by_key(key).scalar() or cls()
272 new_ui = cls.get_by_key(key).scalar() or cls()
273 new_ui.ui_section = 'hooks'
273 new_ui.ui_section = 'hooks'
274 new_ui.ui_active = True
274 new_ui.ui_active = True
275 new_ui.ui_key = key
275 new_ui.ui_key = key
276 new_ui.ui_value = val
276 new_ui.ui_value = val
277
277
278 Session.add(new_ui)
278 Session.add(new_ui)
279
279
280
280
281 class User(Base, BaseModel):
281 class User(Base, BaseModel):
282 __tablename__ = 'users'
282 __tablename__ = 'users'
283 __table_args__ = (
283 __table_args__ = (
284 UniqueConstraint('username'), UniqueConstraint('email'),
284 UniqueConstraint('username'), UniqueConstraint('email'),
285 {'extend_existing': True, 'mysql_engine':'InnoDB',
285 {'extend_existing': True, 'mysql_engine':'InnoDB',
286 'mysql_charset': 'utf8'}
286 'mysql_charset': 'utf8'}
287 )
287 )
288 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
288 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
289 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
289 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
290 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
290 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
291 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
291 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
292 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
292 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
293 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
293 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
294 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
294 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
295 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
295 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
296 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
296 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
297 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
297 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
298 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
298 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
299
299
300 user_log = relationship('UserLog', cascade='all')
300 user_log = relationship('UserLog', cascade='all')
301 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
301 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
302
302
303 repositories = relationship('Repository')
303 repositories = relationship('Repository')
304 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
304 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
305 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
305 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
306 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
306 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
307
307
308 group_member = relationship('UsersGroupMember', cascade='all')
308 group_member = relationship('UsersGroupMember', cascade='all')
309
309
310 notifications = relationship('UserNotification', cascade='all')
310 notifications = relationship('UserNotification', cascade='all')
311 # notifications assigned to this user
311 # notifications assigned to this user
312 user_created_notifications = relationship('Notification', cascade='all')
312 user_created_notifications = relationship('Notification', cascade='all')
313 # comments created by this user
313 # comments created by this user
314 user_comments = relationship('ChangesetComment', cascade='all')
314 user_comments = relationship('ChangesetComment', cascade='all')
315
315
316 @hybrid_property
316 @hybrid_property
317 def email(self):
317 def email(self):
318 return self._email
318 return self._email
319
319
320 @email.setter
320 @email.setter
321 def email(self, val):
321 def email(self, val):
322 self._email = val.lower() if val else None
322 self._email = val.lower() if val else None
323
323
324 @property
324 @property
325 def full_name(self):
325 def full_name(self):
326 return '%s %s' % (self.name, self.lastname)
326 return '%s %s' % (self.name, self.lastname)
327
327
328 @property
328 @property
329 def full_name_or_username(self):
329 def full_name_or_username(self):
330 return ('%s %s' % (self.name, self.lastname)
330 return ('%s %s' % (self.name, self.lastname)
331 if (self.name and self.lastname) else self.username)
331 if (self.name and self.lastname) else self.username)
332
332
333 @property
333 @property
334 def full_contact(self):
334 def full_contact(self):
335 return '%s %s <%s>' % (self.name, self.lastname, self.email)
335 return '%s %s <%s>' % (self.name, self.lastname, self.email)
336
336
337 @property
337 @property
338 def short_contact(self):
338 def short_contact(self):
339 return '%s %s' % (self.name, self.lastname)
339 return '%s %s' % (self.name, self.lastname)
340
340
341 @property
341 @property
342 def is_admin(self):
342 def is_admin(self):
343 return self.admin
343 return self.admin
344
344
345 def __unicode__(self):
345 def __unicode__(self):
346 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
346 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
347 self.user_id, self.username)
347 self.user_id, self.username)
348
348
349 @classmethod
349 @classmethod
350 def get_by_username(cls, username, case_insensitive=False, cache=False):
350 def get_by_username(cls, username, case_insensitive=False, cache=False):
351 if case_insensitive:
351 if case_insensitive:
352 q = cls.query().filter(cls.username.ilike(username))
352 q = cls.query().filter(cls.username.ilike(username))
353 else:
353 else:
354 q = cls.query().filter(cls.username == username)
354 q = cls.query().filter(cls.username == username)
355
355
356 if cache:
356 if cache:
357 q = q.options(FromCache(
357 q = q.options(FromCache(
358 "sql_cache_short",
358 "sql_cache_short",
359 "get_user_%s" % _hash_key(username)
359 "get_user_%s" % _hash_key(username)
360 )
360 )
361 )
361 )
362 return q.scalar()
362 return q.scalar()
363
363
364 @classmethod
364 @classmethod
365 def get_by_api_key(cls, api_key, cache=False):
365 def get_by_api_key(cls, api_key, cache=False):
366 q = cls.query().filter(cls.api_key == api_key)
366 q = cls.query().filter(cls.api_key == api_key)
367
367
368 if cache:
368 if cache:
369 q = q.options(FromCache("sql_cache_short",
369 q = q.options(FromCache("sql_cache_short",
370 "get_api_key_%s" % api_key))
370 "get_api_key_%s" % api_key))
371 return q.scalar()
371 return q.scalar()
372
372
373 @classmethod
373 @classmethod
374 def get_by_email(cls, email, case_insensitive=False, cache=False):
374 def get_by_email(cls, email, case_insensitive=False, cache=False):
375 if case_insensitive:
375 if case_insensitive:
376 q = cls.query().filter(cls.email.ilike(email))
376 q = cls.query().filter(cls.email.ilike(email))
377 else:
377 else:
378 q = cls.query().filter(cls.email == email)
378 q = cls.query().filter(cls.email == email)
379
379
380 if cache:
380 if cache:
381 q = q.options(FromCache("sql_cache_short",
381 q = q.options(FromCache("sql_cache_short",
382 "get_api_key_%s" % email))
382 "get_api_key_%s" % email))
383 return q.scalar()
383 return q.scalar()
384
384
385 def update_lastlogin(self):
385 def update_lastlogin(self):
386 """Update user lastlogin"""
386 """Update user lastlogin"""
387 self.last_login = datetime.datetime.now()
387 self.last_login = datetime.datetime.now()
388 Session.add(self)
388 Session.add(self)
389 log.debug('updated user %s lastlogin' % self.username)
389 log.debug('updated user %s lastlogin' % self.username)
390
390
391 def __json__(self):
391 def __json__(self):
392 return dict(
392 return dict(
393 user_id=self.user_id,
393 user_id=self.user_id,
394 first_name=self.name,
394 first_name=self.name,
395 last_name=self.lastname,
395 last_name=self.lastname,
396 email=self.email,
396 email=self.email,
397 full_name=self.full_name,
397 full_name=self.full_name,
398 full_name_or_username=self.full_name_or_username,
398 full_name_or_username=self.full_name_or_username,
399 short_contact=self.short_contact,
399 short_contact=self.short_contact,
400 full_contact=self.full_contact
400 full_contact=self.full_contact
401 )
401 )
402
402
403
403
404 class UserLog(Base, BaseModel):
404 class UserLog(Base, BaseModel):
405 __tablename__ = 'user_logs'
405 __tablename__ = 'user_logs'
406 __table_args__ = (
406 __table_args__ = (
407 {'extend_existing': True, 'mysql_engine':'InnoDB',
407 {'extend_existing': True, 'mysql_engine':'InnoDB',
408 'mysql_charset': 'utf8'},
408 'mysql_charset': 'utf8'},
409 )
409 )
410 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
410 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
411 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
411 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
412 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
412 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
413 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
413 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
414 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
414 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
415 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
415 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
416 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
416 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
417
417
418 @property
418 @property
419 def action_as_day(self):
419 def action_as_day(self):
420 return datetime.date(*self.action_date.timetuple()[:3])
420 return datetime.date(*self.action_date.timetuple()[:3])
421
421
422 user = relationship('User')
422 user = relationship('User')
423 repository = relationship('Repository', cascade='')
423 repository = relationship('Repository', cascade='')
424
424
425
425
426 class UsersGroup(Base, BaseModel):
426 class UsersGroup(Base, BaseModel):
427 __tablename__ = 'users_groups'
427 __tablename__ = 'users_groups'
428 __table_args__ = (
428 __table_args__ = (
429 {'extend_existing': True, 'mysql_engine':'InnoDB',
429 {'extend_existing': True, 'mysql_engine':'InnoDB',
430 'mysql_charset': 'utf8'},
430 'mysql_charset': 'utf8'},
431 )
431 )
432
432
433 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
433 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
434 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
434 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
435 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
435 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
436
436
437 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
437 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
438 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
438 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
439 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
439 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
440
440
441 def __unicode__(self):
441 def __unicode__(self):
442 return u'<userGroup(%s)>' % (self.users_group_name)
442 return u'<userGroup(%s)>' % (self.users_group_name)
443
443
444 @classmethod
444 @classmethod
445 def get_by_group_name(cls, group_name, cache=False,
445 def get_by_group_name(cls, group_name, cache=False,
446 case_insensitive=False):
446 case_insensitive=False):
447 if case_insensitive:
447 if case_insensitive:
448 q = cls.query().filter(cls.users_group_name.ilike(group_name))
448 q = cls.query().filter(cls.users_group_name.ilike(group_name))
449 else:
449 else:
450 q = cls.query().filter(cls.users_group_name == group_name)
450 q = cls.query().filter(cls.users_group_name == group_name)
451 if cache:
451 if cache:
452 q = q.options(FromCache(
452 q = q.options(FromCache(
453 "sql_cache_short",
453 "sql_cache_short",
454 "get_user_%s" % _hash_key(group_name)
454 "get_user_%s" % _hash_key(group_name)
455 )
455 )
456 )
456 )
457 return q.scalar()
457 return q.scalar()
458
458
459 @classmethod
459 @classmethod
460 def get(cls, users_group_id, cache=False):
460 def get(cls, users_group_id, cache=False):
461 users_group = cls.query()
461 users_group = cls.query()
462 if cache:
462 if cache:
463 users_group = users_group.options(FromCache("sql_cache_short",
463 users_group = users_group.options(FromCache("sql_cache_short",
464 "get_users_group_%s" % users_group_id))
464 "get_users_group_%s" % users_group_id))
465 return users_group.get(users_group_id)
465 return users_group.get(users_group_id)
466
466
467
467
468 class UsersGroupMember(Base, BaseModel):
468 class UsersGroupMember(Base, BaseModel):
469 __tablename__ = 'users_groups_members'
469 __tablename__ = 'users_groups_members'
470 __table_args__ = (
470 __table_args__ = (
471 {'extend_existing': True, 'mysql_engine':'InnoDB',
471 {'extend_existing': True, 'mysql_engine':'InnoDB',
472 'mysql_charset': 'utf8'},
472 'mysql_charset': 'utf8'},
473 )
473 )
474
474
475 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
475 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
476 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
476 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
477 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
477 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
478
478
479 user = relationship('User', lazy='joined')
479 user = relationship('User', lazy='joined')
480 users_group = relationship('UsersGroup')
480 users_group = relationship('UsersGroup')
481
481
482 def __init__(self, gr_id='', u_id=''):
482 def __init__(self, gr_id='', u_id=''):
483 self.users_group_id = gr_id
483 self.users_group_id = gr_id
484 self.user_id = u_id
484 self.user_id = u_id
485
485
486
486
487 class Repository(Base, BaseModel):
487 class Repository(Base, BaseModel):
488 __tablename__ = 'repositories'
488 __tablename__ = 'repositories'
489 __table_args__ = (
489 __table_args__ = (
490 UniqueConstraint('repo_name'),
490 UniqueConstraint('repo_name'),
491 {'extend_existing': True, 'mysql_engine':'InnoDB',
491 {'extend_existing': True, 'mysql_engine':'InnoDB',
492 'mysql_charset': 'utf8'},
492 'mysql_charset': 'utf8'},
493 )
493 )
494
494
495 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
495 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
496 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
496 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
497 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
497 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
498 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
498 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
499 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
499 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
500 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
500 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
501 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
501 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
502 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
502 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
503 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
503 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
504 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
504 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
505
505
506 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
506 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
507 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
507 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
508
508
509 user = relationship('User')
509 user = relationship('User')
510 fork = relationship('Repository', remote_side=repo_id)
510 fork = relationship('Repository', remote_side=repo_id)
511 group = relationship('RepoGroup')
511 group = relationship('RepoGroup')
512 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
512 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
513 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
513 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
514 stats = relationship('Statistics', cascade='all', uselist=False)
514 stats = relationship('Statistics', cascade='all', uselist=False)
515
515
516 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
516 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
517
517
518 logs = relationship('UserLog')
518 logs = relationship('UserLog')
519
519
520 def __unicode__(self):
520 def __unicode__(self):
521 return u"<%s('%s:%s')>" % (self.__class__.__name__,self.repo_id,
521 return u"<%s('%s:%s')>" % (self.__class__.__name__,self.repo_id,
522 self.repo_name)
522 self.repo_name)
523
523
524 @classmethod
524 @classmethod
525 def url_sep(cls):
525 def url_sep(cls):
526 return URL_SEP
526 return URL_SEP
527
527
528 @classmethod
528 @classmethod
529 def get_by_repo_name(cls, repo_name):
529 def get_by_repo_name(cls, repo_name):
530 q = Session.query(cls).filter(cls.repo_name == repo_name)
530 q = Session.query(cls).filter(cls.repo_name == repo_name)
531 q = q.options(joinedload(Repository.fork))\
531 q = q.options(joinedload(Repository.fork))\
532 .options(joinedload(Repository.user))\
532 .options(joinedload(Repository.user))\
533 .options(joinedload(Repository.group))
533 .options(joinedload(Repository.group))
534 return q.scalar()
534 return q.scalar()
535
535
536 @classmethod
536 @classmethod
537 def get_repo_forks(cls, repo_id):
537 def get_repo_forks(cls, repo_id):
538 return cls.query().filter(Repository.fork_id == repo_id)
538 return cls.query().filter(Repository.fork_id == repo_id)
539
539
540 @classmethod
540 @classmethod
541 def base_path(cls):
541 def base_path(cls):
542 """
542 """
543 Returns base path when all repos are stored
543 Returns base path when all repos are stored
544
544
545 :param cls:
545 :param cls:
546 """
546 """
547 q = Session.query(RhodeCodeUi)\
547 q = Session.query(RhodeCodeUi)\
548 .filter(RhodeCodeUi.ui_key == cls.url_sep())
548 .filter(RhodeCodeUi.ui_key == cls.url_sep())
549 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
549 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
550 return q.one().ui_value
550 return q.one().ui_value
551
551
552 @property
552 @property
553 def just_name(self):
553 def just_name(self):
554 return self.repo_name.split(Repository.url_sep())[-1]
554 return self.repo_name.split(Repository.url_sep())[-1]
555
555
556 @property
556 @property
557 def groups_with_parents(self):
557 def groups_with_parents(self):
558 groups = []
558 groups = []
559 if self.group is None:
559 if self.group is None:
560 return groups
560 return groups
561
561
562 cur_gr = self.group
562 cur_gr = self.group
563 groups.insert(0, cur_gr)
563 groups.insert(0, cur_gr)
564 while 1:
564 while 1:
565 gr = getattr(cur_gr, 'parent_group', None)
565 gr = getattr(cur_gr, 'parent_group', None)
566 cur_gr = cur_gr.parent_group
566 cur_gr = cur_gr.parent_group
567 if gr is None:
567 if gr is None:
568 break
568 break
569 groups.insert(0, gr)
569 groups.insert(0, gr)
570
570
571 return groups
571 return groups
572
572
573 @property
573 @property
574 def groups_and_repo(self):
574 def groups_and_repo(self):
575 return self.groups_with_parents, self.just_name
575 return self.groups_with_parents, self.just_name
576
576
577 @LazyProperty
577 @LazyProperty
578 def repo_path(self):
578 def repo_path(self):
579 """
579 """
580 Returns base full path for that repository means where it actually
580 Returns base full path for that repository means where it actually
581 exists on a filesystem
581 exists on a filesystem
582 """
582 """
583 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
583 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
584 Repository.url_sep())
584 Repository.url_sep())
585 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
585 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
586 return q.one().ui_value
586 return q.one().ui_value
587
587
588 @property
588 @property
589 def repo_full_path(self):
589 def repo_full_path(self):
590 p = [self.repo_path]
590 p = [self.repo_path]
591 # we need to split the name by / since this is how we store the
591 # we need to split the name by / since this is how we store the
592 # names in the database, but that eventually needs to be converted
592 # names in the database, but that eventually needs to be converted
593 # into a valid system path
593 # into a valid system path
594 p += self.repo_name.split(Repository.url_sep())
594 p += self.repo_name.split(Repository.url_sep())
595 return os.path.join(*p)
595 return os.path.join(*p)
596
596
597 def get_new_name(self, repo_name):
597 def get_new_name(self, repo_name):
598 """
598 """
599 returns new full repository name based on assigned group and new new
599 returns new full repository name based on assigned group and new new
600
600
601 :param group_name:
601 :param group_name:
602 """
602 """
603 path_prefix = self.group.full_path_splitted if self.group else []
603 path_prefix = self.group.full_path_splitted if self.group else []
604 return Repository.url_sep().join(path_prefix + [repo_name])
604 return Repository.url_sep().join(path_prefix + [repo_name])
605
605
606 @property
606 @property
607 def _ui(self):
607 def _ui(self):
608 """
608 """
609 Creates an db based ui object for this repository
609 Creates an db based ui object for this repository
610 """
610 """
611 from mercurial import ui
611 from mercurial import ui
612 from mercurial import config
612 from mercurial import config
613 baseui = ui.ui()
613 baseui = ui.ui()
614
614
615 #clean the baseui object
615 #clean the baseui object
616 baseui._ocfg = config.config()
616 baseui._ocfg = config.config()
617 baseui._ucfg = config.config()
617 baseui._ucfg = config.config()
618 baseui._tcfg = config.config()
618 baseui._tcfg = config.config()
619
619
620 ret = RhodeCodeUi.query()\
620 ret = RhodeCodeUi.query()\
621 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
621 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
622
622
623 hg_ui = ret
623 hg_ui = ret
624 for ui_ in hg_ui:
624 for ui_ in hg_ui:
625 if ui_.ui_active:
625 if ui_.ui_active:
626 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
626 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
627 ui_.ui_key, ui_.ui_value)
627 ui_.ui_key, ui_.ui_value)
628 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
628 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
629
629
630 return baseui
630 return baseui
631
631
632 @classmethod
632 @classmethod
633 def is_valid(cls, repo_name):
633 def is_valid(cls, repo_name):
634 """
634 """
635 returns True if given repo name is a valid filesystem repository
635 returns True if given repo name is a valid filesystem repository
636
636
637 :param cls:
637 :param cls:
638 :param repo_name:
638 :param repo_name:
639 """
639 """
640 from rhodecode.lib.utils import is_valid_repo
640 from rhodecode.lib.utils import is_valid_repo
641
641
642 return is_valid_repo(repo_name, cls.base_path())
642 return is_valid_repo(repo_name, cls.base_path())
643
643
644 #==========================================================================
644 #==========================================================================
645 # SCM PROPERTIES
645 # SCM PROPERTIES
646 #==========================================================================
646 #==========================================================================
647
647
648 def get_changeset(self, rev=None):
648 def get_changeset(self, rev=None):
649 return get_changeset_safe(self.scm_instance, rev)
649 return get_changeset_safe(self.scm_instance, rev)
650
650
651 @property
651 @property
652 def tip(self):
652 def tip(self):
653 return self.get_changeset('tip')
653 return self.get_changeset('tip')
654
654
655 @property
655 @property
656 def author(self):
656 def author(self):
657 return self.tip.author
657 return self.tip.author
658
658
659 @property
659 @property
660 def last_change(self):
660 def last_change(self):
661 return self.scm_instance.last_change
661 return self.scm_instance.last_change
662
662
663 def comments(self, revisions=None):
663 def comments(self, revisions=None):
664 """
664 """
665 Returns comments for this repository grouped by revisions
665 Returns comments for this repository grouped by revisions
666
666
667 :param revisions: filter query by revisions only
667 :param revisions: filter query by revisions only
668 """
668 """
669 cmts = ChangesetComment.query()\
669 cmts = ChangesetComment.query()\
670 .filter(ChangesetComment.repo == self)
670 .filter(ChangesetComment.repo == self)
671 if revisions:
671 if revisions:
672 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
672 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
673 grouped = defaultdict(list)
673 grouped = defaultdict(list)
674 for cmt in cmts.all():
674 for cmt in cmts.all():
675 grouped[cmt.revision].append(cmt)
675 grouped[cmt.revision].append(cmt)
676 return grouped
676 return grouped
677
677
678 #==========================================================================
678 #==========================================================================
679 # SCM CACHE INSTANCE
679 # SCM CACHE INSTANCE
680 #==========================================================================
680 #==========================================================================
681
681
682 @property
682 @property
683 def invalidate(self):
683 def invalidate(self):
684 return CacheInvalidation.invalidate(self.repo_name)
684 return CacheInvalidation.invalidate(self.repo_name)
685
685
686 def set_invalidate(self):
686 def set_invalidate(self):
687 """
687 """
688 set a cache for invalidation for this instance
688 set a cache for invalidation for this instance
689 """
689 """
690 CacheInvalidation.set_invalidate(self.repo_name)
690 CacheInvalidation.set_invalidate(self.repo_name)
691
691
692 @LazyProperty
692 @LazyProperty
693 def scm_instance(self):
693 def scm_instance(self):
694 return self.__get_instance()
694 return self.__get_instance()
695
695
696 @property
696 def scm_instance_cached(self, cache_map=None):
697 def scm_instance_cached(self):
698 @cache_region('long_term')
697 @cache_region('long_term')
699 def _c(repo_name):
698 def _c(repo_name):
700 return self.__get_instance()
699 return self.__get_instance()
701 rn = self.repo_name
700 rn = self.repo_name
702 log.debug('Getting cached instance of repo')
701 log.debug('Getting cached instance of repo')
703 inv = self.invalidate
702
704 if inv is not None:
703 if cache_map:
704 # get using prefilled cache_map
705 invalidate_repo = cache_map[self.repo_name]
706 if invalidate_repo:
707 invalidate_repo = (None if invalidate_repo.cache_active
708 else invalidate_repo)
709 else:
710 # get from invalidate
711 invalidate_repo = self.invalidate
712
713 if invalidate_repo is not None:
705 region_invalidate(_c, None, rn)
714 region_invalidate(_c, None, rn)
706 # update our cache
715 # update our cache
707 CacheInvalidation.set_valid(inv.cache_key)
716 CacheInvalidation.set_valid(invalidate_repo.cache_key)
708 return _c(rn)
717 return _c(rn)
709
718
710 def __get_instance(self):
719 def __get_instance(self):
711 repo_full_path = self.repo_full_path
720 repo_full_path = self.repo_full_path
712 try:
721 try:
713 alias = get_scm(repo_full_path)[0]
722 alias = get_scm(repo_full_path)[0]
714 log.debug('Creating instance of %s repository' % alias)
723 log.debug('Creating instance of %s repository' % alias)
715 backend = get_backend(alias)
724 backend = get_backend(alias)
716 except VCSError:
725 except VCSError:
717 log.error(traceback.format_exc())
726 log.error(traceback.format_exc())
718 log.error('Perhaps this repository is in db and not in '
727 log.error('Perhaps this repository is in db and not in '
719 'filesystem run rescan repositories with '
728 'filesystem run rescan repositories with '
720 '"destroy old data " option from admin panel')
729 '"destroy old data " option from admin panel')
721 return
730 return
722
731
723 if alias == 'hg':
732 if alias == 'hg':
724
733
725 repo = backend(safe_str(repo_full_path), create=False,
734 repo = backend(safe_str(repo_full_path), create=False,
726 baseui=self._ui)
735 baseui=self._ui)
727 # skip hidden web repository
736 # skip hidden web repository
728 if repo._get_hidden():
737 if repo._get_hidden():
729 return
738 return
730 else:
739 else:
731 repo = backend(repo_full_path, create=False)
740 repo = backend(repo_full_path, create=False)
732
741
733 return repo
742 return repo
734
743
735
744
736 class RepoGroup(Base, BaseModel):
745 class RepoGroup(Base, BaseModel):
737 __tablename__ = 'groups'
746 __tablename__ = 'groups'
738 __table_args__ = (
747 __table_args__ = (
739 UniqueConstraint('group_name', 'group_parent_id'),
748 UniqueConstraint('group_name', 'group_parent_id'),
740 CheckConstraint('group_id != group_parent_id'),
749 CheckConstraint('group_id != group_parent_id'),
741 {'extend_existing': True, 'mysql_engine':'InnoDB',
750 {'extend_existing': True, 'mysql_engine':'InnoDB',
742 'mysql_charset': 'utf8'},
751 'mysql_charset': 'utf8'},
743 )
752 )
744 __mapper_args__ = {'order_by': 'group_name'}
753 __mapper_args__ = {'order_by': 'group_name'}
745
754
746 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
755 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
747 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
756 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
748 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
757 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
749 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
758 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
750
759
751 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
760 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
752 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
761 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
753
762
754 parent_group = relationship('RepoGroup', remote_side=group_id)
763 parent_group = relationship('RepoGroup', remote_side=group_id)
755
764
756 def __init__(self, group_name='', parent_group=None):
765 def __init__(self, group_name='', parent_group=None):
757 self.group_name = group_name
766 self.group_name = group_name
758 self.parent_group = parent_group
767 self.parent_group = parent_group
759
768
760 def __unicode__(self):
769 def __unicode__(self):
761 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
770 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
762 self.group_name)
771 self.group_name)
763
772
764 @classmethod
773 @classmethod
765 def groups_choices(cls):
774 def groups_choices(cls):
766 from webhelpers.html import literal as _literal
775 from webhelpers.html import literal as _literal
767 repo_groups = [('', '')]
776 repo_groups = [('', '')]
768 sep = ' &raquo; '
777 sep = ' &raquo; '
769 _name = lambda k: _literal(sep.join(k))
778 _name = lambda k: _literal(sep.join(k))
770
779
771 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
780 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
772 for x in cls.query().all()])
781 for x in cls.query().all()])
773
782
774 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
783 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
775 return repo_groups
784 return repo_groups
776
785
777 @classmethod
786 @classmethod
778 def url_sep(cls):
787 def url_sep(cls):
779 return URL_SEP
788 return URL_SEP
780
789
781 @classmethod
790 @classmethod
782 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
791 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
783 if case_insensitive:
792 if case_insensitive:
784 gr = cls.query()\
793 gr = cls.query()\
785 .filter(cls.group_name.ilike(group_name))
794 .filter(cls.group_name.ilike(group_name))
786 else:
795 else:
787 gr = cls.query()\
796 gr = cls.query()\
788 .filter(cls.group_name == group_name)
797 .filter(cls.group_name == group_name)
789 if cache:
798 if cache:
790 gr = gr.options(FromCache(
799 gr = gr.options(FromCache(
791 "sql_cache_short",
800 "sql_cache_short",
792 "get_group_%s" % _hash_key(group_name)
801 "get_group_%s" % _hash_key(group_name)
793 )
802 )
794 )
803 )
795 return gr.scalar()
804 return gr.scalar()
796
805
797 @property
806 @property
798 def parents(self):
807 def parents(self):
799 parents_recursion_limit = 5
808 parents_recursion_limit = 5
800 groups = []
809 groups = []
801 if self.parent_group is None:
810 if self.parent_group is None:
802 return groups
811 return groups
803 cur_gr = self.parent_group
812 cur_gr = self.parent_group
804 groups.insert(0, cur_gr)
813 groups.insert(0, cur_gr)
805 cnt = 0
814 cnt = 0
806 while 1:
815 while 1:
807 cnt += 1
816 cnt += 1
808 gr = getattr(cur_gr, 'parent_group', None)
817 gr = getattr(cur_gr, 'parent_group', None)
809 cur_gr = cur_gr.parent_group
818 cur_gr = cur_gr.parent_group
810 if gr is None:
819 if gr is None:
811 break
820 break
812 if cnt == parents_recursion_limit:
821 if cnt == parents_recursion_limit:
813 # this will prevent accidental infinit loops
822 # this will prevent accidental infinit loops
814 log.error('group nested more than %s' %
823 log.error('group nested more than %s' %
815 parents_recursion_limit)
824 parents_recursion_limit)
816 break
825 break
817
826
818 groups.insert(0, gr)
827 groups.insert(0, gr)
819 return groups
828 return groups
820
829
821 @property
830 @property
822 def children(self):
831 def children(self):
823 return RepoGroup.query().filter(RepoGroup.parent_group == self)
832 return RepoGroup.query().filter(RepoGroup.parent_group == self)
824
833
825 @property
834 @property
826 def name(self):
835 def name(self):
827 return self.group_name.split(RepoGroup.url_sep())[-1]
836 return self.group_name.split(RepoGroup.url_sep())[-1]
828
837
829 @property
838 @property
830 def full_path(self):
839 def full_path(self):
831 return self.group_name
840 return self.group_name
832
841
833 @property
842 @property
834 def full_path_splitted(self):
843 def full_path_splitted(self):
835 return self.group_name.split(RepoGroup.url_sep())
844 return self.group_name.split(RepoGroup.url_sep())
836
845
837 @property
846 @property
838 def repositories(self):
847 def repositories(self):
839 return Repository.query()\
848 return Repository.query()\
840 .filter(Repository.group == self)\
849 .filter(Repository.group == self)\
841 .order_by(Repository.repo_name)
850 .order_by(Repository.repo_name)
842
851
843 @property
852 @property
844 def repositories_recursive_count(self):
853 def repositories_recursive_count(self):
845 cnt = self.repositories.count()
854 cnt = self.repositories.count()
846
855
847 def children_count(group):
856 def children_count(group):
848 cnt = 0
857 cnt = 0
849 for child in group.children:
858 for child in group.children:
850 cnt += child.repositories.count()
859 cnt += child.repositories.count()
851 cnt += children_count(child)
860 cnt += children_count(child)
852 return cnt
861 return cnt
853
862
854 return cnt + children_count(self)
863 return cnt + children_count(self)
855
864
856 def get_new_name(self, group_name):
865 def get_new_name(self, group_name):
857 """
866 """
858 returns new full group name based on parent and new name
867 returns new full group name based on parent and new name
859
868
860 :param group_name:
869 :param group_name:
861 """
870 """
862 path_prefix = (self.parent_group.full_path_splitted if
871 path_prefix = (self.parent_group.full_path_splitted if
863 self.parent_group else [])
872 self.parent_group else [])
864 return RepoGroup.url_sep().join(path_prefix + [group_name])
873 return RepoGroup.url_sep().join(path_prefix + [group_name])
865
874
866
875
867 class Permission(Base, BaseModel):
876 class Permission(Base, BaseModel):
868 __tablename__ = 'permissions'
877 __tablename__ = 'permissions'
869 __table_args__ = (
878 __table_args__ = (
870 {'extend_existing': True, 'mysql_engine':'InnoDB',
879 {'extend_existing': True, 'mysql_engine':'InnoDB',
871 'mysql_charset': 'utf8'},
880 'mysql_charset': 'utf8'},
872 )
881 )
873 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
882 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
874 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
883 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
875 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
884 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
876
885
877 def __unicode__(self):
886 def __unicode__(self):
878 return u"<%s('%s:%s')>" % (
887 return u"<%s('%s:%s')>" % (
879 self.__class__.__name__, self.permission_id, self.permission_name
888 self.__class__.__name__, self.permission_id, self.permission_name
880 )
889 )
881
890
882 @classmethod
891 @classmethod
883 def get_by_key(cls, key):
892 def get_by_key(cls, key):
884 return cls.query().filter(cls.permission_name == key).scalar()
893 return cls.query().filter(cls.permission_name == key).scalar()
885
894
886 @classmethod
895 @classmethod
887 def get_default_perms(cls, default_user_id):
896 def get_default_perms(cls, default_user_id):
888 q = Session.query(UserRepoToPerm, Repository, cls)\
897 q = Session.query(UserRepoToPerm, Repository, cls)\
889 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
898 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
890 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
899 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
891 .filter(UserRepoToPerm.user_id == default_user_id)
900 .filter(UserRepoToPerm.user_id == default_user_id)
892
901
893 return q.all()
902 return q.all()
894
903
895 @classmethod
904 @classmethod
896 def get_default_group_perms(cls, default_user_id):
905 def get_default_group_perms(cls, default_user_id):
897 q = Session.query(UserRepoGroupToPerm, RepoGroup, cls)\
906 q = Session.query(UserRepoGroupToPerm, RepoGroup, cls)\
898 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
907 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
899 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
908 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
900 .filter(UserRepoGroupToPerm.user_id == default_user_id)
909 .filter(UserRepoGroupToPerm.user_id == default_user_id)
901
910
902 return q.all()
911 return q.all()
903
912
904
913
905 class UserRepoToPerm(Base, BaseModel):
914 class UserRepoToPerm(Base, BaseModel):
906 __tablename__ = 'repo_to_perm'
915 __tablename__ = 'repo_to_perm'
907 __table_args__ = (
916 __table_args__ = (
908 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
917 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
909 {'extend_existing': True, 'mysql_engine':'InnoDB',
918 {'extend_existing': True, 'mysql_engine':'InnoDB',
910 'mysql_charset': 'utf8'}
919 'mysql_charset': 'utf8'}
911 )
920 )
912 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
921 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
913 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
922 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
914 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
923 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
915 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
924 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
916
925
917 user = relationship('User')
926 user = relationship('User')
918 repository = relationship('Repository')
927 repository = relationship('Repository')
919 permission = relationship('Permission')
928 permission = relationship('Permission')
920
929
921 @classmethod
930 @classmethod
922 def create(cls, user, repository, permission):
931 def create(cls, user, repository, permission):
923 n = cls()
932 n = cls()
924 n.user = user
933 n.user = user
925 n.repository = repository
934 n.repository = repository
926 n.permission = permission
935 n.permission = permission
927 Session.add(n)
936 Session.add(n)
928 return n
937 return n
929
938
930 def __unicode__(self):
939 def __unicode__(self):
931 return u'<user:%s => %s >' % (self.user, self.repository)
940 return u'<user:%s => %s >' % (self.user, self.repository)
932
941
933
942
934 class UserToPerm(Base, BaseModel):
943 class UserToPerm(Base, BaseModel):
935 __tablename__ = 'user_to_perm'
944 __tablename__ = 'user_to_perm'
936 __table_args__ = (
945 __table_args__ = (
937 UniqueConstraint('user_id', 'permission_id'),
946 UniqueConstraint('user_id', 'permission_id'),
938 {'extend_existing': True, 'mysql_engine':'InnoDB',
947 {'extend_existing': True, 'mysql_engine':'InnoDB',
939 'mysql_charset': 'utf8'}
948 'mysql_charset': 'utf8'}
940 )
949 )
941 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
950 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
942 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
951 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
943 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
952 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
944
953
945 user = relationship('User')
954 user = relationship('User')
946 permission = relationship('Permission', lazy='joined')
955 permission = relationship('Permission', lazy='joined')
947
956
948
957
949 class UsersGroupRepoToPerm(Base, BaseModel):
958 class UsersGroupRepoToPerm(Base, BaseModel):
950 __tablename__ = 'users_group_repo_to_perm'
959 __tablename__ = 'users_group_repo_to_perm'
951 __table_args__ = (
960 __table_args__ = (
952 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
961 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
953 {'extend_existing': True, 'mysql_engine':'InnoDB',
962 {'extend_existing': True, 'mysql_engine':'InnoDB',
954 'mysql_charset': 'utf8'}
963 'mysql_charset': 'utf8'}
955 )
964 )
956 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
965 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
957 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
966 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
958 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
967 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
959 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
968 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
960
969
961 users_group = relationship('UsersGroup')
970 users_group = relationship('UsersGroup')
962 permission = relationship('Permission')
971 permission = relationship('Permission')
963 repository = relationship('Repository')
972 repository = relationship('Repository')
964
973
965 @classmethod
974 @classmethod
966 def create(cls, users_group, repository, permission):
975 def create(cls, users_group, repository, permission):
967 n = cls()
976 n = cls()
968 n.users_group = users_group
977 n.users_group = users_group
969 n.repository = repository
978 n.repository = repository
970 n.permission = permission
979 n.permission = permission
971 Session.add(n)
980 Session.add(n)
972 return n
981 return n
973
982
974 def __unicode__(self):
983 def __unicode__(self):
975 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
984 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
976
985
977
986
978 class UsersGroupToPerm(Base, BaseModel):
987 class UsersGroupToPerm(Base, BaseModel):
979 __tablename__ = 'users_group_to_perm'
988 __tablename__ = 'users_group_to_perm'
980 __table_args__ = (
989 __table_args__ = (
981 UniqueConstraint('users_group_id', 'permission_id',),
990 UniqueConstraint('users_group_id', 'permission_id',),
982 {'extend_existing': True, 'mysql_engine':'InnoDB',
991 {'extend_existing': True, 'mysql_engine':'InnoDB',
983 'mysql_charset': 'utf8'}
992 'mysql_charset': 'utf8'}
984 )
993 )
985 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
994 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
986 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
995 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
987 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
996 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
988
997
989 users_group = relationship('UsersGroup')
998 users_group = relationship('UsersGroup')
990 permission = relationship('Permission')
999 permission = relationship('Permission')
991
1000
992
1001
993 class UserRepoGroupToPerm(Base, BaseModel):
1002 class UserRepoGroupToPerm(Base, BaseModel):
994 __tablename__ = 'user_repo_group_to_perm'
1003 __tablename__ = 'user_repo_group_to_perm'
995 __table_args__ = (
1004 __table_args__ = (
996 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1005 UniqueConstraint('user_id', 'group_id', 'permission_id'),
997 {'extend_existing': True, 'mysql_engine':'InnoDB',
1006 {'extend_existing': True, 'mysql_engine':'InnoDB',
998 'mysql_charset': 'utf8'}
1007 'mysql_charset': 'utf8'}
999 )
1008 )
1000
1009
1001 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1010 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1002 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1011 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1003 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1012 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1004 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1013 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1005
1014
1006 user = relationship('User')
1015 user = relationship('User')
1007 group = relationship('RepoGroup')
1016 group = relationship('RepoGroup')
1008 permission = relationship('Permission')
1017 permission = relationship('Permission')
1009
1018
1010
1019
1011 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1020 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1012 __tablename__ = 'users_group_repo_group_to_perm'
1021 __tablename__ = 'users_group_repo_group_to_perm'
1013 __table_args__ = (
1022 __table_args__ = (
1014 UniqueConstraint('users_group_id', 'group_id'),
1023 UniqueConstraint('users_group_id', 'group_id'),
1015 {'extend_existing': True, 'mysql_engine':'InnoDB',
1024 {'extend_existing': True, 'mysql_engine':'InnoDB',
1016 'mysql_charset': 'utf8'}
1025 'mysql_charset': 'utf8'}
1017 )
1026 )
1018
1027
1019 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)
1028 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)
1020 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1029 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1021 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1030 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1022 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1031 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1023
1032
1024 users_group = relationship('UsersGroup')
1033 users_group = relationship('UsersGroup')
1025 permission = relationship('Permission')
1034 permission = relationship('Permission')
1026 group = relationship('RepoGroup')
1035 group = relationship('RepoGroup')
1027
1036
1028
1037
1029 class Statistics(Base, BaseModel):
1038 class Statistics(Base, BaseModel):
1030 __tablename__ = 'statistics'
1039 __tablename__ = 'statistics'
1031 __table_args__ = (
1040 __table_args__ = (
1032 UniqueConstraint('repository_id'),
1041 UniqueConstraint('repository_id'),
1033 {'extend_existing': True, 'mysql_engine':'InnoDB',
1042 {'extend_existing': True, 'mysql_engine':'InnoDB',
1034 'mysql_charset': 'utf8'}
1043 'mysql_charset': 'utf8'}
1035 )
1044 )
1036 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1045 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1037 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1046 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1038 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1047 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1039 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1048 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1040 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1049 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1041 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1050 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1042
1051
1043 repository = relationship('Repository', single_parent=True)
1052 repository = relationship('Repository', single_parent=True)
1044
1053
1045
1054
1046 class UserFollowing(Base, BaseModel):
1055 class UserFollowing(Base, BaseModel):
1047 __tablename__ = 'user_followings'
1056 __tablename__ = 'user_followings'
1048 __table_args__ = (
1057 __table_args__ = (
1049 UniqueConstraint('user_id', 'follows_repository_id'),
1058 UniqueConstraint('user_id', 'follows_repository_id'),
1050 UniqueConstraint('user_id', 'follows_user_id'),
1059 UniqueConstraint('user_id', 'follows_user_id'),
1051 {'extend_existing': True, 'mysql_engine':'InnoDB',
1060 {'extend_existing': True, 'mysql_engine':'InnoDB',
1052 'mysql_charset': 'utf8'}
1061 'mysql_charset': 'utf8'}
1053 )
1062 )
1054
1063
1055 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1064 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1056 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1065 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1057 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1066 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1058 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1067 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1059 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1068 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1060
1069
1061 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1070 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1062
1071
1063 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1072 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1064 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1073 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1065
1074
1066 @classmethod
1075 @classmethod
1067 def get_repo_followers(cls, repo_id):
1076 def get_repo_followers(cls, repo_id):
1068 return cls.query().filter(cls.follows_repo_id == repo_id)
1077 return cls.query().filter(cls.follows_repo_id == repo_id)
1069
1078
1070
1079
1071 class CacheInvalidation(Base, BaseModel):
1080 class CacheInvalidation(Base, BaseModel):
1072 __tablename__ = 'cache_invalidation'
1081 __tablename__ = 'cache_invalidation'
1073 __table_args__ = (
1082 __table_args__ = (
1074 UniqueConstraint('cache_key'),
1083 UniqueConstraint('cache_key'),
1084 Index('key_idx', 'cache_key'),
1075 {'extend_existing': True, 'mysql_engine':'InnoDB',
1085 {'extend_existing': True, 'mysql_engine':'InnoDB',
1076 'mysql_charset': 'utf8'},
1086 'mysql_charset': 'utf8'},
1077 )
1087 )
1078 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1088 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1079 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1089 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1080 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1090 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1081 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1091 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1082
1092
1083 def __init__(self, cache_key, cache_args=''):
1093 def __init__(self, cache_key, cache_args=''):
1084 self.cache_key = cache_key
1094 self.cache_key = cache_key
1085 self.cache_args = cache_args
1095 self.cache_args = cache_args
1086 self.cache_active = False
1096 self.cache_active = False
1087
1097
1088 def __unicode__(self):
1098 def __unicode__(self):
1089 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1099 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1090 self.cache_id, self.cache_key)
1100 self.cache_id, self.cache_key)
1101
1091 @classmethod
1102 @classmethod
1092 def clear_cache(cls):
1103 def clear_cache(cls):
1093 cls.query().delete()
1104 cls.query().delete()
1094
1105
1095 @classmethod
1106 @classmethod
1096 def _get_key(cls, key):
1107 def _get_key(cls, key):
1097 """
1108 """
1098 Wrapper for generating a key, together with a prefix
1109 Wrapper for generating a key, together with a prefix
1099
1110
1100 :param key:
1111 :param key:
1101 """
1112 """
1102 import rhodecode
1113 import rhodecode
1103 prefix = ''
1114 prefix = ''
1104 iid = rhodecode.CONFIG.get('instance_id')
1115 iid = rhodecode.CONFIG.get('instance_id')
1105 if iid:
1116 if iid:
1106 prefix = iid
1117 prefix = iid
1107 return "%s%s" % (prefix, key), prefix, key.rstrip('_README')
1118 return "%s%s" % (prefix, key), prefix, key.rstrip('_README')
1108
1119
1109 @classmethod
1120 @classmethod
1110 def get_by_key(cls, key):
1121 def get_by_key(cls, key):
1111 return cls.query().filter(cls.cache_key == key).scalar()
1122 return cls.query().filter(cls.cache_key == key).scalar()
1112
1123
1113 @classmethod
1124 @classmethod
1114 def _get_or_create_key(cls, key, prefix, org_key):
1125 def _get_or_create_key(cls, key, prefix, org_key):
1115 inv_obj = Session.query(cls).filter(cls.cache_key == key).scalar()
1126 inv_obj = Session.query(cls).filter(cls.cache_key == key).scalar()
1116 if not inv_obj:
1127 if not inv_obj:
1117 try:
1128 try:
1118 inv_obj = CacheInvalidation(key, org_key)
1129 inv_obj = CacheInvalidation(key, org_key)
1119 Session.add(inv_obj)
1130 Session.add(inv_obj)
1120 Session.commit()
1131 #Session.commit()
1121 except Exception:
1132 except Exception:
1122 log.error(traceback.format_exc())
1133 log.error(traceback.format_exc())
1123 Session.rollback()
1134 Session.rollback()
1124 return inv_obj
1135 return inv_obj
1125
1136
1126 @classmethod
1137 @classmethod
1127 def invalidate(cls, key):
1138 def invalidate(cls, key):
1128 """
1139 """
1129 Returns Invalidation object if this given key should be invalidated
1140 Returns Invalidation object if this given key should be invalidated
1130 None otherwise. `cache_active = False` means that this cache
1141 None otherwise. `cache_active = False` means that this cache
1131 state is not valid and needs to be invalidated
1142 state is not valid and needs to be invalidated
1132
1143
1133 :param key:
1144 :param key:
1134 """
1145 """
1135
1146
1136 key, _prefix, _org_key = cls._get_key(key)
1147 key, _prefix, _org_key = cls._get_key(key)
1137 inv = cls._get_or_create_key(key, _prefix, _org_key)
1148 inv = cls._get_or_create_key(key, _prefix, _org_key)
1138
1149
1139 if inv and inv.cache_active is False:
1150 if inv and inv.cache_active is False:
1140 return inv
1151 return inv
1141
1152
1142 @classmethod
1153 @classmethod
1143 def set_invalidate(cls, key):
1154 def set_invalidate(cls, key):
1144 """
1155 """
1145 Mark this Cache key for invalidation
1156 Mark this Cache key for invalidation
1146
1157
1147 :param key:
1158 :param key:
1148 """
1159 """
1149
1160
1150 key, _prefix, _org_key = cls._get_key(key)
1161 key, _prefix, _org_key = cls._get_key(key)
1151 inv_objs = Session.query(cls).filter(cls.cache_args == _org_key).all()
1162 inv_objs = Session.query(cls).filter(cls.cache_args == _org_key).all()
1152 log.debug('marking %s key[s] %s for invalidation' % (len(inv_objs),
1163 log.debug('marking %s key[s] %s for invalidation' % (len(inv_objs),
1153 _org_key))
1164 _org_key))
1154 try:
1165 try:
1155 for inv_obj in inv_objs:
1166 for inv_obj in inv_objs:
1156 if inv_obj:
1167 if inv_obj:
1157 inv_obj.cache_active = False
1168 inv_obj.cache_active = False
1158
1169
1159 Session.add(inv_obj)
1170 Session.add(inv_obj)
1160 Session.commit()
1171 Session.commit()
1161 except Exception:
1172 except Exception:
1162 log.error(traceback.format_exc())
1173 log.error(traceback.format_exc())
1163 Session.rollback()
1174 Session.rollback()
1164
1175
1165 @classmethod
1176 @classmethod
1166 def set_valid(cls, key):
1177 def set_valid(cls, key):
1167 """
1178 """
1168 Mark this cache key as active and currently cached
1179 Mark this cache key as active and currently cached
1169
1180
1170 :param key:
1181 :param key:
1171 """
1182 """
1172 inv_obj = cls.get_by_key(key)
1183 inv_obj = cls.get_by_key(key)
1173 inv_obj.cache_active = True
1184 inv_obj.cache_active = True
1174 Session.add(inv_obj)
1185 Session.add(inv_obj)
1175 Session.commit()
1186 Session.commit()
1176
1187
1188 @classmethod
1189 def get_cache_map(cls):
1190
1191 class cachemapdict(dict):
1192
1193 def __init__(self, *args, **kwargs):
1194 fixkey = kwargs.get('fixkey')
1195 if fixkey:
1196 del kwargs['fixkey']
1197 self.fixkey = fixkey
1198 super(cachemapdict, self).__init__(*args, **kwargs)
1199
1200 def __getattr__(self, name):
1201 key = name
1202 if self.fixkey:
1203 key, _prefix, _org_key = cls._get_key(key)
1204 if key in self.__dict__:
1205 return self.__dict__[key]
1206 else:
1207 return self[key]
1208
1209 def __getitem__(self, key):
1210 if self.fixkey:
1211 key, _prefix, _org_key = cls._get_key(key)
1212 try:
1213 return super(cachemapdict, self).__getitem__(key)
1214 except KeyError:
1215 return
1216
1217 cache_map = cachemapdict(fixkey=True)
1218 for obj in cls.query().all():
1219 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1220 return cache_map
1221
1177
1222
1178 class ChangesetComment(Base, BaseModel):
1223 class ChangesetComment(Base, BaseModel):
1179 __tablename__ = 'changeset_comments'
1224 __tablename__ = 'changeset_comments'
1180 __table_args__ = (
1225 __table_args__ = (
1181 {'extend_existing': True, 'mysql_engine':'InnoDB',
1226 {'extend_existing': True, 'mysql_engine':'InnoDB',
1182 'mysql_charset': 'utf8'},
1227 'mysql_charset': 'utf8'},
1183 )
1228 )
1184 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1229 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1185 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1230 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1186 revision = Column('revision', String(40), nullable=False)
1231 revision = Column('revision', String(40), nullable=False)
1187 line_no = Column('line_no', Unicode(10), nullable=True)
1232 line_no = Column('line_no', Unicode(10), nullable=True)
1188 f_path = Column('f_path', Unicode(1000), nullable=True)
1233 f_path = Column('f_path', Unicode(1000), nullable=True)
1189 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1234 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1190 text = Column('text', Unicode(25000), nullable=False)
1235 text = Column('text', Unicode(25000), nullable=False)
1191 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1236 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1192
1237
1193 author = relationship('User', lazy='joined')
1238 author = relationship('User', lazy='joined')
1194 repo = relationship('Repository')
1239 repo = relationship('Repository')
1195
1240
1196 @classmethod
1241 @classmethod
1197 def get_users(cls, revision):
1242 def get_users(cls, revision):
1198 """
1243 """
1199 Returns user associated with this changesetComment. ie those
1244 Returns user associated with this changesetComment. ie those
1200 who actually commented
1245 who actually commented
1201
1246
1202 :param cls:
1247 :param cls:
1203 :param revision:
1248 :param revision:
1204 """
1249 """
1205 return Session.query(User)\
1250 return Session.query(User)\
1206 .filter(cls.revision == revision)\
1251 .filter(cls.revision == revision)\
1207 .join(ChangesetComment.author).all()
1252 .join(ChangesetComment.author).all()
1208
1253
1209
1254
1210 class Notification(Base, BaseModel):
1255 class Notification(Base, BaseModel):
1211 __tablename__ = 'notifications'
1256 __tablename__ = 'notifications'
1212 __table_args__ = (
1257 __table_args__ = (
1213 {'extend_existing': True, 'mysql_engine':'InnoDB',
1258 {'extend_existing': True, 'mysql_engine':'InnoDB',
1214 'mysql_charset': 'utf8'},
1259 'mysql_charset': 'utf8'},
1215 )
1260 )
1216
1261
1217 TYPE_CHANGESET_COMMENT = u'cs_comment'
1262 TYPE_CHANGESET_COMMENT = u'cs_comment'
1218 TYPE_MESSAGE = u'message'
1263 TYPE_MESSAGE = u'message'
1219 TYPE_MENTION = u'mention'
1264 TYPE_MENTION = u'mention'
1220 TYPE_REGISTRATION = u'registration'
1265 TYPE_REGISTRATION = u'registration'
1221
1266
1222 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1267 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1223 subject = Column('subject', Unicode(512), nullable=True)
1268 subject = Column('subject', Unicode(512), nullable=True)
1224 body = Column('body', Unicode(50000), nullable=True)
1269 body = Column('body', Unicode(50000), nullable=True)
1225 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1270 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1226 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1271 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1227 type_ = Column('type', Unicode(256))
1272 type_ = Column('type', Unicode(256))
1228
1273
1229 created_by_user = relationship('User')
1274 created_by_user = relationship('User')
1230 notifications_to_users = relationship('UserNotification', lazy='joined',
1275 notifications_to_users = relationship('UserNotification', lazy='joined',
1231 cascade="all, delete, delete-orphan")
1276 cascade="all, delete, delete-orphan")
1232
1277
1233 @property
1278 @property
1234 def recipients(self):
1279 def recipients(self):
1235 return [x.user for x in UserNotification.query()\
1280 return [x.user for x in UserNotification.query()\
1236 .filter(UserNotification.notification == self)\
1281 .filter(UserNotification.notification == self)\
1237 .order_by(UserNotification.user).all()]
1282 .order_by(UserNotification.user).all()]
1238
1283
1239 @classmethod
1284 @classmethod
1240 def create(cls, created_by, subject, body, recipients, type_=None):
1285 def create(cls, created_by, subject, body, recipients, type_=None):
1241 if type_ is None:
1286 if type_ is None:
1242 type_ = Notification.TYPE_MESSAGE
1287 type_ = Notification.TYPE_MESSAGE
1243
1288
1244 notification = cls()
1289 notification = cls()
1245 notification.created_by_user = created_by
1290 notification.created_by_user = created_by
1246 notification.subject = subject
1291 notification.subject = subject
1247 notification.body = body
1292 notification.body = body
1248 notification.type_ = type_
1293 notification.type_ = type_
1249 notification.created_on = datetime.datetime.now()
1294 notification.created_on = datetime.datetime.now()
1250
1295
1251 for u in recipients:
1296 for u in recipients:
1252 assoc = UserNotification()
1297 assoc = UserNotification()
1253 assoc.notification = notification
1298 assoc.notification = notification
1254 u.notifications.append(assoc)
1299 u.notifications.append(assoc)
1255 Session.add(notification)
1300 Session.add(notification)
1256 return notification
1301 return notification
1257
1302
1258 @property
1303 @property
1259 def description(self):
1304 def description(self):
1260 from rhodecode.model.notification import NotificationModel
1305 from rhodecode.model.notification import NotificationModel
1261 return NotificationModel().make_description(self)
1306 return NotificationModel().make_description(self)
1262
1307
1263
1308
1264 class UserNotification(Base, BaseModel):
1309 class UserNotification(Base, BaseModel):
1265 __tablename__ = 'user_to_notification'
1310 __tablename__ = 'user_to_notification'
1266 __table_args__ = (
1311 __table_args__ = (
1267 UniqueConstraint('user_id', 'notification_id'),
1312 UniqueConstraint('user_id', 'notification_id'),
1268 {'extend_existing': True, 'mysql_engine':'InnoDB',
1313 {'extend_existing': True, 'mysql_engine':'InnoDB',
1269 'mysql_charset': 'utf8'}
1314 'mysql_charset': 'utf8'}
1270 )
1315 )
1271 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1316 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1272 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1317 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1273 read = Column('read', Boolean, default=False)
1318 read = Column('read', Boolean, default=False)
1274 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1319 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1275
1320
1276 user = relationship('User', lazy="joined")
1321 user = relationship('User', lazy="joined")
1277 notification = relationship('Notification', lazy="joined",
1322 notification = relationship('Notification', lazy="joined",
1278 order_by=lambda: Notification.created_on.desc(),)
1323 order_by=lambda: Notification.created_on.desc(),)
1279
1324
1280 def mark_as_read(self):
1325 def mark_as_read(self):
1281 self.read = True
1326 self.read = True
1282 Session.add(self)
1327 Session.add(self)
1283
1328
1284
1329
1285 class DbMigrateVersion(Base, BaseModel):
1330 class DbMigrateVersion(Base, BaseModel):
1286 __tablename__ = 'db_migrate_version'
1331 __tablename__ = 'db_migrate_version'
1287 __table_args__ = (
1332 __table_args__ = (
1288 {'extend_existing': True, 'mysql_engine':'InnoDB',
1333 {'extend_existing': True, 'mysql_engine':'InnoDB',
1289 'mysql_charset': 'utf8'},
1334 'mysql_charset': 'utf8'},
1290 )
1335 )
1291 repository_id = Column('repository_id', String(250), primary_key=True)
1336 repository_id = Column('repository_id', String(250), primary_key=True)
1292 repository_path = Column('repository_path', Text)
1337 repository_path = Column('repository_path', Text)
1293 version = Column('version', Integer)
1338 version = Column('version', Integer)
@@ -1,466 +1,470 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 import os
25 import os
26 import time
26 import time
27 import traceback
27 import traceback
28 import logging
28 import logging
29 import cStringIO
29 import cStringIO
30
30
31 from rhodecode.lib.vcs import get_backend
31 from rhodecode.lib.vcs import get_backend
32 from rhodecode.lib.vcs.exceptions import RepositoryError
32 from rhodecode.lib.vcs.exceptions import RepositoryError
33 from rhodecode.lib.vcs.utils.lazy import LazyProperty
33 from rhodecode.lib.vcs.utils.lazy import LazyProperty
34 from rhodecode.lib.vcs.nodes import FileNode
34 from rhodecode.lib.vcs.nodes import FileNode
35
35
36 from rhodecode import BACKENDS
36 from rhodecode import BACKENDS
37 from rhodecode.lib import helpers as h
37 from rhodecode.lib import helpers as h
38 from rhodecode.lib.utils2 import safe_str, safe_unicode
38 from rhodecode.lib.utils2 import safe_str, safe_unicode
39 from rhodecode.lib.auth import HasRepoPermissionAny, HasReposGroupPermissionAny
39 from rhodecode.lib.auth import HasRepoPermissionAny, HasReposGroupPermissionAny
40 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
40 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
41 action_logger, EmptyChangeset, REMOVED_REPO_PAT
41 action_logger, EmptyChangeset, REMOVED_REPO_PAT
42 from rhodecode.model import BaseModel
42 from rhodecode.model import BaseModel
43 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
43 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
44 UserFollowing, UserLog, User, RepoGroup
44 UserFollowing, UserLog, User, RepoGroup
45
45
46 log = logging.getLogger(__name__)
46 log = logging.getLogger(__name__)
47
47
48
48
49 class UserTemp(object):
49 class UserTemp(object):
50 def __init__(self, user_id):
50 def __init__(self, user_id):
51 self.user_id = user_id
51 self.user_id = user_id
52
52
53 def __repr__(self):
53 def __repr__(self):
54 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
54 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
55
55
56
56
57 class RepoTemp(object):
57 class RepoTemp(object):
58 def __init__(self, repo_id):
58 def __init__(self, repo_id):
59 self.repo_id = repo_id
59 self.repo_id = repo_id
60
60
61 def __repr__(self):
61 def __repr__(self):
62 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
62 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
63
63
64
64
65 class CachedRepoList(object):
65 class CachedRepoList(object):
66
66
67 def __init__(self, db_repo_list, repos_path, order_by=None):
67 def __init__(self, db_repo_list, repos_path, order_by=None):
68 self.db_repo_list = db_repo_list
68 self.db_repo_list = db_repo_list
69 self.repos_path = repos_path
69 self.repos_path = repos_path
70 self.order_by = order_by
70 self.order_by = order_by
71 self.reversed = (order_by or '').startswith('-')
71 self.reversed = (order_by or '').startswith('-')
72
72
73 def __len__(self):
73 def __len__(self):
74 return len(self.db_repo_list)
74 return len(self.db_repo_list)
75
75
76 def __repr__(self):
76 def __repr__(self):
77 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
77 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
78
78
79 def __iter__(self):
79 def __iter__(self):
80 # pre-propagated cache_map to save executing select statements
81 # for each repo
82 cache_map = CacheInvalidation.get_cache_map()
83
80 for dbr in self.db_repo_list:
84 for dbr in self.db_repo_list:
81 scmr = dbr.scm_instance_cached
85 scmr = dbr.scm_instance_cached(cache_map)
82 # check permission at this level
86 # check permission at this level
83 if not HasRepoPermissionAny(
87 if not HasRepoPermissionAny(
84 'repository.read', 'repository.write', 'repository.admin'
88 'repository.read', 'repository.write', 'repository.admin'
85 )(dbr.repo_name, 'get repo check'):
89 )(dbr.repo_name, 'get repo check'):
86 continue
90 continue
87
91
88 if scmr is None:
92 if scmr is None:
89 log.error(
93 log.error(
90 '%s this repository is present in database but it '
94 '%s this repository is present in database but it '
91 'cannot be created as an scm instance' % dbr.repo_name
95 'cannot be created as an scm instance' % dbr.repo_name
92 )
96 )
93 continue
97 continue
94
98
95 last_change = scmr.last_change
99 last_change = scmr.last_change
96 tip = h.get_changeset_safe(scmr, 'tip')
100 tip = h.get_changeset_safe(scmr, 'tip')
97
101
98 tmp_d = {}
102 tmp_d = {}
99 tmp_d['name'] = dbr.repo_name
103 tmp_d['name'] = dbr.repo_name
100 tmp_d['name_sort'] = tmp_d['name'].lower()
104 tmp_d['name_sort'] = tmp_d['name'].lower()
101 tmp_d['description'] = dbr.description
105 tmp_d['description'] = dbr.description
102 tmp_d['description_sort'] = tmp_d['description']
106 tmp_d['description_sort'] = tmp_d['description']
103 tmp_d['last_change'] = last_change
107 tmp_d['last_change'] = last_change
104 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
108 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
105 tmp_d['tip'] = tip.raw_id
109 tmp_d['tip'] = tip.raw_id
106 tmp_d['tip_sort'] = tip.revision
110 tmp_d['tip_sort'] = tip.revision
107 tmp_d['rev'] = tip.revision
111 tmp_d['rev'] = tip.revision
108 tmp_d['contact'] = dbr.user.full_contact
112 tmp_d['contact'] = dbr.user.full_contact
109 tmp_d['contact_sort'] = tmp_d['contact']
113 tmp_d['contact_sort'] = tmp_d['contact']
110 tmp_d['owner_sort'] = tmp_d['contact']
114 tmp_d['owner_sort'] = tmp_d['contact']
111 tmp_d['repo_archives'] = list(scmr._get_archives())
115 tmp_d['repo_archives'] = list(scmr._get_archives())
112 tmp_d['last_msg'] = tip.message
116 tmp_d['last_msg'] = tip.message
113 tmp_d['author'] = tip.author
117 tmp_d['author'] = tip.author
114 tmp_d['dbrepo'] = dbr.get_dict()
118 tmp_d['dbrepo'] = dbr.get_dict()
115 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork else {}
119 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork else {}
116 yield tmp_d
120 yield tmp_d
117
121
118
122
119 class GroupList(object):
123 class GroupList(object):
120
124
121 def __init__(self, db_repo_group_list):
125 def __init__(self, db_repo_group_list):
122 self.db_repo_group_list = db_repo_group_list
126 self.db_repo_group_list = db_repo_group_list
123
127
124 def __len__(self):
128 def __len__(self):
125 return len(self.db_repo_group_list)
129 return len(self.db_repo_group_list)
126
130
127 def __repr__(self):
131 def __repr__(self):
128 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
132 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
129
133
130 def __iter__(self):
134 def __iter__(self):
131 for dbgr in self.db_repo_group_list:
135 for dbgr in self.db_repo_group_list:
132 # check permission at this level
136 # check permission at this level
133 if not HasReposGroupPermissionAny(
137 if not HasReposGroupPermissionAny(
134 'group.read', 'group.write', 'group.admin'
138 'group.read', 'group.write', 'group.admin'
135 )(dbgr.group_name, 'get group repo check'):
139 )(dbgr.group_name, 'get group repo check'):
136 continue
140 continue
137
141
138 yield dbgr
142 yield dbgr
139
143
140
144
141 class ScmModel(BaseModel):
145 class ScmModel(BaseModel):
142 """
146 """
143 Generic Scm Model
147 Generic Scm Model
144 """
148 """
145
149
146 def __get_repo(self, instance):
150 def __get_repo(self, instance):
147 cls = Repository
151 cls = Repository
148 if isinstance(instance, cls):
152 if isinstance(instance, cls):
149 return instance
153 return instance
150 elif isinstance(instance, int) or str(instance).isdigit():
154 elif isinstance(instance, int) or str(instance).isdigit():
151 return cls.get(instance)
155 return cls.get(instance)
152 elif isinstance(instance, basestring):
156 elif isinstance(instance, basestring):
153 return cls.get_by_repo_name(instance)
157 return cls.get_by_repo_name(instance)
154 elif instance:
158 elif instance:
155 raise Exception('given object must be int, basestr or Instance'
159 raise Exception('given object must be int, basestr or Instance'
156 ' of %s got %s' % (type(cls), type(instance)))
160 ' of %s got %s' % (type(cls), type(instance)))
157
161
158 @LazyProperty
162 @LazyProperty
159 def repos_path(self):
163 def repos_path(self):
160 """
164 """
161 Get's the repositories root path from database
165 Get's the repositories root path from database
162 """
166 """
163
167
164 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
168 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
165
169
166 return q.ui_value
170 return q.ui_value
167
171
168 def repo_scan(self, repos_path=None):
172 def repo_scan(self, repos_path=None):
169 """
173 """
170 Listing of repositories in given path. This path should not be a
174 Listing of repositories in given path. This path should not be a
171 repository itself. Return a dictionary of repository objects
175 repository itself. Return a dictionary of repository objects
172
176
173 :param repos_path: path to directory containing repositories
177 :param repos_path: path to directory containing repositories
174 """
178 """
175
179
176 if repos_path is None:
180 if repos_path is None:
177 repos_path = self.repos_path
181 repos_path = self.repos_path
178
182
179 log.info('scanning for repositories in %s' % repos_path)
183 log.info('scanning for repositories in %s' % repos_path)
180
184
181 baseui = make_ui('db')
185 baseui = make_ui('db')
182 repos = {}
186 repos = {}
183
187
184 for name, path in get_filesystem_repos(repos_path, recursive=True):
188 for name, path in get_filesystem_repos(repos_path, recursive=True):
185 # skip removed repos
189 # skip removed repos
186 if REMOVED_REPO_PAT.match(name):
190 if REMOVED_REPO_PAT.match(name):
187 continue
191 continue
188
192
189 # name need to be decomposed and put back together using the /
193 # name need to be decomposed and put back together using the /
190 # since this is internal storage separator for rhodecode
194 # since this is internal storage separator for rhodecode
191 name = Repository.url_sep().join(name.split(os.sep))
195 name = Repository.url_sep().join(name.split(os.sep))
192
196
193 try:
197 try:
194 if name in repos:
198 if name in repos:
195 raise RepositoryError('Duplicate repository name %s '
199 raise RepositoryError('Duplicate repository name %s '
196 'found in %s' % (name, path))
200 'found in %s' % (name, path))
197 else:
201 else:
198
202
199 klass = get_backend(path[0])
203 klass = get_backend(path[0])
200
204
201 if path[0] == 'hg' and path[0] in BACKENDS.keys():
205 if path[0] == 'hg' and path[0] in BACKENDS.keys():
202 repos[name] = klass(safe_str(path[1]), baseui=baseui)
206 repos[name] = klass(safe_str(path[1]), baseui=baseui)
203
207
204 if path[0] == 'git' and path[0] in BACKENDS.keys():
208 if path[0] == 'git' and path[0] in BACKENDS.keys():
205 repos[name] = klass(path[1])
209 repos[name] = klass(path[1])
206 except OSError:
210 except OSError:
207 continue
211 continue
208
212
209 return repos
213 return repos
210
214
211 def get_repos(self, all_repos=None, sort_key=None):
215 def get_repos(self, all_repos=None, sort_key=None):
212 """
216 """
213 Get all repos from db and for each repo create it's
217 Get all repos from db and for each repo create it's
214 backend instance and fill that backed with information from database
218 backend instance and fill that backed with information from database
215
219
216 :param all_repos: list of repository names as strings
220 :param all_repos: list of repository names as strings
217 give specific repositories list, good for filtering
221 give specific repositories list, good for filtering
218 """
222 """
219 if all_repos is None:
223 if all_repos is None:
220 all_repos = self.sa.query(Repository)\
224 all_repos = self.sa.query(Repository)\
221 .filter(Repository.group_id == None)\
225 .filter(Repository.group_id == None)\
222 .order_by(Repository.repo_name).all()
226 .order_by(Repository.repo_name).all()
223
227
224 repo_iter = CachedRepoList(all_repos, repos_path=self.repos_path,
228 repo_iter = CachedRepoList(all_repos, repos_path=self.repos_path,
225 order_by=sort_key)
229 order_by=sort_key)
226
230
227 return repo_iter
231 return repo_iter
228
232
229 def get_repos_groups(self, all_groups=None):
233 def get_repos_groups(self, all_groups=None):
230 if all_groups is None:
234 if all_groups is None:
231 all_groups = RepoGroup.query()\
235 all_groups = RepoGroup.query()\
232 .filter(RepoGroup.group_parent_id == None).all()
236 .filter(RepoGroup.group_parent_id == None).all()
233 group_iter = GroupList(all_groups)
237 group_iter = GroupList(all_groups)
234
238
235 return group_iter
239 return group_iter
236
240
237 def mark_for_invalidation(self, repo_name):
241 def mark_for_invalidation(self, repo_name):
238 """
242 """
239 Puts cache invalidation task into db for
243 Puts cache invalidation task into db for
240 further global cache invalidation
244 further global cache invalidation
241
245
242 :param repo_name: this repo that should invalidation take place
246 :param repo_name: this repo that should invalidation take place
243 """
247 """
244 CacheInvalidation.set_invalidate(repo_name)
248 CacheInvalidation.set_invalidate(repo_name)
245
249
246 def toggle_following_repo(self, follow_repo_id, user_id):
250 def toggle_following_repo(self, follow_repo_id, user_id):
247
251
248 f = self.sa.query(UserFollowing)\
252 f = self.sa.query(UserFollowing)\
249 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
253 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
250 .filter(UserFollowing.user_id == user_id).scalar()
254 .filter(UserFollowing.user_id == user_id).scalar()
251
255
252 if f is not None:
256 if f is not None:
253 try:
257 try:
254 self.sa.delete(f)
258 self.sa.delete(f)
255 action_logger(UserTemp(user_id),
259 action_logger(UserTemp(user_id),
256 'stopped_following_repo',
260 'stopped_following_repo',
257 RepoTemp(follow_repo_id))
261 RepoTemp(follow_repo_id))
258 return
262 return
259 except:
263 except:
260 log.error(traceback.format_exc())
264 log.error(traceback.format_exc())
261 raise
265 raise
262
266
263 try:
267 try:
264 f = UserFollowing()
268 f = UserFollowing()
265 f.user_id = user_id
269 f.user_id = user_id
266 f.follows_repo_id = follow_repo_id
270 f.follows_repo_id = follow_repo_id
267 self.sa.add(f)
271 self.sa.add(f)
268
272
269 action_logger(UserTemp(user_id),
273 action_logger(UserTemp(user_id),
270 'started_following_repo',
274 'started_following_repo',
271 RepoTemp(follow_repo_id))
275 RepoTemp(follow_repo_id))
272 except:
276 except:
273 log.error(traceback.format_exc())
277 log.error(traceback.format_exc())
274 raise
278 raise
275
279
276 def toggle_following_user(self, follow_user_id, user_id):
280 def toggle_following_user(self, follow_user_id, user_id):
277 f = self.sa.query(UserFollowing)\
281 f = self.sa.query(UserFollowing)\
278 .filter(UserFollowing.follows_user_id == follow_user_id)\
282 .filter(UserFollowing.follows_user_id == follow_user_id)\
279 .filter(UserFollowing.user_id == user_id).scalar()
283 .filter(UserFollowing.user_id == user_id).scalar()
280
284
281 if f is not None:
285 if f is not None:
282 try:
286 try:
283 self.sa.delete(f)
287 self.sa.delete(f)
284 return
288 return
285 except:
289 except:
286 log.error(traceback.format_exc())
290 log.error(traceback.format_exc())
287 raise
291 raise
288
292
289 try:
293 try:
290 f = UserFollowing()
294 f = UserFollowing()
291 f.user_id = user_id
295 f.user_id = user_id
292 f.follows_user_id = follow_user_id
296 f.follows_user_id = follow_user_id
293 self.sa.add(f)
297 self.sa.add(f)
294 except:
298 except:
295 log.error(traceback.format_exc())
299 log.error(traceback.format_exc())
296 raise
300 raise
297
301
298 def is_following_repo(self, repo_name, user_id, cache=False):
302 def is_following_repo(self, repo_name, user_id, cache=False):
299 r = self.sa.query(Repository)\
303 r = self.sa.query(Repository)\
300 .filter(Repository.repo_name == repo_name).scalar()
304 .filter(Repository.repo_name == repo_name).scalar()
301
305
302 f = self.sa.query(UserFollowing)\
306 f = self.sa.query(UserFollowing)\
303 .filter(UserFollowing.follows_repository == r)\
307 .filter(UserFollowing.follows_repository == r)\
304 .filter(UserFollowing.user_id == user_id).scalar()
308 .filter(UserFollowing.user_id == user_id).scalar()
305
309
306 return f is not None
310 return f is not None
307
311
308 def is_following_user(self, username, user_id, cache=False):
312 def is_following_user(self, username, user_id, cache=False):
309 u = User.get_by_username(username)
313 u = User.get_by_username(username)
310
314
311 f = self.sa.query(UserFollowing)\
315 f = self.sa.query(UserFollowing)\
312 .filter(UserFollowing.follows_user == u)\
316 .filter(UserFollowing.follows_user == u)\
313 .filter(UserFollowing.user_id == user_id).scalar()
317 .filter(UserFollowing.user_id == user_id).scalar()
314
318
315 return f is not None
319 return f is not None
316
320
317 def get_followers(self, repo_id):
321 def get_followers(self, repo_id):
318 if not isinstance(repo_id, int):
322 if not isinstance(repo_id, int):
319 repo_id = getattr(Repository.get_by_repo_name(repo_id), 'repo_id')
323 repo_id = getattr(Repository.get_by_repo_name(repo_id), 'repo_id')
320
324
321 return self.sa.query(UserFollowing)\
325 return self.sa.query(UserFollowing)\
322 .filter(UserFollowing.follows_repo_id == repo_id).count()
326 .filter(UserFollowing.follows_repo_id == repo_id).count()
323
327
324 def get_forks(self, repo_id):
328 def get_forks(self, repo_id):
325 if not isinstance(repo_id, int):
329 if not isinstance(repo_id, int):
326 repo_id = getattr(Repository.get_by_repo_name(repo_id), 'repo_id')
330 repo_id = getattr(Repository.get_by_repo_name(repo_id), 'repo_id')
327
331
328 return self.sa.query(Repository)\
332 return self.sa.query(Repository)\
329 .filter(Repository.fork_id == repo_id).count()
333 .filter(Repository.fork_id == repo_id).count()
330
334
331 def mark_as_fork(self, repo, fork, user):
335 def mark_as_fork(self, repo, fork, user):
332 repo = self.__get_repo(repo)
336 repo = self.__get_repo(repo)
333 fork = self.__get_repo(fork)
337 fork = self.__get_repo(fork)
334 repo.fork = fork
338 repo.fork = fork
335 self.sa.add(repo)
339 self.sa.add(repo)
336 return repo
340 return repo
337
341
338 def pull_changes(self, repo_name, username):
342 def pull_changes(self, repo_name, username):
339 dbrepo = Repository.get_by_repo_name(repo_name)
343 dbrepo = Repository.get_by_repo_name(repo_name)
340 clone_uri = dbrepo.clone_uri
344 clone_uri = dbrepo.clone_uri
341 if not clone_uri:
345 if not clone_uri:
342 raise Exception("This repository doesn't have a clone uri")
346 raise Exception("This repository doesn't have a clone uri")
343
347
344 repo = dbrepo.scm_instance
348 repo = dbrepo.scm_instance
345 try:
349 try:
346 extras = {
350 extras = {
347 'ip': '',
351 'ip': '',
348 'username': username,
352 'username': username,
349 'action': 'push_remote',
353 'action': 'push_remote',
350 'repository': repo_name,
354 'repository': repo_name,
351 'scm': repo.alias,
355 'scm': repo.alias,
352 }
356 }
353
357
354 # inject ui extra param to log this action via push logger
358 # inject ui extra param to log this action via push logger
355 for k, v in extras.items():
359 for k, v in extras.items():
356 repo._repo.ui.setconfig('rhodecode_extras', k, v)
360 repo._repo.ui.setconfig('rhodecode_extras', k, v)
357
361
358 repo.pull(clone_uri)
362 repo.pull(clone_uri)
359 self.mark_for_invalidation(repo_name)
363 self.mark_for_invalidation(repo_name)
360 except:
364 except:
361 log.error(traceback.format_exc())
365 log.error(traceback.format_exc())
362 raise
366 raise
363
367
364 def commit_change(self, repo, repo_name, cs, user, author, message,
368 def commit_change(self, repo, repo_name, cs, user, author, message,
365 content, f_path):
369 content, f_path):
366
370
367 if repo.alias == 'hg':
371 if repo.alias == 'hg':
368 from rhodecode.lib.vcs.backends.hg import \
372 from rhodecode.lib.vcs.backends.hg import \
369 MercurialInMemoryChangeset as IMC
373 MercurialInMemoryChangeset as IMC
370 elif repo.alias == 'git':
374 elif repo.alias == 'git':
371 from rhodecode.lib.vcs.backends.git import \
375 from rhodecode.lib.vcs.backends.git import \
372 GitInMemoryChangeset as IMC
376 GitInMemoryChangeset as IMC
373
377
374 # decoding here will force that we have proper encoded values
378 # decoding here will force that we have proper encoded values
375 # in any other case this will throw exceptions and deny commit
379 # in any other case this will throw exceptions and deny commit
376 content = safe_str(content)
380 content = safe_str(content)
377 path = safe_str(f_path)
381 path = safe_str(f_path)
378 # message and author needs to be unicode
382 # message and author needs to be unicode
379 # proper backend should then translate that into required type
383 # proper backend should then translate that into required type
380 message = safe_unicode(message)
384 message = safe_unicode(message)
381 author = safe_unicode(author)
385 author = safe_unicode(author)
382 m = IMC(repo)
386 m = IMC(repo)
383 m.change(FileNode(path, content))
387 m.change(FileNode(path, content))
384 tip = m.commit(message=message,
388 tip = m.commit(message=message,
385 author=author,
389 author=author,
386 parents=[cs], branch=cs.branch)
390 parents=[cs], branch=cs.branch)
387
391
388 new_cs = tip.short_id
392 new_cs = tip.short_id
389 action = 'push_local:%s' % new_cs
393 action = 'push_local:%s' % new_cs
390
394
391 action_logger(user, action, repo_name)
395 action_logger(user, action, repo_name)
392
396
393 self.mark_for_invalidation(repo_name)
397 self.mark_for_invalidation(repo_name)
394
398
395 def create_node(self, repo, repo_name, cs, user, author, message, content,
399 def create_node(self, repo, repo_name, cs, user, author, message, content,
396 f_path):
400 f_path):
397 if repo.alias == 'hg':
401 if repo.alias == 'hg':
398 from rhodecode.lib.vcs.backends.hg import MercurialInMemoryChangeset as IMC
402 from rhodecode.lib.vcs.backends.hg import MercurialInMemoryChangeset as IMC
399 elif repo.alias == 'git':
403 elif repo.alias == 'git':
400 from rhodecode.lib.vcs.backends.git import GitInMemoryChangeset as IMC
404 from rhodecode.lib.vcs.backends.git import GitInMemoryChangeset as IMC
401 # decoding here will force that we have proper encoded values
405 # decoding here will force that we have proper encoded values
402 # in any other case this will throw exceptions and deny commit
406 # in any other case this will throw exceptions and deny commit
403
407
404 if isinstance(content, (basestring,)):
408 if isinstance(content, (basestring,)):
405 content = safe_str(content)
409 content = safe_str(content)
406 elif isinstance(content, (file, cStringIO.OutputType,)):
410 elif isinstance(content, (file, cStringIO.OutputType,)):
407 content = content.read()
411 content = content.read()
408 else:
412 else:
409 raise Exception('Content is of unrecognized type %s' % (
413 raise Exception('Content is of unrecognized type %s' % (
410 type(content)
414 type(content)
411 ))
415 ))
412
416
413 message = safe_unicode(message)
417 message = safe_unicode(message)
414 author = safe_unicode(author)
418 author = safe_unicode(author)
415 path = safe_str(f_path)
419 path = safe_str(f_path)
416 m = IMC(repo)
420 m = IMC(repo)
417
421
418 if isinstance(cs, EmptyChangeset):
422 if isinstance(cs, EmptyChangeset):
419 # EmptyChangeset means we we're editing empty repository
423 # EmptyChangeset means we we're editing empty repository
420 parents = None
424 parents = None
421 else:
425 else:
422 parents = [cs]
426 parents = [cs]
423
427
424 m.add(FileNode(path, content=content))
428 m.add(FileNode(path, content=content))
425 tip = m.commit(message=message,
429 tip = m.commit(message=message,
426 author=author,
430 author=author,
427 parents=parents, branch=cs.branch)
431 parents=parents, branch=cs.branch)
428 new_cs = tip.short_id
432 new_cs = tip.short_id
429 action = 'push_local:%s' % new_cs
433 action = 'push_local:%s' % new_cs
430
434
431 action_logger(user, action, repo_name)
435 action_logger(user, action, repo_name)
432
436
433 self.mark_for_invalidation(repo_name)
437 self.mark_for_invalidation(repo_name)
434
438
435 def get_nodes(self, repo_name, revision, root_path='/', flat=True):
439 def get_nodes(self, repo_name, revision, root_path='/', flat=True):
436 """
440 """
437 recursive walk in root dir and return a set of all path in that dir
441 recursive walk in root dir and return a set of all path in that dir
438 based on repository walk function
442 based on repository walk function
439
443
440 :param repo_name: name of repository
444 :param repo_name: name of repository
441 :param revision: revision for which to list nodes
445 :param revision: revision for which to list nodes
442 :param root_path: root path to list
446 :param root_path: root path to list
443 :param flat: return as a list, if False returns a dict with decription
447 :param flat: return as a list, if False returns a dict with decription
444
448
445 """
449 """
446 _files = list()
450 _files = list()
447 _dirs = list()
451 _dirs = list()
448 try:
452 try:
449 _repo = self.__get_repo(repo_name)
453 _repo = self.__get_repo(repo_name)
450 changeset = _repo.scm_instance.get_changeset(revision)
454 changeset = _repo.scm_instance.get_changeset(revision)
451 root_path = root_path.lstrip('/')
455 root_path = root_path.lstrip('/')
452 for topnode, dirs, files in changeset.walk(root_path):
456 for topnode, dirs, files in changeset.walk(root_path):
453 for f in files:
457 for f in files:
454 _files.append(f.path if flat else {"name": f.path,
458 _files.append(f.path if flat else {"name": f.path,
455 "type": "file"})
459 "type": "file"})
456 for d in dirs:
460 for d in dirs:
457 _dirs.append(d.path if flat else {"name": d.path,
461 _dirs.append(d.path if flat else {"name": d.path,
458 "type": "dir"})
462 "type": "dir"})
459 except RepositoryError:
463 except RepositoryError:
460 log.debug(traceback.format_exc())
464 log.debug(traceback.format_exc())
461 raise
465 raise
462
466
463 return _dirs, _files
467 return _dirs, _files
464
468
465 def get_unread_journal(self):
469 def get_unread_journal(self):
466 return self.sa.query(UserLog).count()
470 return self.sa.query(UserLog).count()
General Comments 0
You need to be logged in to leave comments. Login now