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