##// END OF EJS Templates
fixed some issues with cache invalidation, and simplified invalidation codes
marcink -
r1428:e5467730 beta
parent child Browse files
Show More
@@ -1,800 +1,799 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.orm import relationship, backref, joinedload, class_mapper
34 from sqlalchemy.orm import relationship, backref, joinedload, class_mapper
35 from sqlalchemy.orm.interfaces import MapperExtension
35 from sqlalchemy.orm.interfaces import MapperExtension
36
36
37 from beaker.cache import cache_region, region_invalidate
37 from beaker.cache import cache_region, region_invalidate
38
38
39 from vcs import get_backend
39 from vcs import get_backend
40 from vcs.utils.helpers import get_scm
40 from vcs.utils.helpers import get_scm
41 from vcs.exceptions import RepositoryError, VCSError
41 from vcs.exceptions import RepositoryError, VCSError
42 from vcs.utils.lazy import LazyProperty
42 from vcs.utils.lazy import LazyProperty
43 from vcs.nodes import FileNode
43 from vcs.nodes import FileNode
44
44
45 from rhodecode.lib import str2bool, json, safe_str
45 from rhodecode.lib import str2bool, json, safe_str
46 from rhodecode.model.meta import Base, Session
46 from rhodecode.model.meta import Base, Session
47 from rhodecode.model.caching_query import FromCache
47 from rhodecode.model.caching_query import FromCache
48
48
49 log = logging.getLogger(__name__)
49 log = logging.getLogger(__name__)
50
50
51 #==============================================================================
51 #==============================================================================
52 # BASE CLASSES
52 # BASE CLASSES
53 #==============================================================================
53 #==============================================================================
54
54
55 class ModelSerializer(json.JSONEncoder):
55 class ModelSerializer(json.JSONEncoder):
56 """
56 """
57 Simple Serializer for JSON,
57 Simple Serializer for JSON,
58
58
59 usage::
59 usage::
60
60
61 to make object customized for serialization implement a __json__
61 to make object customized for serialization implement a __json__
62 method that will return a dict for serialization into json
62 method that will return a dict for serialization into json
63
63
64 example::
64 example::
65
65
66 class Task(object):
66 class Task(object):
67
67
68 def __init__(self, name, value):
68 def __init__(self, name, value):
69 self.name = name
69 self.name = name
70 self.value = value
70 self.value = value
71
71
72 def __json__(self):
72 def __json__(self):
73 return dict(name=self.name,
73 return dict(name=self.name,
74 value=self.value)
74 value=self.value)
75
75
76 """
76 """
77
77
78 def default(self, obj):
78 def default(self, obj):
79
79
80 if hasattr(obj, '__json__'):
80 if hasattr(obj, '__json__'):
81 return obj.__json__()
81 return obj.__json__()
82 else:
82 else:
83 return json.JSONEncoder.default(self, obj)
83 return json.JSONEncoder.default(self, obj)
84
84
85 class BaseModel(object):
85 class BaseModel(object):
86 """Base Model for all classess
86 """Base Model for all classess
87
87
88 """
88 """
89
89
90 @classmethod
90 @classmethod
91 def _get_keys(cls):
91 def _get_keys(cls):
92 """return column names for this model """
92 """return column names for this model """
93 return class_mapper(cls).c.keys()
93 return class_mapper(cls).c.keys()
94
94
95 def get_dict(self):
95 def get_dict(self):
96 """return dict with keys and values corresponding
96 """return dict with keys and values corresponding
97 to this model data """
97 to this model data """
98
98
99 d = {}
99 d = {}
100 for k in self._get_keys():
100 for k in self._get_keys():
101 d[k] = getattr(self, k)
101 d[k] = getattr(self, k)
102 return d
102 return d
103
103
104 def get_appstruct(self):
104 def get_appstruct(self):
105 """return list with keys and values tupples corresponding
105 """return list with keys and values tupples corresponding
106 to this model data """
106 to this model data """
107
107
108 l = []
108 l = []
109 for k in self._get_keys():
109 for k in self._get_keys():
110 l.append((k, getattr(self, k),))
110 l.append((k, getattr(self, k),))
111 return l
111 return l
112
112
113 def populate_obj(self, populate_dict):
113 def populate_obj(self, populate_dict):
114 """populate model with data from given populate_dict"""
114 """populate model with data from given populate_dict"""
115
115
116 for k in self._get_keys():
116 for k in self._get_keys():
117 if k in populate_dict:
117 if k in populate_dict:
118 setattr(self, k, populate_dict[k])
118 setattr(self, k, populate_dict[k])
119
119
120 @classmethod
120 @classmethod
121 def query(cls):
121 def query(cls):
122 return Session.query(cls)
122 return Session.query(cls)
123
123
124 @classmethod
124 @classmethod
125 def get(cls, id_):
125 def get(cls, id_):
126 return Session.query(cls).get(id_)
126 return Session.query(cls).get(id_)
127
127
128
128
129 class RhodeCodeSettings(Base, BaseModel):
129 class RhodeCodeSettings(Base, BaseModel):
130 __tablename__ = 'rhodecode_settings'
130 __tablename__ = 'rhodecode_settings'
131 __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
131 __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
132 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
132 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
133 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
133 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
134 app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
134 app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
135
135
136 def __init__(self, k='', v=''):
136 def __init__(self, k='', v=''):
137 self.app_settings_name = k
137 self.app_settings_name = k
138 self.app_settings_value = v
138 self.app_settings_value = v
139
139
140 def __repr__(self):
140 def __repr__(self):
141 return "<%s('%s:%s')>" % (self.__class__.__name__,
141 return "<%s('%s:%s')>" % (self.__class__.__name__,
142 self.app_settings_name, self.app_settings_value)
142 self.app_settings_name, self.app_settings_value)
143
143
144
144
145 @classmethod
145 @classmethod
146 def get_by_name(cls, ldap_key):
146 def get_by_name(cls, ldap_key):
147 return Session.query(cls)\
147 return Session.query(cls)\
148 .filter(cls.app_settings_name == ldap_key).scalar()
148 .filter(cls.app_settings_name == ldap_key).scalar()
149
149
150 @classmethod
150 @classmethod
151 def get_app_settings(cls, cache=False):
151 def get_app_settings(cls, cache=False):
152
152
153 ret = Session.query(cls)
153 ret = Session.query(cls)
154
154
155 if cache:
155 if cache:
156 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
156 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
157
157
158 if not ret:
158 if not ret:
159 raise Exception('Could not get application settings !')
159 raise Exception('Could not get application settings !')
160 settings = {}
160 settings = {}
161 for each in ret:
161 for each in ret:
162 settings['rhodecode_' + each.app_settings_name] = \
162 settings['rhodecode_' + each.app_settings_name] = \
163 each.app_settings_value
163 each.app_settings_value
164
164
165 return settings
165 return settings
166
166
167 @classmethod
167 @classmethod
168 def get_ldap_settings(cls, cache=False):
168 def get_ldap_settings(cls, cache=False):
169 ret = Session.query(cls)\
169 ret = Session.query(cls)\
170 .filter(cls.app_settings_name.startswith('ldap_'))\
170 .filter(cls.app_settings_name.startswith('ldap_'))\
171 .all()
171 .all()
172 fd = {}
172 fd = {}
173 for row in ret:
173 for row in ret:
174 fd.update({row.app_settings_name:row.app_settings_value})
174 fd.update({row.app_settings_name:row.app_settings_value})
175
175
176 fd.update({'ldap_active':str2bool(fd.get('ldap_active'))})
176 fd.update({'ldap_active':str2bool(fd.get('ldap_active'))})
177
177
178 return fd
178 return fd
179
179
180
180
181 class RhodeCodeUi(Base, BaseModel):
181 class RhodeCodeUi(Base, BaseModel):
182 __tablename__ = 'rhodecode_ui'
182 __tablename__ = 'rhodecode_ui'
183 __table_args__ = {'extend_existing':True}
183 __table_args__ = {'extend_existing':True}
184 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
184 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
185 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
185 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
186 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
186 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
187 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
187 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
188 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
188 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
189
189
190
190
191 @classmethod
191 @classmethod
192 def get_by_key(cls, key):
192 def get_by_key(cls, key):
193 return Session.query(cls).filter(cls.ui_key == key)
193 return Session.query(cls).filter(cls.ui_key == key)
194
194
195
195
196 class User(Base, BaseModel):
196 class User(Base, BaseModel):
197 __tablename__ = 'users'
197 __tablename__ = 'users'
198 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True})
198 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True})
199 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
199 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
200 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
200 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
201 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
201 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
202 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
202 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
203 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
203 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
204 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
204 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
205 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
205 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
206 email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
206 email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
207 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
207 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
208 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
208 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
209 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
209 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
210
210
211 user_log = relationship('UserLog', cascade='all')
211 user_log = relationship('UserLog', cascade='all')
212 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
212 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
213
213
214 repositories = relationship('Repository')
214 repositories = relationship('Repository')
215 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
215 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
216 repo_to_perm = relationship('RepoToPerm', primaryjoin='RepoToPerm.user_id==User.user_id', cascade='all')
216 repo_to_perm = relationship('RepoToPerm', primaryjoin='RepoToPerm.user_id==User.user_id', cascade='all')
217
217
218 group_member = relationship('UsersGroupMember', cascade='all')
218 group_member = relationship('UsersGroupMember', cascade='all')
219
219
220 @property
220 @property
221 def full_contact(self):
221 def full_contact(self):
222 return '%s %s <%s>' % (self.name, self.lastname, self.email)
222 return '%s %s <%s>' % (self.name, self.lastname, self.email)
223
223
224 @property
224 @property
225 def short_contact(self):
225 def short_contact(self):
226 return '%s %s' % (self.name, self.lastname)
226 return '%s %s' % (self.name, self.lastname)
227
227
228 @property
228 @property
229 def is_admin(self):
229 def is_admin(self):
230 return self.admin
230 return self.admin
231
231
232 def __repr__(self):
232 def __repr__(self):
233 try:
233 try:
234 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
234 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
235 self.user_id, self.username)
235 self.user_id, self.username)
236 except:
236 except:
237 return self.__class__.__name__
237 return self.__class__.__name__
238
238
239 @classmethod
239 @classmethod
240 def by_username(cls, username, case_insensitive=False):
240 def by_username(cls, username, case_insensitive=False):
241 if case_insensitive:
241 if case_insensitive:
242 return Session.query(cls).filter(cls.username.like(username)).one()
242 return Session.query(cls).filter(cls.username.like(username)).one()
243 else:
243 else:
244 return Session.query(cls).filter(cls.username == username).one()
244 return Session.query(cls).filter(cls.username == username).one()
245
245
246 @classmethod
246 @classmethod
247 def get_by_api_key(cls, api_key):
247 def get_by_api_key(cls, api_key):
248 return Session.query(cls).filter(cls.api_key == api_key).one()
248 return Session.query(cls).filter(cls.api_key == api_key).one()
249
249
250
250
251 def update_lastlogin(self):
251 def update_lastlogin(self):
252 """Update user lastlogin"""
252 """Update user lastlogin"""
253
253
254 self.last_login = datetime.datetime.now()
254 self.last_login = datetime.datetime.now()
255 Session.add(self)
255 Session.add(self)
256 Session.commit()
256 Session.commit()
257 log.debug('updated user %s lastlogin', self.username)
257 log.debug('updated user %s lastlogin', self.username)
258
258
259
259
260 class UserLog(Base, BaseModel):
260 class UserLog(Base, BaseModel):
261 __tablename__ = 'user_logs'
261 __tablename__ = 'user_logs'
262 __table_args__ = {'extend_existing':True}
262 __table_args__ = {'extend_existing':True}
263 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
263 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
264 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
264 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
265 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
265 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
266 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
266 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
267 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
267 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
268 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
268 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
269 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
269 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
270
270
271 @property
271 @property
272 def action_as_day(self):
272 def action_as_day(self):
273 return date(*self.action_date.timetuple()[:3])
273 return date(*self.action_date.timetuple()[:3])
274
274
275 user = relationship('User')
275 user = relationship('User')
276 repository = relationship('Repository')
276 repository = relationship('Repository')
277
277
278
278
279 class UsersGroup(Base, BaseModel):
279 class UsersGroup(Base, BaseModel):
280 __tablename__ = 'users_groups'
280 __tablename__ = 'users_groups'
281 __table_args__ = {'extend_existing':True}
281 __table_args__ = {'extend_existing':True}
282
282
283 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
283 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
284 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
284 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
285 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
285 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
286
286
287 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
287 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
288
288
289
289
290 @classmethod
290 @classmethod
291 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
291 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
292 if case_insensitive:
292 if case_insensitive:
293 gr = Session.query(cls)\
293 gr = Session.query(cls)\
294 .filter(cls.users_group_name.ilike(group_name))
294 .filter(cls.users_group_name.ilike(group_name))
295 else:
295 else:
296 gr = Session.query(UsersGroup)\
296 gr = Session.query(UsersGroup)\
297 .filter(UsersGroup.users_group_name == group_name)
297 .filter(UsersGroup.users_group_name == group_name)
298 if cache:
298 if cache:
299 gr = gr.options(FromCache("sql_cache_short",
299 gr = gr.options(FromCache("sql_cache_short",
300 "get_user_%s" % group_name))
300 "get_user_%s" % group_name))
301 return gr.scalar()
301 return gr.scalar()
302
302
303 class UsersGroupMember(Base, BaseModel):
303 class UsersGroupMember(Base, BaseModel):
304 __tablename__ = 'users_groups_members'
304 __tablename__ = 'users_groups_members'
305 __table_args__ = {'extend_existing':True}
305 __table_args__ = {'extend_existing':True}
306
306
307 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
307 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
308 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
308 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
309 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
309 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
310
310
311 user = relationship('User', lazy='joined')
311 user = relationship('User', lazy='joined')
312 users_group = relationship('UsersGroup')
312 users_group = relationship('UsersGroup')
313
313
314 def __init__(self, gr_id='', u_id=''):
314 def __init__(self, gr_id='', u_id=''):
315 self.users_group_id = gr_id
315 self.users_group_id = gr_id
316 self.user_id = u_id
316 self.user_id = u_id
317
317
318 class Repository(Base, BaseModel):
318 class Repository(Base, BaseModel):
319 __tablename__ = 'repositories'
319 __tablename__ = 'repositories'
320 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
320 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
321
321
322 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
322 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
323 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
323 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
324 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
324 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
325 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
325 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
326 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
326 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
327 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
327 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
328 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
328 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
329 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
329 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
330 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
330 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
331 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
331 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
332
332
333 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
333 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
334 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
334 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
335
335
336
336
337 user = relationship('User')
337 user = relationship('User')
338 fork = relationship('Repository', remote_side=repo_id)
338 fork = relationship('Repository', remote_side=repo_id)
339 group = relationship('Group')
339 group = relationship('Group')
340 repo_to_perm = relationship('RepoToPerm', cascade='all', order_by='RepoToPerm.repo_to_perm_id')
340 repo_to_perm = relationship('RepoToPerm', cascade='all', order_by='RepoToPerm.repo_to_perm_id')
341 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
341 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
342 stats = relationship('Statistics', cascade='all', uselist=False)
342 stats = relationship('Statistics', cascade='all', uselist=False)
343
343
344 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
344 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
345
345
346 logs = relationship('UserLog', cascade='all')
346 logs = relationship('UserLog', cascade='all')
347
347
348 def __repr__(self):
348 def __repr__(self):
349 return "<%s('%s:%s')>" % (self.__class__.__name__,
349 return "<%s('%s:%s')>" % (self.__class__.__name__,
350 self.repo_id, self.repo_name)
350 self.repo_id, self.repo_name)
351
351
352 @classmethod
352 @classmethod
353 def by_repo_name(cls, repo_name):
353 def by_repo_name(cls, repo_name):
354 q = Session.query(cls).filter(cls.repo_name == repo_name)
354 q = Session.query(cls).filter(cls.repo_name == repo_name)
355
355
356 q = q.options(joinedload(Repository.fork))\
356 q = q.options(joinedload(Repository.fork))\
357 .options(joinedload(Repository.user))\
357 .options(joinedload(Repository.user))\
358 .options(joinedload(Repository.group))\
358 .options(joinedload(Repository.group))\
359
359
360 return q.one()
360 return q.one()
361
361
362 @classmethod
362 @classmethod
363 def get_repo_forks(cls, repo_id):
363 def get_repo_forks(cls, repo_id):
364 return Session.query(cls).filter(Repository.fork_id == repo_id)
364 return Session.query(cls).filter(Repository.fork_id == repo_id)
365
365
366 @property
366 @property
367 def just_name(self):
367 def just_name(self):
368 return self.repo_name.split(os.sep)[-1]
368 return self.repo_name.split(os.sep)[-1]
369
369
370 @property
370 @property
371 def groups_with_parents(self):
371 def groups_with_parents(self):
372 groups = []
372 groups = []
373 if self.group is None:
373 if self.group is None:
374 return groups
374 return groups
375
375
376 cur_gr = self.group
376 cur_gr = self.group
377 groups.insert(0, cur_gr)
377 groups.insert(0, cur_gr)
378 while 1:
378 while 1:
379 gr = getattr(cur_gr, 'parent_group', None)
379 gr = getattr(cur_gr, 'parent_group', None)
380 cur_gr = cur_gr.parent_group
380 cur_gr = cur_gr.parent_group
381 if gr is None:
381 if gr is None:
382 break
382 break
383 groups.insert(0, gr)
383 groups.insert(0, gr)
384
384
385 return groups
385 return groups
386
386
387 @property
387 @property
388 def groups_and_repo(self):
388 def groups_and_repo(self):
389 return self.groups_with_parents, self.just_name
389 return self.groups_with_parents, self.just_name
390
390
391 @LazyProperty
391 @LazyProperty
392 def repo_path(self):
392 def repo_path(self):
393 """
393 """
394 Returns base full path for that repository means where it actually
394 Returns base full path for that repository means where it actually
395 exists on a filesystem
395 exists on a filesystem
396 """
396 """
397 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/')
397 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/')
398 q.options(FromCache("sql_cache_short", "repository_repo_path"))
398 q.options(FromCache("sql_cache_short", "repository_repo_path"))
399 return q.one().ui_value
399 return q.one().ui_value
400
400
401 @property
401 @property
402 def repo_full_path(self):
402 def repo_full_path(self):
403 p = [self.repo_path]
403 p = [self.repo_path]
404 # we need to split the name by / since this is how we store the
404 # we need to split the name by / since this is how we store the
405 # names in the database, but that eventually needs to be converted
405 # names in the database, but that eventually needs to be converted
406 # into a valid system path
406 # into a valid system path
407 p += self.repo_name.split('/')
407 p += self.repo_name.split('/')
408 return os.path.join(*p)
408 return os.path.join(*p)
409
409
410 @property
410 @property
411 def _ui(self):
411 def _ui(self):
412 """
412 """
413 Creates an db based ui object for this repository
413 Creates an db based ui object for this repository
414 """
414 """
415 from mercurial import ui
415 from mercurial import ui
416 from mercurial import config
416 from mercurial import config
417 baseui = ui.ui()
417 baseui = ui.ui()
418
418
419 #clean the baseui object
419 #clean the baseui object
420 baseui._ocfg = config.config()
420 baseui._ocfg = config.config()
421 baseui._ucfg = config.config()
421 baseui._ucfg = config.config()
422 baseui._tcfg = config.config()
422 baseui._tcfg = config.config()
423
423
424
424
425 ret = Session.query(RhodeCodeUi)\
425 ret = Session.query(RhodeCodeUi)\
426 .options(FromCache("sql_cache_short",
426 .options(FromCache("sql_cache_short",
427 "repository_repo_ui")).all()
427 "repository_repo_ui")).all()
428
428
429 hg_ui = ret
429 hg_ui = ret
430 for ui_ in hg_ui:
430 for ui_ in hg_ui:
431 if ui_.ui_active:
431 if ui_.ui_active:
432 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
432 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
433 ui_.ui_key, ui_.ui_value)
433 ui_.ui_key, ui_.ui_value)
434 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
434 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
435
435
436 return baseui
436 return baseui
437
437
438 #==========================================================================
438 #==========================================================================
439 # SCM CACHE INSTANCE
439 # SCM CACHE INSTANCE
440 #==========================================================================
440 #==========================================================================
441
441
442 @property
442 @property
443 def invalidate(self):
443 def invalidate(self):
444 """
444 """
445 Returns Invalidation object if this repo should be invalidated
445 Returns Invalidation object if this repo should be invalidated
446 None otherwise. `cache_active = False` means that this cache
446 None otherwise. `cache_active = False` means that this cache
447 state is not valid and needs to be invalidated
447 state is not valid and needs to be invalidated
448 """
448 """
449 return Session.query(CacheInvalidation)\
449 return Session.query(CacheInvalidation)\
450 .filter(CacheInvalidation.cache_key == self.repo_name)\
450 .filter(CacheInvalidation.cache_key == self.repo_name)\
451 .filter(CacheInvalidation.cache_active == False)\
451 .filter(CacheInvalidation.cache_active == False)\
452 .scalar()
452 .scalar()
453
453
454 @property
455 def set_invalidate(self):
454 def set_invalidate(self):
456 """
455 """
457 set a cache for invalidation for this instance
456 set a cache for invalidation for this instance
458 """
457 """
459 inv = Session.query(CacheInvalidation)\
458 inv = Session.query(CacheInvalidation)\
460 .filter(CacheInvalidation.cache_key == self.repo_name)\
459 .filter(CacheInvalidation.cache_key == self.repo_name)\
461 .scalar()
460 .scalar()
462
461
463 if inv is None:
462 if inv is None:
464 inv = CacheInvalidation(self.repo_name)
463 inv = CacheInvalidation(self.repo_name)
465 inv.cache_active = True
464 inv.cache_active = True
466 Session.add(inv)
465 Session.add(inv)
467 Session.commit()
466 Session.commit()
468
467
469 @property
468 @property
470 def scm_instance(self):
469 def scm_instance(self):
471 return self.__get_instance()
470 return self.__get_instance()
472
471
473 @property
472 @property
474 def scm_instance_cached(self):
473 def scm_instance_cached(self):
475 @cache_region('long_term')
474 @cache_region('long_term')
476 def _c(repo_name):
475 def _c(repo_name):
477 return self.__get_instance()
476 return self.__get_instance()
478
477
478 # TODO: remove this trick when beaker 1.6 is released
479 # and have fixed this issue with not supporting unicode keys
480 rn = safe_str(self.repo_name)
481
479 inv = self.invalidate
482 inv = self.invalidate
480 if inv is not None:
483 if inv is not None:
481 region_invalidate(_c, None, self.repo_name)
484 region_invalidate(_c, None, rn)
482 #update our cache
485 # update our cache
483 inv.cache_active = True
486 inv.cache_active = True
484 Session.add(inv)
487 Session.add(inv)
485 Session.commit()
488 Session.commit()
486
489
487 # TODO: remove this trick when beaker 1.6 is released
488 # and have fixed this issue
489 rn = safe_str(self.repo_name)
490
491 return _c(rn)
490 return _c(rn)
492
491
493 def __get_instance(self):
492 def __get_instance(self):
494
493
495 repo_full_path = self.repo_full_path
494 repo_full_path = self.repo_full_path
496
495
497 try:
496 try:
498 alias = get_scm(repo_full_path)[0]
497 alias = get_scm(repo_full_path)[0]
499 log.debug('Creating instance of %s repository', alias)
498 log.debug('Creating instance of %s repository', alias)
500 backend = get_backend(alias)
499 backend = get_backend(alias)
501 except VCSError:
500 except VCSError:
502 log.error(traceback.format_exc())
501 log.error(traceback.format_exc())
503 log.error('Perhaps this repository is in db and not in '
502 log.error('Perhaps this repository is in db and not in '
504 'filesystem run rescan repositories with '
503 'filesystem run rescan repositories with '
505 '"destroy old data " option from admin panel')
504 '"destroy old data " option from admin panel')
506 return
505 return
507
506
508 if alias == 'hg':
507 if alias == 'hg':
509
508
510 repo = backend(safe_str(repo_full_path), create=False,
509 repo = backend(safe_str(repo_full_path), create=False,
511 baseui=self._ui)
510 baseui=self._ui)
512 #skip hidden web repository
511 #skip hidden web repository
513 if repo._get_hidden():
512 if repo._get_hidden():
514 return
513 return
515 else:
514 else:
516 repo = backend(repo_full_path, create=False)
515 repo = backend(repo_full_path, create=False)
517
516
518 return repo
517 return repo
519
518
520
519
521 class Group(Base, BaseModel):
520 class Group(Base, BaseModel):
522 __tablename__ = 'groups'
521 __tablename__ = 'groups'
523 __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'),
522 __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'),
524 CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},)
523 CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},)
525 __mapper_args__ = {'order_by':'group_name'}
524 __mapper_args__ = {'order_by':'group_name'}
526
525
527 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
526 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
528 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
527 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
529 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
528 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
530 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
529 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
531
530
532 parent_group = relationship('Group', remote_side=group_id)
531 parent_group = relationship('Group', remote_side=group_id)
533
532
534
533
535 def __init__(self, group_name='', parent_group=None):
534 def __init__(self, group_name='', parent_group=None):
536 self.group_name = group_name
535 self.group_name = group_name
537 self.parent_group = parent_group
536 self.parent_group = parent_group
538
537
539 def __repr__(self):
538 def __repr__(self):
540 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
539 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
541 self.group_name)
540 self.group_name)
542
541
543 @classmethod
542 @classmethod
544 def url_sep(cls):
543 def url_sep(cls):
545 return '/'
544 return '/'
546
545
547 @property
546 @property
548 def parents(self):
547 def parents(self):
549 parents_recursion_limit = 5
548 parents_recursion_limit = 5
550 groups = []
549 groups = []
551 if self.parent_group is None:
550 if self.parent_group is None:
552 return groups
551 return groups
553 cur_gr = self.parent_group
552 cur_gr = self.parent_group
554 groups.insert(0, cur_gr)
553 groups.insert(0, cur_gr)
555 cnt = 0
554 cnt = 0
556 while 1:
555 while 1:
557 cnt += 1
556 cnt += 1
558 gr = getattr(cur_gr, 'parent_group', None)
557 gr = getattr(cur_gr, 'parent_group', None)
559 cur_gr = cur_gr.parent_group
558 cur_gr = cur_gr.parent_group
560 if gr is None:
559 if gr is None:
561 break
560 break
562 if cnt == parents_recursion_limit:
561 if cnt == parents_recursion_limit:
563 # this will prevent accidental infinit loops
562 # this will prevent accidental infinit loops
564 log.error('group nested more than %s' %
563 log.error('group nested more than %s' %
565 parents_recursion_limit)
564 parents_recursion_limit)
566 break
565 break
567
566
568 groups.insert(0, gr)
567 groups.insert(0, gr)
569 return groups
568 return groups
570
569
571 @property
570 @property
572 def children(self):
571 def children(self):
573 return Session.query(Group).filter(Group.parent_group == self)
572 return Session.query(Group).filter(Group.parent_group == self)
574
573
575 @property
574 @property
576 def full_path(self):
575 def full_path(self):
577 return Group.url_sep().join([g.group_name for g in self.parents] +
576 return Group.url_sep().join([g.group_name for g in self.parents] +
578 [self.group_name])
577 [self.group_name])
579
578
580 @property
579 @property
581 def repositories(self):
580 def repositories(self):
582 return Session.query(Repository).filter(Repository.group == self)
581 return Session.query(Repository).filter(Repository.group == self)
583
582
584 @property
583 @property
585 def repositories_recursive_count(self):
584 def repositories_recursive_count(self):
586 cnt = self.repositories.count()
585 cnt = self.repositories.count()
587
586
588 def children_count(group):
587 def children_count(group):
589 cnt = 0
588 cnt = 0
590 for child in group.children:
589 for child in group.children:
591 cnt += child.repositories.count()
590 cnt += child.repositories.count()
592 cnt += children_count(child)
591 cnt += children_count(child)
593 return cnt
592 return cnt
594
593
595 return cnt + children_count(self)
594 return cnt + children_count(self)
596
595
597 class Permission(Base, BaseModel):
596 class Permission(Base, BaseModel):
598 __tablename__ = 'permissions'
597 __tablename__ = 'permissions'
599 __table_args__ = {'extend_existing':True}
598 __table_args__ = {'extend_existing':True}
600 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
599 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
601 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
600 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
602 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
601 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
603
602
604 def __repr__(self):
603 def __repr__(self):
605 return "<%s('%s:%s')>" % (self.__class__.__name__,
604 return "<%s('%s:%s')>" % (self.__class__.__name__,
606 self.permission_id, self.permission_name)
605 self.permission_id, self.permission_name)
607
606
608 @classmethod
607 @classmethod
609 def get_by_key(cls, key):
608 def get_by_key(cls, key):
610 return Session.query(cls).filter(cls.permission_name == key).scalar()
609 return Session.query(cls).filter(cls.permission_name == key).scalar()
611
610
612 class RepoToPerm(Base, BaseModel):
611 class RepoToPerm(Base, BaseModel):
613 __tablename__ = 'repo_to_perm'
612 __tablename__ = 'repo_to_perm'
614 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True})
613 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True})
615 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
614 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
616 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
615 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
617 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
616 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
618 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
617 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
619
618
620 user = relationship('User')
619 user = relationship('User')
621 permission = relationship('Permission')
620 permission = relationship('Permission')
622 repository = relationship('Repository')
621 repository = relationship('Repository')
623
622
624 class UserToPerm(Base, BaseModel):
623 class UserToPerm(Base, BaseModel):
625 __tablename__ = 'user_to_perm'
624 __tablename__ = 'user_to_perm'
626 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
625 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
627 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
626 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
628 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
627 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
629 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
628 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
630
629
631 user = relationship('User')
630 user = relationship('User')
632 permission = relationship('Permission')
631 permission = relationship('Permission')
633
632
634 @classmethod
633 @classmethod
635 def has_perm(cls, user_id, perm):
634 def has_perm(cls, user_id, perm):
636 if not isinstance(perm, Permission):
635 if not isinstance(perm, Permission):
637 raise Exception('perm needs to be an instance of Permission class')
636 raise Exception('perm needs to be an instance of Permission class')
638
637
639 return Session.query(cls).filter(cls.user_id == user_id)\
638 return Session.query(cls).filter(cls.user_id == user_id)\
640 .filter(cls.permission == perm).scalar() is not None
639 .filter(cls.permission == perm).scalar() is not None
641
640
642 @classmethod
641 @classmethod
643 def grant_perm(cls, user_id, perm):
642 def grant_perm(cls, user_id, perm):
644 if not isinstance(perm, Permission):
643 if not isinstance(perm, Permission):
645 raise Exception('perm needs to be an instance of Permission class')
644 raise Exception('perm needs to be an instance of Permission class')
646
645
647 new = cls()
646 new = cls()
648 new.user_id = user_id
647 new.user_id = user_id
649 new.permission = perm
648 new.permission = perm
650 try:
649 try:
651 Session.add(new)
650 Session.add(new)
652 Session.commit()
651 Session.commit()
653 except:
652 except:
654 Session.rollback()
653 Session.rollback()
655
654
656
655
657 @classmethod
656 @classmethod
658 def revoke_perm(cls, user_id, perm):
657 def revoke_perm(cls, user_id, perm):
659 if not isinstance(perm, Permission):
658 if not isinstance(perm, Permission):
660 raise Exception('perm needs to be an instance of Permission class')
659 raise Exception('perm needs to be an instance of Permission class')
661
660
662 try:
661 try:
663 Session.query(cls).filter(cls.user_id == user_id)\
662 Session.query(cls).filter(cls.user_id == user_id)\
664 .filter(cls.permission == perm).delete()
663 .filter(cls.permission == perm).delete()
665 Session.commit()
664 Session.commit()
666 except:
665 except:
667 Session.rollback()
666 Session.rollback()
668
667
669 class UsersGroupRepoToPerm(Base, BaseModel):
668 class UsersGroupRepoToPerm(Base, BaseModel):
670 __tablename__ = 'users_group_repo_to_perm'
669 __tablename__ = 'users_group_repo_to_perm'
671 __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True})
670 __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True})
672 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
671 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
673 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
672 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
674 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
673 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
675 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
674 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
676
675
677 users_group = relationship('UsersGroup')
676 users_group = relationship('UsersGroup')
678 permission = relationship('Permission')
677 permission = relationship('Permission')
679 repository = relationship('Repository')
678 repository = relationship('Repository')
680
679
681
680
682 class UsersGroupToPerm(Base, BaseModel):
681 class UsersGroupToPerm(Base, BaseModel):
683 __tablename__ = 'users_group_to_perm'
682 __tablename__ = 'users_group_to_perm'
684 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
683 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
685 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
684 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
686 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
685 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
687
686
688 users_group = relationship('UsersGroup')
687 users_group = relationship('UsersGroup')
689 permission = relationship('Permission')
688 permission = relationship('Permission')
690
689
691
690
692 @classmethod
691 @classmethod
693 def has_perm(cls, users_group_id, perm):
692 def has_perm(cls, users_group_id, perm):
694 if not isinstance(perm, Permission):
693 if not isinstance(perm, Permission):
695 raise Exception('perm needs to be an instance of Permission class')
694 raise Exception('perm needs to be an instance of Permission class')
696
695
697 return Session.query(cls).filter(cls.users_group_id ==
696 return Session.query(cls).filter(cls.users_group_id ==
698 users_group_id)\
697 users_group_id)\
699 .filter(cls.permission == perm)\
698 .filter(cls.permission == perm)\
700 .scalar() is not None
699 .scalar() is not None
701
700
702 @classmethod
701 @classmethod
703 def grant_perm(cls, users_group_id, perm):
702 def grant_perm(cls, users_group_id, perm):
704 if not isinstance(perm, Permission):
703 if not isinstance(perm, Permission):
705 raise Exception('perm needs to be an instance of Permission class')
704 raise Exception('perm needs to be an instance of Permission class')
706
705
707 new = cls()
706 new = cls()
708 new.users_group_id = users_group_id
707 new.users_group_id = users_group_id
709 new.permission = perm
708 new.permission = perm
710 try:
709 try:
711 Session.add(new)
710 Session.add(new)
712 Session.commit()
711 Session.commit()
713 except:
712 except:
714 Session.rollback()
713 Session.rollback()
715
714
716
715
717 @classmethod
716 @classmethod
718 def revoke_perm(cls, users_group_id, perm):
717 def revoke_perm(cls, users_group_id, perm):
719 if not isinstance(perm, Permission):
718 if not isinstance(perm, Permission):
720 raise Exception('perm needs to be an instance of Permission class')
719 raise Exception('perm needs to be an instance of Permission class')
721
720
722 try:
721 try:
723 Session.query(cls).filter(cls.users_group_id == users_group_id)\
722 Session.query(cls).filter(cls.users_group_id == users_group_id)\
724 .filter(cls.permission == perm).delete()
723 .filter(cls.permission == perm).delete()
725 Session.commit()
724 Session.commit()
726 except:
725 except:
727 Session.rollback()
726 Session.rollback()
728
727
729
728
730 class GroupToPerm(Base, BaseModel):
729 class GroupToPerm(Base, BaseModel):
731 __tablename__ = 'group_to_perm'
730 __tablename__ = 'group_to_perm'
732 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
731 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
733
732
734 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
733 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
735 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
734 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
736 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
735 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
737 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
736 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
738
737
739 user = relationship('User')
738 user = relationship('User')
740 permission = relationship('Permission')
739 permission = relationship('Permission')
741 group = relationship('Group')
740 group = relationship('Group')
742
741
743 class Statistics(Base, BaseModel):
742 class Statistics(Base, BaseModel):
744 __tablename__ = 'statistics'
743 __tablename__ = 'statistics'
745 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True})
744 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True})
746 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
745 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
747 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
746 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
748 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
747 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
749 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
748 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
750 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
749 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
751 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
750 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
752
751
753 repository = relationship('Repository', single_parent=True)
752 repository = relationship('Repository', single_parent=True)
754
753
755 class UserFollowing(Base, BaseModel):
754 class UserFollowing(Base, BaseModel):
756 __tablename__ = 'user_followings'
755 __tablename__ = 'user_followings'
757 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
756 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
758 UniqueConstraint('user_id', 'follows_user_id')
757 UniqueConstraint('user_id', 'follows_user_id')
759 , {'extend_existing':True})
758 , {'extend_existing':True})
760
759
761 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
760 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
762 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
761 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
763 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
762 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
764 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
763 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
765 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
764 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
766
765
767 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
766 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
768
767
769 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
768 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
770 follows_repository = relationship('Repository', order_by='Repository.repo_name')
769 follows_repository = relationship('Repository', order_by='Repository.repo_name')
771
770
772
771
773 @classmethod
772 @classmethod
774 def get_repo_followers(cls, repo_id):
773 def get_repo_followers(cls, repo_id):
775 return Session.query(cls).filter(cls.follows_repo_id == repo_id)
774 return Session.query(cls).filter(cls.follows_repo_id == repo_id)
776
775
777 class CacheInvalidation(Base, BaseModel):
776 class CacheInvalidation(Base, BaseModel):
778 __tablename__ = 'cache_invalidation'
777 __tablename__ = 'cache_invalidation'
779 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True})
778 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True})
780 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
779 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
781 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
780 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
782 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
781 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
783 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
782 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
784
783
785
784
786 def __init__(self, cache_key, cache_args=''):
785 def __init__(self, cache_key, cache_args=''):
787 self.cache_key = cache_key
786 self.cache_key = cache_key
788 self.cache_args = cache_args
787 self.cache_args = cache_args
789 self.cache_active = False
788 self.cache_active = False
790
789
791 def __repr__(self):
790 def __repr__(self):
792 return "<%s('%s:%s')>" % (self.__class__.__name__,
791 return "<%s('%s:%s')>" % (self.__class__.__name__,
793 self.cache_id, self.cache_key)
792 self.cache_id, self.cache_key)
794
793
795 class DbMigrateVersion(Base, BaseModel):
794 class DbMigrateVersion(Base, BaseModel):
796 __tablename__ = 'db_migrate_version'
795 __tablename__ = 'db_migrate_version'
797 __table_args__ = {'extend_existing':True}
796 __table_args__ = {'extend_existing':True}
798 repository_id = Column('repository_id', String(250), primary_key=True)
797 repository_id = Column('repository_id', String(250), primary_key=True)
799 repository_path = Column('repository_path', Text)
798 repository_path = Column('repository_path', Text)
800 version = Column('version', Integer)
799 version = Column('version', Integer)
@@ -1,413 +1,374 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 os
26 import time
26 import time
27 import traceback
27 import traceback
28 import logging
28 import logging
29
29
30 from sqlalchemy.exc import DatabaseError
30 from sqlalchemy.exc import DatabaseError
31
31
32 from vcs import get_backend
32 from vcs import get_backend
33 from vcs.utils.helpers import get_scm
33 from vcs.utils.helpers import get_scm
34 from vcs.exceptions import RepositoryError, VCSError
34 from vcs.exceptions import RepositoryError, VCSError
35 from vcs.utils.lazy import LazyProperty
35 from vcs.utils.lazy import LazyProperty
36 from vcs.nodes import FileNode
36 from vcs.nodes import FileNode
37
37
38 from rhodecode import BACKENDS
38 from rhodecode import BACKENDS
39 from rhodecode.lib import helpers as h
39 from rhodecode.lib import helpers as h
40 from rhodecode.lib import safe_str
40 from rhodecode.lib import safe_str
41 from rhodecode.lib.auth import HasRepoPermissionAny
41 from rhodecode.lib.auth import HasRepoPermissionAny
42 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
42 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
43 action_logger
43 action_logger
44 from rhodecode.model import BaseModel
44 from rhodecode.model import BaseModel
45 from rhodecode.model.user import UserModel
45 from rhodecode.model.user import UserModel
46 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
46 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
47 UserFollowing, UserLog
47 UserFollowing, UserLog
48
48
49 log = logging.getLogger(__name__)
49 log = logging.getLogger(__name__)
50
50
51
51
52 class UserTemp(object):
52 class UserTemp(object):
53 def __init__(self, user_id):
53 def __init__(self, user_id):
54 self.user_id = user_id
54 self.user_id = user_id
55
55
56 def __repr__(self):
56 def __repr__(self):
57 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
57 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
58
58
59
59
60 class RepoTemp(object):
60 class RepoTemp(object):
61 def __init__(self, repo_id):
61 def __init__(self, repo_id):
62 self.repo_id = repo_id
62 self.repo_id = repo_id
63
63
64 def __repr__(self):
64 def __repr__(self):
65 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
65 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
66
66
67 class CachedRepoList(object):
67 class CachedRepoList(object):
68
68
69 def __init__(self, db_repo_list, invalidation_list, repos_path,
69 def __init__(self, db_repo_list, repos_path, order_by=None):
70 order_by=None):
71 self.db_repo_list = db_repo_list
70 self.db_repo_list = db_repo_list
72 self.invalidation_list = invalidation_list
73 self.repos_path = repos_path
71 self.repos_path = repos_path
74 self.order_by = order_by
72 self.order_by = order_by
75 self.reversed = (order_by or '').startswith('-')
73 self.reversed = (order_by or '').startswith('-')
76
74
77 def __len__(self):
75 def __len__(self):
78 return len(self.db_repo_list)
76 return len(self.db_repo_list)
79
77
80 def __repr__(self):
78 def __repr__(self):
81 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
79 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
82
80
83 def __iter__(self):
81 def __iter__(self):
84 for db_repo in self.db_repo_list:
82 for dbr in self.db_repo_list:
85 dbr = db_repo
86
87 # invalidate the repo cache if needed before getting the
88 # scm instance
89
83
90 scm_invalidate = False
84 scmr = dbr.scm_instance_cached
91 if self.invalidation_list is not None:
92 scm_invalidate = dbr.repo_name in self.invalidation_list
93
94 if scm_invalidate:
95 log.info('invalidating cache for repository %s',
96 dbr.repo_name)
97 db_repo.set_invalidate
98
99 scmr = db_repo.scm_instance_cached
100
85
101 #check permission at this level
86 #check permission at this level
102 if not HasRepoPermissionAny('repository.read',
87 if not HasRepoPermissionAny('repository.read',
103 'repository.write',
88 'repository.write',
104 'repository.admin')(dbr.repo_name,
89 'repository.admin')(dbr.repo_name,
105 'get repo check'):
90 'get repo check'):
106 continue
91 continue
107
92
108
93
109 if scmr is None:
94 if scmr is None:
110 log.error('%s this repository is present in database but it '
95 log.error('%s this repository is present in database but it '
111 'cannot be created as an scm instance',
96 'cannot be created as an scm instance',
112 dbr.repo_name)
97 dbr.repo_name)
113 continue
98 continue
114
99
115
100
116 last_change = scmr.last_change
101 last_change = scmr.last_change
117 tip = h.get_changeset_safe(scmr, 'tip')
102 tip = h.get_changeset_safe(scmr, 'tip')
118
103
119 tmp_d = {}
104 tmp_d = {}
120 tmp_d['name'] = dbr.repo_name
105 tmp_d['name'] = dbr.repo_name
121 tmp_d['name_sort'] = tmp_d['name'].lower()
106 tmp_d['name_sort'] = tmp_d['name'].lower()
122 tmp_d['description'] = dbr.description
107 tmp_d['description'] = dbr.description
123 tmp_d['description_sort'] = tmp_d['description']
108 tmp_d['description_sort'] = tmp_d['description']
124 tmp_d['last_change'] = last_change
109 tmp_d['last_change'] = last_change
125 tmp_d['last_change_sort'] = time.mktime(last_change \
110 tmp_d['last_change_sort'] = time.mktime(last_change \
126 .timetuple())
111 .timetuple())
127 tmp_d['tip'] = tip.raw_id
112 tmp_d['tip'] = tip.raw_id
128 tmp_d['tip_sort'] = tip.revision
113 tmp_d['tip_sort'] = tip.revision
129 tmp_d['rev'] = tip.revision
114 tmp_d['rev'] = tip.revision
130 tmp_d['contact'] = dbr.user.full_contact
115 tmp_d['contact'] = dbr.user.full_contact
131 tmp_d['contact_sort'] = tmp_d['contact']
116 tmp_d['contact_sort'] = tmp_d['contact']
132 tmp_d['owner_sort'] = tmp_d['contact']
117 tmp_d['owner_sort'] = tmp_d['contact']
133 tmp_d['repo_archives'] = list(scmr._get_archives())
118 tmp_d['repo_archives'] = list(scmr._get_archives())
134 tmp_d['last_msg'] = tip.message
119 tmp_d['last_msg'] = tip.message
135 tmp_d['repo'] = scmr
120 tmp_d['repo'] = scmr
136 tmp_d['dbrepo'] = dbr.get_dict()
121 tmp_d['dbrepo'] = dbr.get_dict()
137 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork \
122 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork \
138 else {}
123 else {}
139 yield tmp_d
124 yield tmp_d
140
125
141 class ScmModel(BaseModel):
126 class ScmModel(BaseModel):
142 """Generic Scm Model
127 """Generic Scm Model
143 """
128 """
144
129
145 @LazyProperty
130 @LazyProperty
146 def repos_path(self):
131 def repos_path(self):
147 """Get's the repositories root path from database
132 """Get's the repositories root path from database
148 """
133 """
149
134
150 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
135 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
151
136
152 return q.ui_value
137 return q.ui_value
153
138
154 def repo_scan(self, repos_path=None):
139 def repo_scan(self, repos_path=None):
155 """Listing of repositories in given path. This path should not be a
140 """Listing of repositories in given path. This path should not be a
156 repository itself. Return a dictionary of repository objects
141 repository itself. Return a dictionary of repository objects
157
142
158 :param repos_path: path to directory containing repositories
143 :param repos_path: path to directory containing repositories
159 """
144 """
160
145
161 log.info('scanning for repositories in %s', repos_path)
146 log.info('scanning for repositories in %s', repos_path)
162
147
163 if repos_path is None:
148 if repos_path is None:
164 repos_path = self.repos_path
149 repos_path = self.repos_path
165
150
166 baseui = make_ui('db')
151 baseui = make_ui('db')
167 repos_list = {}
152 repos_list = {}
168
153
169 for name, path in get_filesystem_repos(repos_path, recursive=True):
154 for name, path in get_filesystem_repos(repos_path, recursive=True):
170 try:
155 try:
171 if name in repos_list:
156 if name in repos_list:
172 raise RepositoryError('Duplicate repository name %s '
157 raise RepositoryError('Duplicate repository name %s '
173 'found in %s' % (name, path))
158 'found in %s' % (name, path))
174 else:
159 else:
175
160
176 klass = get_backend(path[0])
161 klass = get_backend(path[0])
177
162
178 if path[0] == 'hg' and path[0] in BACKENDS.keys():
163 if path[0] == 'hg' and path[0] in BACKENDS.keys():
179
164
180 # for mercurial we need to have an str path
165 # for mercurial we need to have an str path
181 repos_list[name] = klass(safe_str(path[1]),
166 repos_list[name] = klass(safe_str(path[1]),
182 baseui=baseui)
167 baseui=baseui)
183
168
184 if path[0] == 'git' and path[0] in BACKENDS.keys():
169 if path[0] == 'git' and path[0] in BACKENDS.keys():
185 repos_list[name] = klass(path[1])
170 repos_list[name] = klass(path[1])
186 except OSError:
171 except OSError:
187 continue
172 continue
188
173
189 return repos_list
174 return repos_list
190
175
191 def get_repos(self, all_repos=None, sort_key=None):
176 def get_repos(self, all_repos=None, sort_key=None):
192 """
177 """
193 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
194 backend instance and fill that backed with information from database
179 backend instance and fill that backed with information from database
195
180
196 :param all_repos: list of repository names as strings
181 :param all_repos: list of repository names as strings
197 give specific repositories list, good for filtering
182 give specific repositories list, good for filtering
198 """
183 """
199 if all_repos is None:
184 if all_repos is None:
200 all_repos = self.sa.query(Repository)\
185 all_repos = self.sa.query(Repository)\
201 .filter(Repository.group_id == None)\
186 .filter(Repository.group_id == None)\
202 .order_by(Repository.repo_name).all()
187 .order_by(Repository.repo_name).all()
203
188
204 #get the repositories that should be invalidated
189 repo_iter = CachedRepoList(all_repos, repos_path=self.repos_path,
205 invalidation_list = [str(x.cache_key) for x in \
206 self.sa.query(CacheInvalidation.cache_key)\
207 .filter(CacheInvalidation.cache_active == False)\
208 .all()]
209
210 repo_iter = CachedRepoList(all_repos, invalidation_list,
211 repos_path=self.repos_path,
212 order_by=sort_key)
190 order_by=sort_key)
213
191
214 return repo_iter
192 return repo_iter
215
193
216 def mark_for_invalidation(self, repo_name):
194 def mark_for_invalidation(self, repo_name):
217 """Puts cache invalidation task into db for
195 """Puts cache invalidation task into db for
218 further global cache invalidation
196 further global cache invalidation
219
197
220 :param repo_name: this repo that should invalidation take place
198 :param repo_name: this repo that should invalidation take place
221 """
199 """
222
200
223 log.debug('marking %s for invalidation', repo_name)
201 log.debug('marking %s for invalidation', repo_name)
224 cache = self.sa.query(CacheInvalidation)\
202 cache = self.sa.query(CacheInvalidation)\
225 .filter(CacheInvalidation.cache_key == repo_name).scalar()
203 .filter(CacheInvalidation.cache_key == repo_name).scalar()
226
204
227 if cache:
205 if cache:
228 #mark this cache as inactive
206 # mark this cache as inactive
229 cache.cache_active = False
207 cache.cache_active = False
230 else:
208 else:
231 log.debug('cache key not found in invalidation db -> creating one')
209 log.debug('cache key not found in invalidation db -> creating one')
232 cache = CacheInvalidation(repo_name)
210 cache = CacheInvalidation(repo_name)
233
211
234 try:
212 try:
235 self.sa.add(cache)
213 self.sa.add(cache)
236 self.sa.commit()
214 self.sa.commit()
237 except (DatabaseError,):
215 except (DatabaseError,):
238 log.error(traceback.format_exc())
216 log.error(traceback.format_exc())
239 self.sa.rollback()
217 self.sa.rollback()
240
218
241 def toggle_following_repo(self, follow_repo_id, user_id):
219 def toggle_following_repo(self, follow_repo_id, user_id):
242
220
243 f = self.sa.query(UserFollowing)\
221 f = self.sa.query(UserFollowing)\
244 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
222 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
245 .filter(UserFollowing.user_id == user_id).scalar()
223 .filter(UserFollowing.user_id == user_id).scalar()
246
224
247 if f is not None:
225 if f is not None:
248
226
249 try:
227 try:
250 self.sa.delete(f)
228 self.sa.delete(f)
251 self.sa.commit()
229 self.sa.commit()
252 action_logger(UserTemp(user_id),
230 action_logger(UserTemp(user_id),
253 'stopped_following_repo',
231 'stopped_following_repo',
254 RepoTemp(follow_repo_id))
232 RepoTemp(follow_repo_id))
255 return
233 return
256 except:
234 except:
257 log.error(traceback.format_exc())
235 log.error(traceback.format_exc())
258 self.sa.rollback()
236 self.sa.rollback()
259 raise
237 raise
260
238
261 try:
239 try:
262 f = UserFollowing()
240 f = UserFollowing()
263 f.user_id = user_id
241 f.user_id = user_id
264 f.follows_repo_id = follow_repo_id
242 f.follows_repo_id = follow_repo_id
265 self.sa.add(f)
243 self.sa.add(f)
266 self.sa.commit()
244 self.sa.commit()
267 action_logger(UserTemp(user_id),
245 action_logger(UserTemp(user_id),
268 'started_following_repo',
246 'started_following_repo',
269 RepoTemp(follow_repo_id))
247 RepoTemp(follow_repo_id))
270 except:
248 except:
271 log.error(traceback.format_exc())
249 log.error(traceback.format_exc())
272 self.sa.rollback()
250 self.sa.rollback()
273 raise
251 raise
274
252
275 def toggle_following_user(self, follow_user_id, user_id):
253 def toggle_following_user(self, follow_user_id, user_id):
276 f = self.sa.query(UserFollowing)\
254 f = self.sa.query(UserFollowing)\
277 .filter(UserFollowing.follows_user_id == follow_user_id)\
255 .filter(UserFollowing.follows_user_id == follow_user_id)\
278 .filter(UserFollowing.user_id == user_id).scalar()
256 .filter(UserFollowing.user_id == user_id).scalar()
279
257
280 if f is not None:
258 if f is not None:
281 try:
259 try:
282 self.sa.delete(f)
260 self.sa.delete(f)
283 self.sa.commit()
261 self.sa.commit()
284 return
262 return
285 except:
263 except:
286 log.error(traceback.format_exc())
264 log.error(traceback.format_exc())
287 self.sa.rollback()
265 self.sa.rollback()
288 raise
266 raise
289
267
290 try:
268 try:
291 f = UserFollowing()
269 f = UserFollowing()
292 f.user_id = user_id
270 f.user_id = user_id
293 f.follows_user_id = follow_user_id
271 f.follows_user_id = follow_user_id
294 self.sa.add(f)
272 self.sa.add(f)
295 self.sa.commit()
273 self.sa.commit()
296 except:
274 except:
297 log.error(traceback.format_exc())
275 log.error(traceback.format_exc())
298 self.sa.rollback()
276 self.sa.rollback()
299 raise
277 raise
300
278
301 def is_following_repo(self, repo_name, user_id, cache=False):
279 def is_following_repo(self, repo_name, user_id, cache=False):
302 r = self.sa.query(Repository)\
280 r = self.sa.query(Repository)\
303 .filter(Repository.repo_name == repo_name).scalar()
281 .filter(Repository.repo_name == repo_name).scalar()
304
282
305 f = self.sa.query(UserFollowing)\
283 f = self.sa.query(UserFollowing)\
306 .filter(UserFollowing.follows_repository == r)\
284 .filter(UserFollowing.follows_repository == r)\
307 .filter(UserFollowing.user_id == user_id).scalar()
285 .filter(UserFollowing.user_id == user_id).scalar()
308
286
309 return f is not None
287 return f is not None
310
288
311 def is_following_user(self, username, user_id, cache=False):
289 def is_following_user(self, username, user_id, cache=False):
312 u = UserModel(self.sa).get_by_username(username)
290 u = UserModel(self.sa).get_by_username(username)
313
291
314 f = self.sa.query(UserFollowing)\
292 f = self.sa.query(UserFollowing)\
315 .filter(UserFollowing.follows_user == u)\
293 .filter(UserFollowing.follows_user == u)\
316 .filter(UserFollowing.user_id == user_id).scalar()
294 .filter(UserFollowing.user_id == user_id).scalar()
317
295
318 return f is not None
296 return f is not None
319
297
320 def get_followers(self, repo_id):
298 def get_followers(self, repo_id):
321 if not isinstance(repo_id, int):
299 if not isinstance(repo_id, int):
322 repo_id = getattr(Repository.by_repo_name(repo_id), 'repo_id')
300 repo_id = getattr(Repository.by_repo_name(repo_id), 'repo_id')
323
301
324 return self.sa.query(UserFollowing)\
302 return self.sa.query(UserFollowing)\
325 .filter(UserFollowing.follows_repo_id == repo_id).count()
303 .filter(UserFollowing.follows_repo_id == repo_id).count()
326
304
327 def get_forks(self, repo_id):
305 def get_forks(self, repo_id):
328 if not isinstance(repo_id, int):
306 if not isinstance(repo_id, int):
329 repo_id = getattr(Repository.by_repo_name(repo_id), 'repo_id')
307 repo_id = getattr(Repository.by_repo_name(repo_id), 'repo_id')
330
308
331 return self.sa.query(Repository)\
309 return self.sa.query(Repository)\
332 .filter(Repository.fork_id == repo_id).count()
310 .filter(Repository.fork_id == repo_id).count()
333
311
334 def pull_changes(self, repo_name, username):
312 def pull_changes(self, repo_name, username):
335 dbrepo = Repository.by_repo_name(repo_name)
313 dbrepo = Repository.by_repo_name(repo_name)
336 repo = dbrepo.scm_instance
314 repo = dbrepo.scm_instance
337 try:
315 try:
338 extras = {'ip': '',
316 extras = {'ip': '',
339 'username': username,
317 'username': username,
340 'action': 'push_remote',
318 'action': 'push_remote',
341 'repository': repo_name}
319 'repository': repo_name}
342
320
343 #inject ui extra param to log this action via push logger
321 #inject ui extra param to log this action via push logger
344 for k, v in extras.items():
322 for k, v in extras.items():
345 repo._repo.ui.setconfig('rhodecode_extras', k, v)
323 repo._repo.ui.setconfig('rhodecode_extras', k, v)
346
324
347 repo.pull(dbrepo.clone_uri)
325 repo.pull(dbrepo.clone_uri)
348 self.mark_for_invalidation(repo_name)
326 self.mark_for_invalidation(repo_name)
349 except:
327 except:
350 log.error(traceback.format_exc())
328 log.error(traceback.format_exc())
351 raise
329 raise
352
330
353
331
354 def commit_change(self, repo, repo_name, cs, user, author, message, content,
332 def commit_change(self, repo, repo_name, cs, user, author, message, content,
355 f_path):
333 f_path):
356
334
357 if repo.alias == 'hg':
335 if repo.alias == 'hg':
358 from vcs.backends.hg import MercurialInMemoryChangeset as IMC
336 from vcs.backends.hg import MercurialInMemoryChangeset as IMC
359 elif repo.alias == 'git':
337 elif repo.alias == 'git':
360 from vcs.backends.git import GitInMemoryChangeset as IMC
338 from vcs.backends.git import GitInMemoryChangeset as IMC
361
339
362 # decoding here will force that we have proper encoded values
340 # decoding here will force that we have proper encoded values
363 # in any other case this will throw exceptions and deny commit
341 # in any other case this will throw exceptions and deny commit
364 content = safe_str(content)
342 content = safe_str(content)
365 message = safe_str(message)
343 message = safe_str(message)
366 path = safe_str(f_path)
344 path = safe_str(f_path)
367 author = safe_str(author)
345 author = safe_str(author)
368 m = IMC(repo)
346 m = IMC(repo)
369 m.change(FileNode(path, content))
347 m.change(FileNode(path, content))
370 tip = m.commit(message=message,
348 tip = m.commit(message=message,
371 author=author,
349 author=author,
372 parents=[cs], branch=cs.branch)
350 parents=[cs], branch=cs.branch)
373
351
374 new_cs = tip.short_id
352 new_cs = tip.short_id
375 action = 'push_local:%s' % new_cs
353 action = 'push_local:%s' % new_cs
376
354
377 action_logger(user, action, repo_name)
355 action_logger(user, action, repo_name)
378
356
379 self.mark_for_invalidation(repo_name)
357 self.mark_for_invalidation(repo_name)
380
358
381
359
382 def get_unread_journal(self):
360 def get_unread_journal(self):
383 return self.sa.query(UserLog).count()
361 return self.sa.query(UserLog).count()
384
362
385 def _should_invalidate(self, repo_name):
363 def _should_invalidate(self, repo_name):
386 """Looks up database for invalidation signals for this repo_name
364 """Looks up database for invalidation signals for this repo_name
387
365
388 :param repo_name:
366 :param repo_name:
389 """
367 """
390
368
391 ret = self.sa.query(CacheInvalidation)\
369 ret = self.sa.query(CacheInvalidation)\
392 .filter(CacheInvalidation.cache_key == repo_name)\
370 .filter(CacheInvalidation.cache_key == repo_name)\
393 .filter(CacheInvalidation.cache_active == False)\
371 .filter(CacheInvalidation.cache_active == False)\
394 .scalar()
372 .scalar()
395
373
396 return ret
374 return ret
397
398 def _mark_invalidated(self, cache_key):
399 """ Marks all occurrences of cache to invalidation as already
400 invalidated
401
402 :param cache_key:
403 """
404
405 if cache_key:
406 log.debug('marking %s as already invalidated', cache_key)
407 try:
408 cache_key.cache_active = True
409 self.sa.add(cache_key)
410 self.sa.commit()
411 except (DatabaseError,):
412 log.error(traceback.format_exc())
413 self.sa.rollback()
General Comments 0
You need to be logged in to leave comments. Login now