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