##// END OF EJS Templates
fixes #245 Rescan of the repositories on Windows
marcink -
r1554:e7c6341a beta
parent child Browse files
Show More
@@ -1,1040 +1,1046 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) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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 datetime import date
30 from datetime import date
31
31
32 from sqlalchemy import *
32 from sqlalchemy import *
33 from sqlalchemy.exc import DatabaseError
33 from sqlalchemy.exc import DatabaseError
34 from sqlalchemy.ext.hybrid import hybrid_property
34 from sqlalchemy.ext.hybrid import hybrid_property
35 from sqlalchemy.orm import relationship, backref, joinedload, class_mapper
35 from sqlalchemy.orm import relationship, backref, joinedload, class_mapper
36 from sqlalchemy.orm.interfaces import MapperExtension
36 from sqlalchemy.orm.interfaces import MapperExtension
37
37
38 from beaker.cache import cache_region, region_invalidate
38 from beaker.cache import cache_region, region_invalidate
39
39
40 from vcs import get_backend
40 from vcs import get_backend
41 from vcs.utils.helpers import get_scm
41 from vcs.utils.helpers import get_scm
42 from vcs.exceptions import VCSError
42 from vcs.exceptions import VCSError
43 from vcs.utils.lazy import LazyProperty
43 from vcs.utils.lazy import LazyProperty
44
44
45 from rhodecode.lib import str2bool, safe_str, get_changeset_safe, \
45 from rhodecode.lib import str2bool, safe_str, get_changeset_safe, \
46 generate_api_key, safe_unicode
46 generate_api_key, safe_unicode
47 from rhodecode.lib.exceptions import UsersGroupsAssignedException
47 from rhodecode.lib.exceptions import UsersGroupsAssignedException
48 from rhodecode.lib.compat import json
48 from rhodecode.lib.compat import json
49
49
50 from rhodecode.model.meta import Base, Session
50 from rhodecode.model.meta import Base, Session
51 from rhodecode.model.caching_query import FromCache
51 from rhodecode.model.caching_query import FromCache
52
52
53
53
54 log = logging.getLogger(__name__)
54 log = logging.getLogger(__name__)
55
55
56 #==============================================================================
56 #==============================================================================
57 # BASE CLASSES
57 # BASE CLASSES
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 class BaseModel(object):
90 class BaseModel(object):
91 """Base Model for all classess
91 """Base Model for all classess
92
92
93 """
93 """
94
94
95 @classmethod
95 @classmethod
96 def _get_keys(cls):
96 def _get_keys(cls):
97 """return column names for this model """
97 """return column names for this model """
98 return class_mapper(cls).c.keys()
98 return class_mapper(cls).c.keys()
99
99
100 def get_dict(self):
100 def get_dict(self):
101 """return dict with keys and values corresponding
101 """return dict with keys and values corresponding
102 to this model data """
102 to this model data """
103
103
104 d = {}
104 d = {}
105 for k in self._get_keys():
105 for k in self._get_keys():
106 d[k] = getattr(self, k)
106 d[k] = getattr(self, k)
107 return d
107 return d
108
108
109 def get_appstruct(self):
109 def get_appstruct(self):
110 """return list with keys and values tupples corresponding
110 """return list with keys and values tupples corresponding
111 to this model data """
111 to this model data """
112
112
113 l = []
113 l = []
114 for k in self._get_keys():
114 for k in self._get_keys():
115 l.append((k, getattr(self, k),))
115 l.append((k, getattr(self, k),))
116 return l
116 return l
117
117
118 def populate_obj(self, populate_dict):
118 def populate_obj(self, populate_dict):
119 """populate model with data from given populate_dict"""
119 """populate model with data from given populate_dict"""
120
120
121 for k in self._get_keys():
121 for k in self._get_keys():
122 if k in populate_dict:
122 if k in populate_dict:
123 setattr(self, k, populate_dict[k])
123 setattr(self, k, populate_dict[k])
124
124
125 @classmethod
125 @classmethod
126 def query(cls):
126 def query(cls):
127 return Session.query(cls)
127 return Session.query(cls)
128
128
129 @classmethod
129 @classmethod
130 def get(cls, id_):
130 def get(cls, id_):
131 if id_:
131 if id_:
132 return Session.query(cls).get(id_)
132 return Session.query(cls).get(id_)
133
133
134 @classmethod
134 @classmethod
135 def delete(cls, id_):
135 def delete(cls, id_):
136 obj = Session.query(cls).get(id_)
136 obj = Session.query(cls).get(id_)
137 Session.delete(obj)
137 Session.delete(obj)
138 Session.commit()
138 Session.commit()
139
139
140
140
141 class RhodeCodeSettings(Base, BaseModel):
141 class RhodeCodeSettings(Base, BaseModel):
142 __tablename__ = 'rhodecode_settings'
142 __tablename__ = 'rhodecode_settings'
143 __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
143 __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
144 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
144 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
145 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
145 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
146 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
146 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
147
147
148 def __init__(self, k='', v=''):
148 def __init__(self, k='', v=''):
149 self.app_settings_name = k
149 self.app_settings_name = k
150 self.app_settings_value = v
150 self.app_settings_value = v
151
151
152
152
153 @hybrid_property
153 @hybrid_property
154 def app_settings_value(self):
154 def app_settings_value(self):
155 v = self._app_settings_value
155 v = self._app_settings_value
156 if v == 'ldap_active':
156 if v == 'ldap_active':
157 v = str2bool(v)
157 v = str2bool(v)
158 return v
158 return v
159
159
160 @app_settings_value.setter
160 @app_settings_value.setter
161 def app_settings_value(self,val):
161 def app_settings_value(self,val):
162 """
162 """
163 Setter that will always make sure we use unicode in app_settings_value
163 Setter that will always make sure we use unicode in app_settings_value
164
164
165 :param val:
165 :param val:
166 """
166 """
167 self._app_settings_value = safe_unicode(val)
167 self._app_settings_value = safe_unicode(val)
168
168
169 def __repr__(self):
169 def __repr__(self):
170 return "<%s('%s:%s')>" % (self.__class__.__name__,
170 return "<%s('%s:%s')>" % (self.__class__.__name__,
171 self.app_settings_name, self.app_settings_value)
171 self.app_settings_name, self.app_settings_value)
172
172
173
173
174 @classmethod
174 @classmethod
175 def get_by_name(cls, ldap_key):
175 def get_by_name(cls, ldap_key):
176 return Session.query(cls)\
176 return Session.query(cls)\
177 .filter(cls.app_settings_name == ldap_key).scalar()
177 .filter(cls.app_settings_name == ldap_key).scalar()
178
178
179 @classmethod
179 @classmethod
180 def get_app_settings(cls, cache=False):
180 def get_app_settings(cls, cache=False):
181
181
182 ret = Session.query(cls)
182 ret = Session.query(cls)
183
183
184 if cache:
184 if cache:
185 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
185 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
186
186
187 if not ret:
187 if not ret:
188 raise Exception('Could not get application settings !')
188 raise Exception('Could not get application settings !')
189 settings = {}
189 settings = {}
190 for each in ret:
190 for each in ret:
191 settings['rhodecode_' + each.app_settings_name] = \
191 settings['rhodecode_' + each.app_settings_name] = \
192 each.app_settings_value
192 each.app_settings_value
193
193
194 return settings
194 return settings
195
195
196 @classmethod
196 @classmethod
197 def get_ldap_settings(cls, cache=False):
197 def get_ldap_settings(cls, cache=False):
198 ret = Session.query(cls)\
198 ret = Session.query(cls)\
199 .filter(cls.app_settings_name.startswith('ldap_')).all()
199 .filter(cls.app_settings_name.startswith('ldap_')).all()
200 fd = {}
200 fd = {}
201 for row in ret:
201 for row in ret:
202 fd.update({row.app_settings_name:row.app_settings_value})
202 fd.update({row.app_settings_name:row.app_settings_value})
203
203
204 return fd
204 return fd
205
205
206
206
207 class RhodeCodeUi(Base, BaseModel):
207 class RhodeCodeUi(Base, BaseModel):
208 __tablename__ = 'rhodecode_ui'
208 __tablename__ = 'rhodecode_ui'
209 __table_args__ = (UniqueConstraint('ui_key'), {'extend_existing':True})
209 __table_args__ = (UniqueConstraint('ui_key'), {'extend_existing':True})
210
210
211 HOOK_UPDATE = 'changegroup.update'
211 HOOK_UPDATE = 'changegroup.update'
212 HOOK_REPO_SIZE = 'changegroup.repo_size'
212 HOOK_REPO_SIZE = 'changegroup.repo_size'
213 HOOK_PUSH = 'pretxnchangegroup.push_logger'
213 HOOK_PUSH = 'pretxnchangegroup.push_logger'
214 HOOK_PULL = 'preoutgoing.pull_logger'
214 HOOK_PULL = 'preoutgoing.pull_logger'
215
215
216 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
216 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
217 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
217 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
218 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
218 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
219 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
219 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
220 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
220 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
221
221
222
222
223 @classmethod
223 @classmethod
224 def get_by_key(cls, key):
224 def get_by_key(cls, key):
225 return Session.query(cls).filter(cls.ui_key == key)
225 return Session.query(cls).filter(cls.ui_key == key)
226
226
227
227
228 @classmethod
228 @classmethod
229 def get_builtin_hooks(cls):
229 def get_builtin_hooks(cls):
230 q = cls.query()
230 q = cls.query()
231 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
231 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
232 cls.HOOK_REPO_SIZE,
232 cls.HOOK_REPO_SIZE,
233 cls.HOOK_PUSH, cls.HOOK_PULL]))
233 cls.HOOK_PUSH, cls.HOOK_PULL]))
234 return q.all()
234 return q.all()
235
235
236 @classmethod
236 @classmethod
237 def get_custom_hooks(cls):
237 def get_custom_hooks(cls):
238 q = cls.query()
238 q = cls.query()
239 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
239 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
240 cls.HOOK_REPO_SIZE,
240 cls.HOOK_REPO_SIZE,
241 cls.HOOK_PUSH, cls.HOOK_PULL]))
241 cls.HOOK_PUSH, cls.HOOK_PULL]))
242 q = q.filter(cls.ui_section == 'hooks')
242 q = q.filter(cls.ui_section == 'hooks')
243 return q.all()
243 return q.all()
244
244
245 @classmethod
245 @classmethod
246 def create_or_update_hook(cls, key, val):
246 def create_or_update_hook(cls, key, val):
247 new_ui = cls.get_by_key(key).scalar() or cls()
247 new_ui = cls.get_by_key(key).scalar() or cls()
248 new_ui.ui_section = 'hooks'
248 new_ui.ui_section = 'hooks'
249 new_ui.ui_active = True
249 new_ui.ui_active = True
250 new_ui.ui_key = key
250 new_ui.ui_key = key
251 new_ui.ui_value = val
251 new_ui.ui_value = val
252
252
253 Session.add(new_ui)
253 Session.add(new_ui)
254 Session.commit()
254 Session.commit()
255
255
256
256
257 class User(Base, BaseModel):
257 class User(Base, BaseModel):
258 __tablename__ = 'users'
258 __tablename__ = 'users'
259 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True})
259 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True})
260 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
260 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
261 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
261 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
262 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
262 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
263 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
263 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
264 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
264 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
265 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
265 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
266 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
266 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
267 email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
267 email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
268 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
268 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
269 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
269 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
270 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
270 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
271
271
272 user_log = relationship('UserLog', cascade='all')
272 user_log = relationship('UserLog', cascade='all')
273 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
273 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
274
274
275 repositories = relationship('Repository')
275 repositories = relationship('Repository')
276 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
276 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
277 repo_to_perm = relationship('RepoToPerm', primaryjoin='RepoToPerm.user_id==User.user_id', cascade='all')
277 repo_to_perm = relationship('RepoToPerm', primaryjoin='RepoToPerm.user_id==User.user_id', cascade='all')
278
278
279 group_member = relationship('UsersGroupMember', cascade='all')
279 group_member = relationship('UsersGroupMember', cascade='all')
280
280
281 @property
281 @property
282 def full_contact(self):
282 def full_contact(self):
283 return '%s %s <%s>' % (self.name, self.lastname, self.email)
283 return '%s %s <%s>' % (self.name, self.lastname, self.email)
284
284
285 @property
285 @property
286 def short_contact(self):
286 def short_contact(self):
287 return '%s %s' % (self.name, self.lastname)
287 return '%s %s' % (self.name, self.lastname)
288
288
289 @property
289 @property
290 def is_admin(self):
290 def is_admin(self):
291 return self.admin
291 return self.admin
292
292
293 def __repr__(self):
293 def __repr__(self):
294 try:
294 try:
295 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
295 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
296 self.user_id, self.username)
296 self.user_id, self.username)
297 except:
297 except:
298 return self.__class__.__name__
298 return self.__class__.__name__
299
299
300 @classmethod
300 @classmethod
301 def get_by_username(cls, username, case_insensitive=False):
301 def get_by_username(cls, username, case_insensitive=False):
302 if case_insensitive:
302 if case_insensitive:
303 return Session.query(cls).filter(cls.username.ilike(username)).scalar()
303 return Session.query(cls).filter(cls.username.ilike(username)).scalar()
304 else:
304 else:
305 return Session.query(cls).filter(cls.username == username).scalar()
305 return Session.query(cls).filter(cls.username == username).scalar()
306
306
307 @classmethod
307 @classmethod
308 def get_by_api_key(cls, api_key):
308 def get_by_api_key(cls, api_key):
309 return Session.query(cls).filter(cls.api_key == api_key).one()
309 return Session.query(cls).filter(cls.api_key == api_key).one()
310
310
311 def update_lastlogin(self):
311 def update_lastlogin(self):
312 """Update user lastlogin"""
312 """Update user lastlogin"""
313
313
314 self.last_login = datetime.datetime.now()
314 self.last_login = datetime.datetime.now()
315 Session.add(self)
315 Session.add(self)
316 Session.commit()
316 Session.commit()
317 log.debug('updated user %s lastlogin', self.username)
317 log.debug('updated user %s lastlogin', self.username)
318
318
319 @classmethod
319 @classmethod
320 def create(cls, form_data):
320 def create(cls, form_data):
321 from rhodecode.lib.auth import get_crypt_password
321 from rhodecode.lib.auth import get_crypt_password
322
322
323 try:
323 try:
324 new_user = cls()
324 new_user = cls()
325 for k, v in form_data.items():
325 for k, v in form_data.items():
326 if k == 'password':
326 if k == 'password':
327 v = get_crypt_password(v)
327 v = get_crypt_password(v)
328 setattr(new_user, k, v)
328 setattr(new_user, k, v)
329
329
330 new_user.api_key = generate_api_key(form_data['username'])
330 new_user.api_key = generate_api_key(form_data['username'])
331 Session.add(new_user)
331 Session.add(new_user)
332 Session.commit()
332 Session.commit()
333 return new_user
333 return new_user
334 except:
334 except:
335 log.error(traceback.format_exc())
335 log.error(traceback.format_exc())
336 Session.rollback()
336 Session.rollback()
337 raise
337 raise
338
338
339 class UserLog(Base, BaseModel):
339 class UserLog(Base, BaseModel):
340 __tablename__ = 'user_logs'
340 __tablename__ = 'user_logs'
341 __table_args__ = {'extend_existing':True}
341 __table_args__ = {'extend_existing':True}
342 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
342 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
343 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
343 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
344 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
344 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
345 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
345 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
346 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
346 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
347 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
347 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
348 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
348 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
349
349
350 @property
350 @property
351 def action_as_day(self):
351 def action_as_day(self):
352 return date(*self.action_date.timetuple()[:3])
352 return date(*self.action_date.timetuple()[:3])
353
353
354 user = relationship('User')
354 user = relationship('User')
355 repository = relationship('Repository')
355 repository = relationship('Repository')
356
356
357
357
358 class UsersGroup(Base, BaseModel):
358 class UsersGroup(Base, BaseModel):
359 __tablename__ = 'users_groups'
359 __tablename__ = 'users_groups'
360 __table_args__ = {'extend_existing':True}
360 __table_args__ = {'extend_existing':True}
361
361
362 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
362 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
363 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
363 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
364 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
364 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
365
365
366 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
366 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
367
367
368 def __repr__(self):
368 def __repr__(self):
369 return '<userGroup(%s)>' % (self.users_group_name)
369 return '<userGroup(%s)>' % (self.users_group_name)
370
370
371 @classmethod
371 @classmethod
372 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
372 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
373 if case_insensitive:
373 if case_insensitive:
374 gr = Session.query(cls)\
374 gr = Session.query(cls)\
375 .filter(cls.users_group_name.ilike(group_name))
375 .filter(cls.users_group_name.ilike(group_name))
376 else:
376 else:
377 gr = Session.query(UsersGroup)\
377 gr = Session.query(UsersGroup)\
378 .filter(UsersGroup.users_group_name == group_name)
378 .filter(UsersGroup.users_group_name == group_name)
379 if cache:
379 if cache:
380 gr = gr.options(FromCache("sql_cache_short",
380 gr = gr.options(FromCache("sql_cache_short",
381 "get_user_%s" % group_name))
381 "get_user_%s" % group_name))
382 return gr.scalar()
382 return gr.scalar()
383
383
384
384
385 @classmethod
385 @classmethod
386 def get(cls, users_group_id, cache=False):
386 def get(cls, users_group_id, cache=False):
387 users_group = Session.query(cls)
387 users_group = Session.query(cls)
388 if cache:
388 if cache:
389 users_group = users_group.options(FromCache("sql_cache_short",
389 users_group = users_group.options(FromCache("sql_cache_short",
390 "get_users_group_%s" % users_group_id))
390 "get_users_group_%s" % users_group_id))
391 return users_group.get(users_group_id)
391 return users_group.get(users_group_id)
392
392
393 @classmethod
393 @classmethod
394 def create(cls, form_data):
394 def create(cls, form_data):
395 try:
395 try:
396 new_users_group = cls()
396 new_users_group = cls()
397 for k, v in form_data.items():
397 for k, v in form_data.items():
398 setattr(new_users_group, k, v)
398 setattr(new_users_group, k, v)
399
399
400 Session.add(new_users_group)
400 Session.add(new_users_group)
401 Session.commit()
401 Session.commit()
402 return new_users_group
402 return new_users_group
403 except:
403 except:
404 log.error(traceback.format_exc())
404 log.error(traceback.format_exc())
405 Session.rollback()
405 Session.rollback()
406 raise
406 raise
407
407
408 @classmethod
408 @classmethod
409 def update(cls, users_group_id, form_data):
409 def update(cls, users_group_id, form_data):
410
410
411 try:
411 try:
412 users_group = cls.get(users_group_id, cache=False)
412 users_group = cls.get(users_group_id, cache=False)
413
413
414 for k, v in form_data.items():
414 for k, v in form_data.items():
415 if k == 'users_group_members':
415 if k == 'users_group_members':
416 users_group.members = []
416 users_group.members = []
417 Session.flush()
417 Session.flush()
418 members_list = []
418 members_list = []
419 if v:
419 if v:
420 for u_id in set(v):
420 for u_id in set(v):
421 members_list.append(UsersGroupMember(
421 members_list.append(UsersGroupMember(
422 users_group_id,
422 users_group_id,
423 u_id))
423 u_id))
424 setattr(users_group, 'members', members_list)
424 setattr(users_group, 'members', members_list)
425 setattr(users_group, k, v)
425 setattr(users_group, k, v)
426
426
427 Session.add(users_group)
427 Session.add(users_group)
428 Session.commit()
428 Session.commit()
429 except:
429 except:
430 log.error(traceback.format_exc())
430 log.error(traceback.format_exc())
431 Session.rollback()
431 Session.rollback()
432 raise
432 raise
433
433
434 @classmethod
434 @classmethod
435 def delete(cls, users_group_id):
435 def delete(cls, users_group_id):
436 try:
436 try:
437
437
438 # check if this group is not assigned to repo
438 # check if this group is not assigned to repo
439 assigned_groups = UsersGroupRepoToPerm.query()\
439 assigned_groups = UsersGroupRepoToPerm.query()\
440 .filter(UsersGroupRepoToPerm.users_group_id ==
440 .filter(UsersGroupRepoToPerm.users_group_id ==
441 users_group_id).all()
441 users_group_id).all()
442
442
443 if assigned_groups:
443 if assigned_groups:
444 raise UsersGroupsAssignedException('Group assigned to %s' %
444 raise UsersGroupsAssignedException('Group assigned to %s' %
445 assigned_groups)
445 assigned_groups)
446
446
447 users_group = cls.get(users_group_id, cache=False)
447 users_group = cls.get(users_group_id, cache=False)
448 Session.delete(users_group)
448 Session.delete(users_group)
449 Session.commit()
449 Session.commit()
450 except:
450 except:
451 log.error(traceback.format_exc())
451 log.error(traceback.format_exc())
452 Session.rollback()
452 Session.rollback()
453 raise
453 raise
454
454
455
455
456 class UsersGroupMember(Base, BaseModel):
456 class UsersGroupMember(Base, BaseModel):
457 __tablename__ = 'users_groups_members'
457 __tablename__ = 'users_groups_members'
458 __table_args__ = {'extend_existing':True}
458 __table_args__ = {'extend_existing':True}
459
459
460 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
460 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
461 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
461 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
462 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
462 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
463
463
464 user = relationship('User', lazy='joined')
464 user = relationship('User', lazy='joined')
465 users_group = relationship('UsersGroup')
465 users_group = relationship('UsersGroup')
466
466
467 def __init__(self, gr_id='', u_id=''):
467 def __init__(self, gr_id='', u_id=''):
468 self.users_group_id = gr_id
468 self.users_group_id = gr_id
469 self.user_id = u_id
469 self.user_id = u_id
470
470
471 class Repository(Base, BaseModel):
471 class Repository(Base, BaseModel):
472 __tablename__ = 'repositories'
472 __tablename__ = 'repositories'
473 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
473 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
474
474
475 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
475 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
476 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
476 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
477 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
477 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
478 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
478 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
479 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
479 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
480 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
480 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
481 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
481 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
482 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
482 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
483 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
483 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
484 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
484 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
485
485
486 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
486 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
487 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
487 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
488
488
489
489
490 user = relationship('User')
490 user = relationship('User')
491 fork = relationship('Repository', remote_side=repo_id)
491 fork = relationship('Repository', remote_side=repo_id)
492 group = relationship('Group')
492 group = relationship('Group')
493 repo_to_perm = relationship('RepoToPerm', cascade='all', order_by='RepoToPerm.repo_to_perm_id')
493 repo_to_perm = relationship('RepoToPerm', cascade='all', order_by='RepoToPerm.repo_to_perm_id')
494 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
494 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
495 stats = relationship('Statistics', cascade='all', uselist=False)
495 stats = relationship('Statistics', cascade='all', uselist=False)
496
496
497 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
497 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
498
498
499 logs = relationship('UserLog', cascade='all')
499 logs = relationship('UserLog', cascade='all')
500
500
501 def __repr__(self):
501 def __repr__(self):
502 return "<%s('%s:%s')>" % (self.__class__.__name__,
502 return "<%s('%s:%s')>" % (self.__class__.__name__,
503 self.repo_id, self.repo_name)
503 self.repo_id, self.repo_name)
504
504
505 @classmethod
505 @classmethod
506 def url_sep(cls):
507 return '/'
508
509 @classmethod
506 def get_by_repo_name(cls, repo_name):
510 def get_by_repo_name(cls, repo_name):
507 q = Session.query(cls).filter(cls.repo_name == repo_name)
511 q = Session.query(cls).filter(cls.repo_name == repo_name)
508
512
509 q = q.options(joinedload(Repository.fork))\
513 q = q.options(joinedload(Repository.fork))\
510 .options(joinedload(Repository.user))\
514 .options(joinedload(Repository.user))\
511 .options(joinedload(Repository.group))\
515 .options(joinedload(Repository.group))\
512
516
513 return q.one()
517 return q.one()
514
518
515 @classmethod
519 @classmethod
516 def get_repo_forks(cls, repo_id):
520 def get_repo_forks(cls, repo_id):
517 return Session.query(cls).filter(Repository.fork_id == repo_id)
521 return Session.query(cls).filter(Repository.fork_id == repo_id)
518
522
519 @classmethod
523 @classmethod
520 def base_path(cls):
524 def base_path(cls):
521 """
525 """
522 Returns base path when all repos are stored
526 Returns base path when all repos are stored
523
527
524 :param cls:
528 :param cls:
525 """
529 """
526 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/')
530 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
531 cls.url_sep())
527 q.options(FromCache("sql_cache_short", "repository_repo_path"))
532 q.options(FromCache("sql_cache_short", "repository_repo_path"))
528 return q.one().ui_value
533 return q.one().ui_value
529
534
530 @property
535 @property
531 def just_name(self):
536 def just_name(self):
532 return self.repo_name.split(os.sep)[-1]
537 return self.repo_name.split(os.sep)[-1]
533
538
534 @property
539 @property
535 def groups_with_parents(self):
540 def groups_with_parents(self):
536 groups = []
541 groups = []
537 if self.group is None:
542 if self.group is None:
538 return groups
543 return groups
539
544
540 cur_gr = self.group
545 cur_gr = self.group
541 groups.insert(0, cur_gr)
546 groups.insert(0, cur_gr)
542 while 1:
547 while 1:
543 gr = getattr(cur_gr, 'parent_group', None)
548 gr = getattr(cur_gr, 'parent_group', None)
544 cur_gr = cur_gr.parent_group
549 cur_gr = cur_gr.parent_group
545 if gr is None:
550 if gr is None:
546 break
551 break
547 groups.insert(0, gr)
552 groups.insert(0, gr)
548
553
549 return groups
554 return groups
550
555
551 @property
556 @property
552 def groups_and_repo(self):
557 def groups_and_repo(self):
553 return self.groups_with_parents, self.just_name
558 return self.groups_with_parents, self.just_name
554
559
555 @LazyProperty
560 @LazyProperty
556 def repo_path(self):
561 def repo_path(self):
557 """
562 """
558 Returns base full path for that repository means where it actually
563 Returns base full path for that repository means where it actually
559 exists on a filesystem
564 exists on a filesystem
560 """
565 """
561 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/')
566 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
567 Repository.url_sep())
562 q.options(FromCache("sql_cache_short", "repository_repo_path"))
568 q.options(FromCache("sql_cache_short", "repository_repo_path"))
563 return q.one().ui_value
569 return q.one().ui_value
564
570
565 @property
571 @property
566 def repo_full_path(self):
572 def repo_full_path(self):
567 p = [self.repo_path]
573 p = [self.repo_path]
568 # we need to split the name by / since this is how we store the
574 # we need to split the name by / since this is how we store the
569 # names in the database, but that eventually needs to be converted
575 # names in the database, but that eventually needs to be converted
570 # into a valid system path
576 # into a valid system path
571 p += self.repo_name.split('/')
577 p += self.repo_name.split(Repository.url_sep())
572 return os.path.join(*p)
578 return os.path.join(*p)
573
579
574 def get_new_name(self, repo_name):
580 def get_new_name(self, repo_name):
575 """
581 """
576 returns new full repository name based on assigned group and new new
582 returns new full repository name based on assigned group and new new
577
583
578 :param group_name:
584 :param group_name:
579 """
585 """
580 path_prefix = self.group.full_path_splitted if self.group else []
586 path_prefix = self.group.full_path_splitted if self.group else []
581 return '/'.join(path_prefix + [repo_name])
587 return Repository.url_sep().join(path_prefix + [repo_name])
582
588
583 @property
589 @property
584 def _ui(self):
590 def _ui(self):
585 """
591 """
586 Creates an db based ui object for this repository
592 Creates an db based ui object for this repository
587 """
593 """
588 from mercurial import ui
594 from mercurial import ui
589 from mercurial import config
595 from mercurial import config
590 baseui = ui.ui()
596 baseui = ui.ui()
591
597
592 #clean the baseui object
598 #clean the baseui object
593 baseui._ocfg = config.config()
599 baseui._ocfg = config.config()
594 baseui._ucfg = config.config()
600 baseui._ucfg = config.config()
595 baseui._tcfg = config.config()
601 baseui._tcfg = config.config()
596
602
597
603
598 ret = Session.query(RhodeCodeUi)\
604 ret = Session.query(RhodeCodeUi)\
599 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
605 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
600
606
601 hg_ui = ret
607 hg_ui = ret
602 for ui_ in hg_ui:
608 for ui_ in hg_ui:
603 if ui_.ui_active:
609 if ui_.ui_active:
604 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
610 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
605 ui_.ui_key, ui_.ui_value)
611 ui_.ui_key, ui_.ui_value)
606 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
612 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
607
613
608 return baseui
614 return baseui
609
615
610 @classmethod
616 @classmethod
611 def is_valid(cls, repo_name):
617 def is_valid(cls, repo_name):
612 """
618 """
613 returns True if given repo name is a valid filesystem repository
619 returns True if given repo name is a valid filesystem repository
614
620
615 @param cls:
621 @param cls:
616 @param repo_name:
622 @param repo_name:
617 """
623 """
618 from rhodecode.lib.utils import is_valid_repo
624 from rhodecode.lib.utils import is_valid_repo
619
625
620 return is_valid_repo(repo_name, cls.base_path())
626 return is_valid_repo(repo_name, cls.base_path())
621
627
622
628
623 #==========================================================================
629 #==========================================================================
624 # SCM PROPERTIES
630 # SCM PROPERTIES
625 #==========================================================================
631 #==========================================================================
626
632
627 def get_changeset(self, rev):
633 def get_changeset(self, rev):
628 return get_changeset_safe(self.scm_instance, rev)
634 return get_changeset_safe(self.scm_instance, rev)
629
635
630 @property
636 @property
631 def tip(self):
637 def tip(self):
632 return self.get_changeset('tip')
638 return self.get_changeset('tip')
633
639
634 @property
640 @property
635 def author(self):
641 def author(self):
636 return self.tip.author
642 return self.tip.author
637
643
638 @property
644 @property
639 def last_change(self):
645 def last_change(self):
640 return self.scm_instance.last_change
646 return self.scm_instance.last_change
641
647
642 #==========================================================================
648 #==========================================================================
643 # SCM CACHE INSTANCE
649 # SCM CACHE INSTANCE
644 #==========================================================================
650 #==========================================================================
645
651
646 @property
652 @property
647 def invalidate(self):
653 def invalidate(self):
648 """
654 """
649 Returns Invalidation object if this repo should be invalidated
655 Returns Invalidation object if this repo should be invalidated
650 None otherwise. `cache_active = False` means that this cache
656 None otherwise. `cache_active = False` means that this cache
651 state is not valid and needs to be invalidated
657 state is not valid and needs to be invalidated
652 """
658 """
653 return Session.query(CacheInvalidation)\
659 return Session.query(CacheInvalidation)\
654 .filter(CacheInvalidation.cache_key == self.repo_name)\
660 .filter(CacheInvalidation.cache_key == self.repo_name)\
655 .filter(CacheInvalidation.cache_active == False)\
661 .filter(CacheInvalidation.cache_active == False)\
656 .scalar()
662 .scalar()
657
663
658 def set_invalidate(self):
664 def set_invalidate(self):
659 """
665 """
660 set a cache for invalidation for this instance
666 set a cache for invalidation for this instance
661 """
667 """
662 inv = Session.query(CacheInvalidation)\
668 inv = Session.query(CacheInvalidation)\
663 .filter(CacheInvalidation.cache_key == self.repo_name)\
669 .filter(CacheInvalidation.cache_key == self.repo_name)\
664 .scalar()
670 .scalar()
665
671
666 if inv is None:
672 if inv is None:
667 inv = CacheInvalidation(self.repo_name)
673 inv = CacheInvalidation(self.repo_name)
668 inv.cache_active = True
674 inv.cache_active = True
669 Session.add(inv)
675 Session.add(inv)
670 Session.commit()
676 Session.commit()
671
677
672 @LazyProperty
678 @LazyProperty
673 def scm_instance(self):
679 def scm_instance(self):
674 return self.__get_instance()
680 return self.__get_instance()
675
681
676 @property
682 @property
677 def scm_instance_cached(self):
683 def scm_instance_cached(self):
678 @cache_region('long_term')
684 @cache_region('long_term')
679 def _c(repo_name):
685 def _c(repo_name):
680 return self.__get_instance()
686 return self.__get_instance()
681
687
682 # TODO: remove this trick when beaker 1.6 is released
688 # TODO: remove this trick when beaker 1.6 is released
683 # and have fixed this issue with not supporting unicode keys
689 # and have fixed this issue with not supporting unicode keys
684 rn = safe_str(self.repo_name)
690 rn = safe_str(self.repo_name)
685
691
686 inv = self.invalidate
692 inv = self.invalidate
687 if inv is not None:
693 if inv is not None:
688 region_invalidate(_c, None, rn)
694 region_invalidate(_c, None, rn)
689 # update our cache
695 # update our cache
690 inv.cache_active = True
696 inv.cache_active = True
691 Session.add(inv)
697 Session.add(inv)
692 Session.commit()
698 Session.commit()
693
699
694 return _c(rn)
700 return _c(rn)
695
701
696 def __get_instance(self):
702 def __get_instance(self):
697
703
698 repo_full_path = self.repo_full_path
704 repo_full_path = self.repo_full_path
699
705
700 try:
706 try:
701 alias = get_scm(repo_full_path)[0]
707 alias = get_scm(repo_full_path)[0]
702 log.debug('Creating instance of %s repository', alias)
708 log.debug('Creating instance of %s repository', alias)
703 backend = get_backend(alias)
709 backend = get_backend(alias)
704 except VCSError:
710 except VCSError:
705 log.error(traceback.format_exc())
711 log.error(traceback.format_exc())
706 log.error('Perhaps this repository is in db and not in '
712 log.error('Perhaps this repository is in db and not in '
707 'filesystem run rescan repositories with '
713 'filesystem run rescan repositories with '
708 '"destroy old data " option from admin panel')
714 '"destroy old data " option from admin panel')
709 return
715 return
710
716
711 if alias == 'hg':
717 if alias == 'hg':
712
718
713 repo = backend(safe_str(repo_full_path), create=False,
719 repo = backend(safe_str(repo_full_path), create=False,
714 baseui=self._ui)
720 baseui=self._ui)
715 #skip hidden web repository
721 #skip hidden web repository
716 if repo._get_hidden():
722 if repo._get_hidden():
717 return
723 return
718 else:
724 else:
719 repo = backend(repo_full_path, create=False)
725 repo = backend(repo_full_path, create=False)
720
726
721 return repo
727 return repo
722
728
723
729
724 class Group(Base, BaseModel):
730 class Group(Base, BaseModel):
725 __tablename__ = 'groups'
731 __tablename__ = 'groups'
726 __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'),
732 __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'),
727 CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},)
733 CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},)
728 __mapper_args__ = {'order_by':'group_name'}
734 __mapper_args__ = {'order_by':'group_name'}
729
735
730 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
736 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
731 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
737 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
732 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
738 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
733 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
739 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
734
740
735 parent_group = relationship('Group', remote_side=group_id)
741 parent_group = relationship('Group', remote_side=group_id)
736
742
737
743
738 def __init__(self, group_name='', parent_group=None):
744 def __init__(self, group_name='', parent_group=None):
739 self.group_name = group_name
745 self.group_name = group_name
740 self.parent_group = parent_group
746 self.parent_group = parent_group
741
747
742 def __repr__(self):
748 def __repr__(self):
743 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
749 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
744 self.group_name)
750 self.group_name)
745
751
746 @classmethod
752 @classmethod
747 def groups_choices(cls):
753 def groups_choices(cls):
748 from webhelpers.html import literal as _literal
754 from webhelpers.html import literal as _literal
749 repo_groups = [('', '')]
755 repo_groups = [('', '')]
750 sep = ' &raquo; '
756 sep = ' &raquo; '
751 _name = lambda k: _literal(sep.join(k))
757 _name = lambda k: _literal(sep.join(k))
752
758
753 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
759 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
754 for x in cls.query().all()])
760 for x in cls.query().all()])
755
761
756 repo_groups = sorted(repo_groups,key=lambda t: t[1].split(sep)[0])
762 repo_groups = sorted(repo_groups,key=lambda t: t[1].split(sep)[0])
757 return repo_groups
763 return repo_groups
758
764
759 @classmethod
765 @classmethod
760 def url_sep(cls):
766 def url_sep(cls):
761 return '/'
767 return '/'
762
768
763 @classmethod
769 @classmethod
764 def get_by_group_name(cls, group_name):
770 def get_by_group_name(cls, group_name):
765 return cls.query().filter(cls.group_name == group_name).scalar()
771 return cls.query().filter(cls.group_name == group_name).scalar()
766
772
767 @property
773 @property
768 def parents(self):
774 def parents(self):
769 parents_recursion_limit = 5
775 parents_recursion_limit = 5
770 groups = []
776 groups = []
771 if self.parent_group is None:
777 if self.parent_group is None:
772 return groups
778 return groups
773 cur_gr = self.parent_group
779 cur_gr = self.parent_group
774 groups.insert(0, cur_gr)
780 groups.insert(0, cur_gr)
775 cnt = 0
781 cnt = 0
776 while 1:
782 while 1:
777 cnt += 1
783 cnt += 1
778 gr = getattr(cur_gr, 'parent_group', None)
784 gr = getattr(cur_gr, 'parent_group', None)
779 cur_gr = cur_gr.parent_group
785 cur_gr = cur_gr.parent_group
780 if gr is None:
786 if gr is None:
781 break
787 break
782 if cnt == parents_recursion_limit:
788 if cnt == parents_recursion_limit:
783 # this will prevent accidental infinit loops
789 # this will prevent accidental infinit loops
784 log.error('group nested more than %s' %
790 log.error('group nested more than %s' %
785 parents_recursion_limit)
791 parents_recursion_limit)
786 break
792 break
787
793
788 groups.insert(0, gr)
794 groups.insert(0, gr)
789 return groups
795 return groups
790
796
791 @property
797 @property
792 def children(self):
798 def children(self):
793 return Session.query(Group).filter(Group.parent_group == self)
799 return Session.query(Group).filter(Group.parent_group == self)
794
800
795 @property
801 @property
796 def name(self):
802 def name(self):
797 return self.group_name.split(Group.url_sep())[-1]
803 return self.group_name.split(Group.url_sep())[-1]
798
804
799 @property
805 @property
800 def full_path(self):
806 def full_path(self):
801 return self.group_name
807 return self.group_name
802
808
803 @property
809 @property
804 def full_path_splitted(self):
810 def full_path_splitted(self):
805 return self.group_name.split(Group.url_sep())
811 return self.group_name.split(Group.url_sep())
806
812
807 @property
813 @property
808 def repositories(self):
814 def repositories(self):
809 return Session.query(Repository).filter(Repository.group == self)
815 return Session.query(Repository).filter(Repository.group == self)
810
816
811 @property
817 @property
812 def repositories_recursive_count(self):
818 def repositories_recursive_count(self):
813 cnt = self.repositories.count()
819 cnt = self.repositories.count()
814
820
815 def children_count(group):
821 def children_count(group):
816 cnt = 0
822 cnt = 0
817 for child in group.children:
823 for child in group.children:
818 cnt += child.repositories.count()
824 cnt += child.repositories.count()
819 cnt += children_count(child)
825 cnt += children_count(child)
820 return cnt
826 return cnt
821
827
822 return cnt + children_count(self)
828 return cnt + children_count(self)
823
829
824
830
825 def get_new_name(self, group_name):
831 def get_new_name(self, group_name):
826 """
832 """
827 returns new full group name based on parent and new name
833 returns new full group name based on parent and new name
828
834
829 :param group_name:
835 :param group_name:
830 """
836 """
831 path_prefix = self.parent_group.full_path_splitted if self.parent_group else []
837 path_prefix = self.parent_group.full_path_splitted if self.parent_group else []
832 return Group.url_sep().join(path_prefix + [group_name])
838 return Group.url_sep().join(path_prefix + [group_name])
833
839
834
840
835 class Permission(Base, BaseModel):
841 class Permission(Base, BaseModel):
836 __tablename__ = 'permissions'
842 __tablename__ = 'permissions'
837 __table_args__ = {'extend_existing':True}
843 __table_args__ = {'extend_existing':True}
838 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
844 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
839 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
845 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
840 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
846 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
841
847
842 def __repr__(self):
848 def __repr__(self):
843 return "<%s('%s:%s')>" % (self.__class__.__name__,
849 return "<%s('%s:%s')>" % (self.__class__.__name__,
844 self.permission_id, self.permission_name)
850 self.permission_id, self.permission_name)
845
851
846 @classmethod
852 @classmethod
847 def get_by_key(cls, key):
853 def get_by_key(cls, key):
848 return Session.query(cls).filter(cls.permission_name == key).scalar()
854 return Session.query(cls).filter(cls.permission_name == key).scalar()
849
855
850 class RepoToPerm(Base, BaseModel):
856 class RepoToPerm(Base, BaseModel):
851 __tablename__ = 'repo_to_perm'
857 __tablename__ = 'repo_to_perm'
852 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True})
858 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True})
853 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
859 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
854 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
860 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
855 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
861 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
856 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
862 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
857
863
858 user = relationship('User')
864 user = relationship('User')
859 permission = relationship('Permission')
865 permission = relationship('Permission')
860 repository = relationship('Repository')
866 repository = relationship('Repository')
861
867
862 class UserToPerm(Base, BaseModel):
868 class UserToPerm(Base, BaseModel):
863 __tablename__ = 'user_to_perm'
869 __tablename__ = 'user_to_perm'
864 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
870 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
865 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
871 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
866 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
872 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
867 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
873 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
868
874
869 user = relationship('User')
875 user = relationship('User')
870 permission = relationship('Permission')
876 permission = relationship('Permission')
871
877
872 @classmethod
878 @classmethod
873 def has_perm(cls, user_id, perm):
879 def has_perm(cls, user_id, perm):
874 if not isinstance(perm, Permission):
880 if not isinstance(perm, Permission):
875 raise Exception('perm needs to be an instance of Permission class')
881 raise Exception('perm needs to be an instance of Permission class')
876
882
877 return Session.query(cls).filter(cls.user_id == user_id)\
883 return Session.query(cls).filter(cls.user_id == user_id)\
878 .filter(cls.permission == perm).scalar() is not None
884 .filter(cls.permission == perm).scalar() is not None
879
885
880 @classmethod
886 @classmethod
881 def grant_perm(cls, user_id, perm):
887 def grant_perm(cls, user_id, perm):
882 if not isinstance(perm, Permission):
888 if not isinstance(perm, Permission):
883 raise Exception('perm needs to be an instance of Permission class')
889 raise Exception('perm needs to be an instance of Permission class')
884
890
885 new = cls()
891 new = cls()
886 new.user_id = user_id
892 new.user_id = user_id
887 new.permission = perm
893 new.permission = perm
888 try:
894 try:
889 Session.add(new)
895 Session.add(new)
890 Session.commit()
896 Session.commit()
891 except:
897 except:
892 Session.rollback()
898 Session.rollback()
893
899
894
900
895 @classmethod
901 @classmethod
896 def revoke_perm(cls, user_id, perm):
902 def revoke_perm(cls, user_id, perm):
897 if not isinstance(perm, Permission):
903 if not isinstance(perm, Permission):
898 raise Exception('perm needs to be an instance of Permission class')
904 raise Exception('perm needs to be an instance of Permission class')
899
905
900 try:
906 try:
901 Session.query(cls).filter(cls.user_id == user_id)\
907 Session.query(cls).filter(cls.user_id == user_id)\
902 .filter(cls.permission == perm).delete()
908 .filter(cls.permission == perm).delete()
903 Session.commit()
909 Session.commit()
904 except:
910 except:
905 Session.rollback()
911 Session.rollback()
906
912
907 class UsersGroupRepoToPerm(Base, BaseModel):
913 class UsersGroupRepoToPerm(Base, BaseModel):
908 __tablename__ = 'users_group_repo_to_perm'
914 __tablename__ = 'users_group_repo_to_perm'
909 __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True})
915 __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True})
910 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
916 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
911 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
917 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
912 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
918 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
913 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
919 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
914
920
915 users_group = relationship('UsersGroup')
921 users_group = relationship('UsersGroup')
916 permission = relationship('Permission')
922 permission = relationship('Permission')
917 repository = relationship('Repository')
923 repository = relationship('Repository')
918
924
919 def __repr__(self):
925 def __repr__(self):
920 return '<userGroup:%s => %s >' % (self.users_group, self.repository)
926 return '<userGroup:%s => %s >' % (self.users_group, self.repository)
921
927
922 class UsersGroupToPerm(Base, BaseModel):
928 class UsersGroupToPerm(Base, BaseModel):
923 __tablename__ = 'users_group_to_perm'
929 __tablename__ = 'users_group_to_perm'
924 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
930 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
925 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
931 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
926 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
932 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
927
933
928 users_group = relationship('UsersGroup')
934 users_group = relationship('UsersGroup')
929 permission = relationship('Permission')
935 permission = relationship('Permission')
930
936
931
937
932 @classmethod
938 @classmethod
933 def has_perm(cls, users_group_id, perm):
939 def has_perm(cls, users_group_id, perm):
934 if not isinstance(perm, Permission):
940 if not isinstance(perm, Permission):
935 raise Exception('perm needs to be an instance of Permission class')
941 raise Exception('perm needs to be an instance of Permission class')
936
942
937 return Session.query(cls).filter(cls.users_group_id ==
943 return Session.query(cls).filter(cls.users_group_id ==
938 users_group_id)\
944 users_group_id)\
939 .filter(cls.permission == perm)\
945 .filter(cls.permission == perm)\
940 .scalar() is not None
946 .scalar() is not None
941
947
942 @classmethod
948 @classmethod
943 def grant_perm(cls, users_group_id, perm):
949 def grant_perm(cls, users_group_id, perm):
944 if not isinstance(perm, Permission):
950 if not isinstance(perm, Permission):
945 raise Exception('perm needs to be an instance of Permission class')
951 raise Exception('perm needs to be an instance of Permission class')
946
952
947 new = cls()
953 new = cls()
948 new.users_group_id = users_group_id
954 new.users_group_id = users_group_id
949 new.permission = perm
955 new.permission = perm
950 try:
956 try:
951 Session.add(new)
957 Session.add(new)
952 Session.commit()
958 Session.commit()
953 except:
959 except:
954 Session.rollback()
960 Session.rollback()
955
961
956
962
957 @classmethod
963 @classmethod
958 def revoke_perm(cls, users_group_id, perm):
964 def revoke_perm(cls, users_group_id, perm):
959 if not isinstance(perm, Permission):
965 if not isinstance(perm, Permission):
960 raise Exception('perm needs to be an instance of Permission class')
966 raise Exception('perm needs to be an instance of Permission class')
961
967
962 try:
968 try:
963 Session.query(cls).filter(cls.users_group_id == users_group_id)\
969 Session.query(cls).filter(cls.users_group_id == users_group_id)\
964 .filter(cls.permission == perm).delete()
970 .filter(cls.permission == perm).delete()
965 Session.commit()
971 Session.commit()
966 except:
972 except:
967 Session.rollback()
973 Session.rollback()
968
974
969
975
970 class GroupToPerm(Base, BaseModel):
976 class GroupToPerm(Base, BaseModel):
971 __tablename__ = 'group_to_perm'
977 __tablename__ = 'group_to_perm'
972 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
978 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
973
979
974 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
980 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
975 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
981 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
976 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
982 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
977 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
983 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
978
984
979 user = relationship('User')
985 user = relationship('User')
980 permission = relationship('Permission')
986 permission = relationship('Permission')
981 group = relationship('Group')
987 group = relationship('Group')
982
988
983 class Statistics(Base, BaseModel):
989 class Statistics(Base, BaseModel):
984 __tablename__ = 'statistics'
990 __tablename__ = 'statistics'
985 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True})
991 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True})
986 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
992 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
987 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
993 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
988 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
994 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
989 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
995 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
990 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
996 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
991 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
997 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
992
998
993 repository = relationship('Repository', single_parent=True)
999 repository = relationship('Repository', single_parent=True)
994
1000
995 class UserFollowing(Base, BaseModel):
1001 class UserFollowing(Base, BaseModel):
996 __tablename__ = 'user_followings'
1002 __tablename__ = 'user_followings'
997 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
1003 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
998 UniqueConstraint('user_id', 'follows_user_id')
1004 UniqueConstraint('user_id', 'follows_user_id')
999 , {'extend_existing':True})
1005 , {'extend_existing':True})
1000
1006
1001 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1007 user_following_id = Column("user_following_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)
1008 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1003 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1009 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1004 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1010 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1005 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1011 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1006
1012
1007 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1013 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1008
1014
1009 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1015 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1010 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1016 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1011
1017
1012
1018
1013 @classmethod
1019 @classmethod
1014 def get_repo_followers(cls, repo_id):
1020 def get_repo_followers(cls, repo_id):
1015 return Session.query(cls).filter(cls.follows_repo_id == repo_id)
1021 return Session.query(cls).filter(cls.follows_repo_id == repo_id)
1016
1022
1017 class CacheInvalidation(Base, BaseModel):
1023 class CacheInvalidation(Base, BaseModel):
1018 __tablename__ = 'cache_invalidation'
1024 __tablename__ = 'cache_invalidation'
1019 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True})
1025 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True})
1020 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1026 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1021 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1027 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1022 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1028 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1023 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1029 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1024
1030
1025
1031
1026 def __init__(self, cache_key, cache_args=''):
1032 def __init__(self, cache_key, cache_args=''):
1027 self.cache_key = cache_key
1033 self.cache_key = cache_key
1028 self.cache_args = cache_args
1034 self.cache_args = cache_args
1029 self.cache_active = False
1035 self.cache_active = False
1030
1036
1031 def __repr__(self):
1037 def __repr__(self):
1032 return "<%s('%s:%s')>" % (self.__class__.__name__,
1038 return "<%s('%s:%s')>" % (self.__class__.__name__,
1033 self.cache_id, self.cache_key)
1039 self.cache_id, self.cache_key)
1034
1040
1035 class DbMigrateVersion(Base, BaseModel):
1041 class DbMigrateVersion(Base, BaseModel):
1036 __tablename__ = 'db_migrate_version'
1042 __tablename__ = 'db_migrate_version'
1037 __table_args__ = {'extend_existing':True}
1043 __table_args__ = {'extend_existing':True}
1038 repository_id = Column('repository_id', String(250), primary_key=True)
1044 repository_id = Column('repository_id', String(250), primary_key=True)
1039 repository_path = Column('repository_path', Text)
1045 repository_path = Column('repository_path', Text)
1040 version = Column('version', Integer)
1046 version = Column('version', Integer)
@@ -1,408 +1,414 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) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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 time
26 import time
26 import traceback
27 import traceback
27 import logging
28 import logging
28
29
29 from sqlalchemy.exc import DatabaseError
30 from sqlalchemy.exc import DatabaseError
30
31
31 from vcs import get_backend
32 from vcs import get_backend
32 from vcs.exceptions import RepositoryError
33 from vcs.exceptions import RepositoryError
33 from vcs.utils.lazy import LazyProperty
34 from vcs.utils.lazy import LazyProperty
34 from vcs.nodes import FileNode
35 from vcs.nodes import FileNode
35
36
36 from rhodecode import BACKENDS
37 from rhodecode import BACKENDS
37 from rhodecode.lib import helpers as h
38 from rhodecode.lib import helpers as h
38 from rhodecode.lib import safe_str
39 from rhodecode.lib import safe_str
39 from rhodecode.lib.auth import HasRepoPermissionAny
40 from rhodecode.lib.auth import HasRepoPermissionAny
40 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
41 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
41 action_logger, EmptyChangeset
42 action_logger, EmptyChangeset
42 from rhodecode.model import BaseModel
43 from rhodecode.model import BaseModel
43 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
44 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
44 UserFollowing, UserLog, User
45 UserFollowing, UserLog, User
45
46
46 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
47
48
48
49
49 class UserTemp(object):
50 class UserTemp(object):
50 def __init__(self, user_id):
51 def __init__(self, user_id):
51 self.user_id = user_id
52 self.user_id = user_id
52
53
53 def __repr__(self):
54 def __repr__(self):
54 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
55 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
55
56
56
57
57 class RepoTemp(object):
58 class RepoTemp(object):
58 def __init__(self, repo_id):
59 def __init__(self, repo_id):
59 self.repo_id = repo_id
60 self.repo_id = repo_id
60
61
61 def __repr__(self):
62 def __repr__(self):
62 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
63 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
63
64
64 class CachedRepoList(object):
65 class CachedRepoList(object):
65
66
66 def __init__(self, db_repo_list, repos_path, order_by=None):
67 def __init__(self, db_repo_list, repos_path, order_by=None):
67 self.db_repo_list = db_repo_list
68 self.db_repo_list = db_repo_list
68 self.repos_path = repos_path
69 self.repos_path = repos_path
69 self.order_by = order_by
70 self.order_by = order_by
70 self.reversed = (order_by or '').startswith('-')
71 self.reversed = (order_by or '').startswith('-')
71
72
72 def __len__(self):
73 def __len__(self):
73 return len(self.db_repo_list)
74 return len(self.db_repo_list)
74
75
75 def __repr__(self):
76 def __repr__(self):
76 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
77 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
77
78
78 def __iter__(self):
79 def __iter__(self):
79 for dbr in self.db_repo_list:
80 for dbr in self.db_repo_list:
80
81
81 scmr = dbr.scm_instance_cached
82 scmr = dbr.scm_instance_cached
82
83
83 # check permission at this level
84 # check permission at this level
84 if not HasRepoPermissionAny('repository.read', 'repository.write',
85 if not HasRepoPermissionAny('repository.read', 'repository.write',
85 'repository.admin')(dbr.repo_name,
86 'repository.admin')(dbr.repo_name,
86 'get repo check'):
87 'get repo check'):
87 continue
88 continue
88
89
89 if scmr is None:
90 if scmr is None:
90 log.error('%s this repository is present in database but it '
91 log.error('%s this repository is present in database but it '
91 'cannot be created as an scm instance',
92 'cannot be created as an scm instance',
92 dbr.repo_name)
93 dbr.repo_name)
93 continue
94 continue
94
95
95 last_change = scmr.last_change
96 last_change = scmr.last_change
96 tip = h.get_changeset_safe(scmr, 'tip')
97 tip = h.get_changeset_safe(scmr, 'tip')
97
98
98 tmp_d = {}
99 tmp_d = {}
99 tmp_d['name'] = dbr.repo_name
100 tmp_d['name'] = dbr.repo_name
100 tmp_d['name_sort'] = tmp_d['name'].lower()
101 tmp_d['name_sort'] = tmp_d['name'].lower()
101 tmp_d['description'] = dbr.description
102 tmp_d['description'] = dbr.description
102 tmp_d['description_sort'] = tmp_d['description']
103 tmp_d['description_sort'] = tmp_d['description']
103 tmp_d['last_change'] = last_change
104 tmp_d['last_change'] = last_change
104 tmp_d['last_change_sort'] = time.mktime(last_change \
105 tmp_d['last_change_sort'] = time.mktime(last_change \
105 .timetuple())
106 .timetuple())
106 tmp_d['tip'] = tip.raw_id
107 tmp_d['tip'] = tip.raw_id
107 tmp_d['tip_sort'] = tip.revision
108 tmp_d['tip_sort'] = tip.revision
108 tmp_d['rev'] = tip.revision
109 tmp_d['rev'] = tip.revision
109 tmp_d['contact'] = dbr.user.full_contact
110 tmp_d['contact'] = dbr.user.full_contact
110 tmp_d['contact_sort'] = tmp_d['contact']
111 tmp_d['contact_sort'] = tmp_d['contact']
111 tmp_d['owner_sort'] = tmp_d['contact']
112 tmp_d['owner_sort'] = tmp_d['contact']
112 tmp_d['repo_archives'] = list(scmr._get_archives())
113 tmp_d['repo_archives'] = list(scmr._get_archives())
113 tmp_d['last_msg'] = tip.message
114 tmp_d['last_msg'] = tip.message
114 tmp_d['author'] = tip.author
115 tmp_d['author'] = tip.author
115 tmp_d['dbrepo'] = dbr.get_dict()
116 tmp_d['dbrepo'] = dbr.get_dict()
116 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork \
117 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork \
117 else {}
118 else {}
118 yield tmp_d
119 yield tmp_d
119
120
120 class ScmModel(BaseModel):
121 class ScmModel(BaseModel):
121 """Generic Scm Model
122 """Generic Scm Model
122 """
123 """
123
124
124 @LazyProperty
125 @LazyProperty
125 def repos_path(self):
126 def repos_path(self):
126 """Get's the repositories root path from database
127 """Get's the repositories root path from database
127 """
128 """
128
129
129 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
130 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
130
131
131 return q.ui_value
132 return q.ui_value
132
133
133 def repo_scan(self, repos_path=None):
134 def repo_scan(self, repos_path=None):
134 """Listing of repositories in given path. This path should not be a
135 """Listing of repositories in given path. This path should not be a
135 repository itself. Return a dictionary of repository objects
136 repository itself. Return a dictionary of repository objects
136
137
137 :param repos_path: path to directory containing repositories
138 :param repos_path: path to directory containing repositories
138 """
139 """
139
140
140 log.info('scanning for repositories in %s', repos_path)
141 log.info('scanning for repositories in %s', repos_path)
141
142
142 if repos_path is None:
143 if repos_path is None:
143 repos_path = self.repos_path
144 repos_path = self.repos_path
144
145
145 baseui = make_ui('db')
146 baseui = make_ui('db')
146 repos_list = {}
147 repos_list = {}
147
148
148 for name, path in get_filesystem_repos(repos_path, recursive=True):
149 for name, path in get_filesystem_repos(repos_path, recursive=True):
150
151 # name need to be decomposed and put back together using the /
152 # since this is internal storage separator for rhodecode
153 name = Repository.url_sep().join(name.split(os.sep))
154
149 try:
155 try:
150 if name in repos_list:
156 if name in repos_list:
151 raise RepositoryError('Duplicate repository name %s '
157 raise RepositoryError('Duplicate repository name %s '
152 'found in %s' % (name, path))
158 'found in %s' % (name, path))
153 else:
159 else:
154
160
155 klass = get_backend(path[0])
161 klass = get_backend(path[0])
156
162
157 if path[0] == 'hg' and path[0] in BACKENDS.keys():
163 if path[0] == 'hg' and path[0] in BACKENDS.keys():
158
164
159 # for mercurial we need to have an str path
165 # for mercurial we need to have an str path
160 repos_list[name] = klass(safe_str(path[1]),
166 repos_list[name] = klass(safe_str(path[1]),
161 baseui=baseui)
167 baseui=baseui)
162
168
163 if path[0] == 'git' and path[0] in BACKENDS.keys():
169 if path[0] == 'git' and path[0] in BACKENDS.keys():
164 repos_list[name] = klass(path[1])
170 repos_list[name] = klass(path[1])
165 except OSError:
171 except OSError:
166 continue
172 continue
167
173
168 return repos_list
174 return repos_list
169
175
170 def get_repos(self, all_repos=None, sort_key=None):
176 def get_repos(self, all_repos=None, sort_key=None):
171 """
177 """
172 Get all repos from db and for each repo create it's
178 Get all repos from db and for each repo create it's
173 backend instance and fill that backed with information from database
179 backend instance and fill that backed with information from database
174
180
175 :param all_repos: list of repository names as strings
181 :param all_repos: list of repository names as strings
176 give specific repositories list, good for filtering
182 give specific repositories list, good for filtering
177 """
183 """
178 if all_repos is None:
184 if all_repos is None:
179 all_repos = self.sa.query(Repository)\
185 all_repos = self.sa.query(Repository)\
180 .filter(Repository.group_id == None)\
186 .filter(Repository.group_id == None)\
181 .order_by(Repository.repo_name).all()
187 .order_by(Repository.repo_name).all()
182
188
183 repo_iter = CachedRepoList(all_repos, repos_path=self.repos_path,
189 repo_iter = CachedRepoList(all_repos, repos_path=self.repos_path,
184 order_by=sort_key)
190 order_by=sort_key)
185
191
186 return repo_iter
192 return repo_iter
187
193
188 def mark_for_invalidation(self, repo_name):
194 def mark_for_invalidation(self, repo_name):
189 """Puts cache invalidation task into db for
195 """Puts cache invalidation task into db for
190 further global cache invalidation
196 further global cache invalidation
191
197
192 :param repo_name: this repo that should invalidation take place
198 :param repo_name: this repo that should invalidation take place
193 """
199 """
194
200
195 log.debug('marking %s for invalidation', repo_name)
201 log.debug('marking %s for invalidation', repo_name)
196 cache = self.sa.query(CacheInvalidation)\
202 cache = self.sa.query(CacheInvalidation)\
197 .filter(CacheInvalidation.cache_key == repo_name).scalar()
203 .filter(CacheInvalidation.cache_key == repo_name).scalar()
198
204
199 if cache:
205 if cache:
200 # mark this cache as inactive
206 # mark this cache as inactive
201 cache.cache_active = False
207 cache.cache_active = False
202 else:
208 else:
203 log.debug('cache key not found in invalidation db -> creating one')
209 log.debug('cache key not found in invalidation db -> creating one')
204 cache = CacheInvalidation(repo_name)
210 cache = CacheInvalidation(repo_name)
205
211
206 try:
212 try:
207 self.sa.add(cache)
213 self.sa.add(cache)
208 self.sa.commit()
214 self.sa.commit()
209 except (DatabaseError,):
215 except (DatabaseError,):
210 log.error(traceback.format_exc())
216 log.error(traceback.format_exc())
211 self.sa.rollback()
217 self.sa.rollback()
212
218
213 def toggle_following_repo(self, follow_repo_id, user_id):
219 def toggle_following_repo(self, follow_repo_id, user_id):
214
220
215 f = self.sa.query(UserFollowing)\
221 f = self.sa.query(UserFollowing)\
216 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
222 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
217 .filter(UserFollowing.user_id == user_id).scalar()
223 .filter(UserFollowing.user_id == user_id).scalar()
218
224
219 if f is not None:
225 if f is not None:
220
226
221 try:
227 try:
222 self.sa.delete(f)
228 self.sa.delete(f)
223 self.sa.commit()
229 self.sa.commit()
224 action_logger(UserTemp(user_id),
230 action_logger(UserTemp(user_id),
225 'stopped_following_repo',
231 'stopped_following_repo',
226 RepoTemp(follow_repo_id))
232 RepoTemp(follow_repo_id))
227 return
233 return
228 except:
234 except:
229 log.error(traceback.format_exc())
235 log.error(traceback.format_exc())
230 self.sa.rollback()
236 self.sa.rollback()
231 raise
237 raise
232
238
233 try:
239 try:
234 f = UserFollowing()
240 f = UserFollowing()
235 f.user_id = user_id
241 f.user_id = user_id
236 f.follows_repo_id = follow_repo_id
242 f.follows_repo_id = follow_repo_id
237 self.sa.add(f)
243 self.sa.add(f)
238 self.sa.commit()
244 self.sa.commit()
239 action_logger(UserTemp(user_id),
245 action_logger(UserTemp(user_id),
240 'started_following_repo',
246 'started_following_repo',
241 RepoTemp(follow_repo_id))
247 RepoTemp(follow_repo_id))
242 except:
248 except:
243 log.error(traceback.format_exc())
249 log.error(traceback.format_exc())
244 self.sa.rollback()
250 self.sa.rollback()
245 raise
251 raise
246
252
247 def toggle_following_user(self, follow_user_id, user_id):
253 def toggle_following_user(self, follow_user_id, user_id):
248 f = self.sa.query(UserFollowing)\
254 f = self.sa.query(UserFollowing)\
249 .filter(UserFollowing.follows_user_id == follow_user_id)\
255 .filter(UserFollowing.follows_user_id == follow_user_id)\
250 .filter(UserFollowing.user_id == user_id).scalar()
256 .filter(UserFollowing.user_id == user_id).scalar()
251
257
252 if f is not None:
258 if f is not None:
253 try:
259 try:
254 self.sa.delete(f)
260 self.sa.delete(f)
255 self.sa.commit()
261 self.sa.commit()
256 return
262 return
257 except:
263 except:
258 log.error(traceback.format_exc())
264 log.error(traceback.format_exc())
259 self.sa.rollback()
265 self.sa.rollback()
260 raise
266 raise
261
267
262 try:
268 try:
263 f = UserFollowing()
269 f = UserFollowing()
264 f.user_id = user_id
270 f.user_id = user_id
265 f.follows_user_id = follow_user_id
271 f.follows_user_id = follow_user_id
266 self.sa.add(f)
272 self.sa.add(f)
267 self.sa.commit()
273 self.sa.commit()
268 except:
274 except:
269 log.error(traceback.format_exc())
275 log.error(traceback.format_exc())
270 self.sa.rollback()
276 self.sa.rollback()
271 raise
277 raise
272
278
273 def is_following_repo(self, repo_name, user_id, cache=False):
279 def is_following_repo(self, repo_name, user_id, cache=False):
274 r = self.sa.query(Repository)\
280 r = self.sa.query(Repository)\
275 .filter(Repository.repo_name == repo_name).scalar()
281 .filter(Repository.repo_name == repo_name).scalar()
276
282
277 f = self.sa.query(UserFollowing)\
283 f = self.sa.query(UserFollowing)\
278 .filter(UserFollowing.follows_repository == r)\
284 .filter(UserFollowing.follows_repository == r)\
279 .filter(UserFollowing.user_id == user_id).scalar()
285 .filter(UserFollowing.user_id == user_id).scalar()
280
286
281 return f is not None
287 return f is not None
282
288
283 def is_following_user(self, username, user_id, cache=False):
289 def is_following_user(self, username, user_id, cache=False):
284 u = User.get_by_username(username)
290 u = User.get_by_username(username)
285
291
286 f = self.sa.query(UserFollowing)\
292 f = self.sa.query(UserFollowing)\
287 .filter(UserFollowing.follows_user == u)\
293 .filter(UserFollowing.follows_user == u)\
288 .filter(UserFollowing.user_id == user_id).scalar()
294 .filter(UserFollowing.user_id == user_id).scalar()
289
295
290 return f is not None
296 return f is not None
291
297
292 def get_followers(self, repo_id):
298 def get_followers(self, repo_id):
293 if not isinstance(repo_id, int):
299 if not isinstance(repo_id, int):
294 repo_id = getattr(Repository.get_by_repo_name(repo_id), 'repo_id')
300 repo_id = getattr(Repository.get_by_repo_name(repo_id), 'repo_id')
295
301
296 return self.sa.query(UserFollowing)\
302 return self.sa.query(UserFollowing)\
297 .filter(UserFollowing.follows_repo_id == repo_id).count()
303 .filter(UserFollowing.follows_repo_id == repo_id).count()
298
304
299 def get_forks(self, repo_id):
305 def get_forks(self, repo_id):
300 if not isinstance(repo_id, int):
306 if not isinstance(repo_id, int):
301 repo_id = getattr(Repository.get_by_repo_name(repo_id), 'repo_id')
307 repo_id = getattr(Repository.get_by_repo_name(repo_id), 'repo_id')
302
308
303 return self.sa.query(Repository)\
309 return self.sa.query(Repository)\
304 .filter(Repository.fork_id == repo_id).count()
310 .filter(Repository.fork_id == repo_id).count()
305
311
306 def pull_changes(self, repo_name, username):
312 def pull_changes(self, repo_name, username):
307 dbrepo = Repository.get_by_repo_name(repo_name)
313 dbrepo = Repository.get_by_repo_name(repo_name)
308 clone_uri = dbrepo.clone_uri
314 clone_uri = dbrepo.clone_uri
309 if not clone_uri:
315 if not clone_uri:
310 raise Exception("This repository doesn't have a clone uri")
316 raise Exception("This repository doesn't have a clone uri")
311
317
312 repo = dbrepo.scm_instance
318 repo = dbrepo.scm_instance
313 try:
319 try:
314 extras = {'ip': '',
320 extras = {'ip': '',
315 'username': username,
321 'username': username,
316 'action': 'push_remote',
322 'action': 'push_remote',
317 'repository': repo_name}
323 'repository': repo_name}
318
324
319 #inject ui extra param to log this action via push logger
325 #inject ui extra param to log this action via push logger
320 for k, v in extras.items():
326 for k, v in extras.items():
321 repo._repo.ui.setconfig('rhodecode_extras', k, v)
327 repo._repo.ui.setconfig('rhodecode_extras', k, v)
322
328
323 repo.pull(clone_uri)
329 repo.pull(clone_uri)
324 self.mark_for_invalidation(repo_name)
330 self.mark_for_invalidation(repo_name)
325 except:
331 except:
326 log.error(traceback.format_exc())
332 log.error(traceback.format_exc())
327 raise
333 raise
328
334
329 def commit_change(self, repo, repo_name, cs, user, author, message, content,
335 def commit_change(self, repo, repo_name, cs, user, author, message, content,
330 f_path):
336 f_path):
331
337
332 if repo.alias == 'hg':
338 if repo.alias == 'hg':
333 from vcs.backends.hg import MercurialInMemoryChangeset as IMC
339 from vcs.backends.hg import MercurialInMemoryChangeset as IMC
334 elif repo.alias == 'git':
340 elif repo.alias == 'git':
335 from vcs.backends.git import GitInMemoryChangeset as IMC
341 from vcs.backends.git import GitInMemoryChangeset as IMC
336
342
337 # decoding here will force that we have proper encoded values
343 # decoding here will force that we have proper encoded values
338 # in any other case this will throw exceptions and deny commit
344 # in any other case this will throw exceptions and deny commit
339 content = safe_str(content)
345 content = safe_str(content)
340 message = safe_str(message)
346 message = safe_str(message)
341 path = safe_str(f_path)
347 path = safe_str(f_path)
342 author = safe_str(author)
348 author = safe_str(author)
343 m = IMC(repo)
349 m = IMC(repo)
344 m.change(FileNode(path, content))
350 m.change(FileNode(path, content))
345 tip = m.commit(message=message,
351 tip = m.commit(message=message,
346 author=author,
352 author=author,
347 parents=[cs], branch=cs.branch)
353 parents=[cs], branch=cs.branch)
348
354
349 new_cs = tip.short_id
355 new_cs = tip.short_id
350 action = 'push_local:%s' % new_cs
356 action = 'push_local:%s' % new_cs
351
357
352 action_logger(user, action, repo_name)
358 action_logger(user, action, repo_name)
353
359
354 self.mark_for_invalidation(repo_name)
360 self.mark_for_invalidation(repo_name)
355
361
356 def create_node(self, repo, repo_name, cs, user, author, message, content,
362 def create_node(self, repo, repo_name, cs, user, author, message, content,
357 f_path):
363 f_path):
358 if repo.alias == 'hg':
364 if repo.alias == 'hg':
359 from vcs.backends.hg import MercurialInMemoryChangeset as IMC
365 from vcs.backends.hg import MercurialInMemoryChangeset as IMC
360 elif repo.alias == 'git':
366 elif repo.alias == 'git':
361 from vcs.backends.git import GitInMemoryChangeset as IMC
367 from vcs.backends.git import GitInMemoryChangeset as IMC
362 # decoding here will force that we have proper encoded values
368 # decoding here will force that we have proper encoded values
363 # in any other case this will throw exceptions and deny commit
369 # in any other case this will throw exceptions and deny commit
364
370
365 if isinstance(content, (basestring,)):
371 if isinstance(content, (basestring,)):
366 content = safe_str(content)
372 content = safe_str(content)
367 elif isinstance(content, file):
373 elif isinstance(content, file):
368 content = content.read()
374 content = content.read()
369
375
370 message = safe_str(message)
376 message = safe_str(message)
371 path = safe_str(f_path)
377 path = safe_str(f_path)
372 author = safe_str(author)
378 author = safe_str(author)
373 m = IMC(repo)
379 m = IMC(repo)
374
380
375 if isinstance(cs, EmptyChangeset):
381 if isinstance(cs, EmptyChangeset):
376 # Emptychangeset means we we're editing empty repository
382 # Emptychangeset means we we're editing empty repository
377 parents = None
383 parents = None
378 else:
384 else:
379 parents = [cs]
385 parents = [cs]
380
386
381 m.add(FileNode(path, content=content))
387 m.add(FileNode(path, content=content))
382 tip = m.commit(message=message,
388 tip = m.commit(message=message,
383 author=author,
389 author=author,
384 parents=parents, branch=cs.branch)
390 parents=parents, branch=cs.branch)
385 new_cs = tip.short_id
391 new_cs = tip.short_id
386 action = 'push_local:%s' % new_cs
392 action = 'push_local:%s' % new_cs
387
393
388 action_logger(user, action, repo_name)
394 action_logger(user, action, repo_name)
389
395
390 self.mark_for_invalidation(repo_name)
396 self.mark_for_invalidation(repo_name)
391
397
392
398
393 def get_unread_journal(self):
399 def get_unread_journal(self):
394 return self.sa.query(UserLog).count()
400 return self.sa.query(UserLog).count()
395
401
396 def _should_invalidate(self, repo_name):
402 def _should_invalidate(self, repo_name):
397 """Looks up database for invalidation signals for this repo_name
403 """Looks up database for invalidation signals for this repo_name
398
404
399 :param repo_name:
405 :param repo_name:
400 """
406 """
401
407
402 ret = self.sa.query(CacheInvalidation)\
408 ret = self.sa.query(CacheInvalidation)\
403 .filter(CacheInvalidation.cache_key == repo_name)\
409 .filter(CacheInvalidation.cache_key == repo_name)\
404 .filter(CacheInvalidation.cache_active == False)\
410 .filter(CacheInvalidation.cache_active == False)\
405 .scalar()
411 .scalar()
406
412
407 return ret
413 return ret
408
414
General Comments 0
You need to be logged in to leave comments. Login now