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