##// END OF EJS Templates
Add default for dashbord items and create default...
marcink -
r3909:e87298cc beta
parent child Browse files
Show More
@@ -1,735 +1,711 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.db_manage
3 rhodecode.lib.db_manage
4 ~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Database creation, and setup module for RhodeCode. Used for creation
6 Database creation, and setup module for RhodeCode. Used for creation
7 of database as well as for migration operations
7 of database as well as for migration operations
8
8
9 :created_on: Apr 10, 2010
9 :created_on: Apr 10, 2010
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software: you can redistribute it and/or modify
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
17 # (at your option) any later version.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26
26
27 import os
27 import os
28 import sys
28 import sys
29 import time
29 import time
30 import uuid
30 import uuid
31 import logging
31 import logging
32 from os.path import dirname as dn, join as jn
32 from os.path import dirname as dn, join as jn
33
33
34 from rhodecode import __dbversion__, __py_version__
34 from rhodecode import __dbversion__, __py_version__
35
35
36 from rhodecode.model.user import UserModel
36 from rhodecode.model.user import UserModel
37 from rhodecode.lib.utils import ask_ok
37 from rhodecode.lib.utils import ask_ok
38 from rhodecode.model import init_model
38 from rhodecode.model import init_model
39 from rhodecode.model.db import User, Permission, RhodeCodeUi, \
39 from rhodecode.model.db import User, Permission, RhodeCodeUi, \
40 RhodeCodeSetting, UserToPerm, DbMigrateVersion, RepoGroup, \
40 RhodeCodeSetting, UserToPerm, DbMigrateVersion, RepoGroup, \
41 UserRepoGroupToPerm, CacheInvalidation, UserGroup
41 UserRepoGroupToPerm, CacheInvalidation, UserGroup
42
42
43 from sqlalchemy.engine import create_engine
43 from sqlalchemy.engine import create_engine
44 from rhodecode.model.repos_group import ReposGroupModel
44 from rhodecode.model.repos_group import ReposGroupModel
45 #from rhodecode.model import meta
45 #from rhodecode.model import meta
46 from rhodecode.model.meta import Session, Base
46 from rhodecode.model.meta import Session, Base
47 from rhodecode.model.repo import RepoModel
47 from rhodecode.model.repo import RepoModel
48 from rhodecode.model.permission import PermissionModel
48 from rhodecode.model.permission import PermissionModel
49 from rhodecode.model.users_group import UserGroupModel
49 from rhodecode.model.users_group import UserGroupModel
50
50
51
51
52 log = logging.getLogger(__name__)
52 log = logging.getLogger(__name__)
53
53
54
54
55 def notify(msg):
55 def notify(msg):
56 """
56 """
57 Notification for migrations messages
57 Notification for migrations messages
58 """
58 """
59 ml = len(msg) + (4 * 2)
59 ml = len(msg) + (4 * 2)
60 print('\n%s\n*** %s ***\n%s' % ('*' * ml, msg, '*' * ml)).upper()
60 print('\n%s\n*** %s ***\n%s' % ('*' * ml, msg, '*' * ml)).upper()
61
61
62
62
63 class UpgradeSteps(object):
63 class UpgradeSteps(object):
64 """
64 """
65 Those steps follow schema versions so for example schema
65 Those steps follow schema versions so for example schema
66 for example schema with seq 002 == step_2 and so on.
66 for example schema with seq 002 == step_2 and so on.
67 """
67 """
68
68
69 def __init__(self, klass):
69 def __init__(self, klass):
70 self.klass = klass
70 self.klass = klass
71
71
72 def step_1(self):
72 def step_1(self):
73 pass
73 pass
74
74
75 def step_2(self):
75 def step_2(self):
76 notify('Patching repo paths for newer version of RhodeCode')
76 notify('Patching repo paths for newer version of RhodeCode')
77 self.klass.fix_repo_paths()
77 self.klass.fix_repo_paths()
78
78
79 notify('Patching default user of RhodeCode')
79 notify('Patching default user of RhodeCode')
80 self.klass.fix_default_user()
80 self.klass.fix_default_user()
81
81
82 log.info('Changing ui settings')
82 log.info('Changing ui settings')
83 self.klass.create_ui_settings()
83 self.klass.create_ui_settings()
84
84
85 def step_3(self):
85 def step_3(self):
86 notify('Adding additional settings into RhodeCode db')
86 notify('Adding additional settings into RhodeCode db')
87 self.klass.fix_settings()
87 self.klass.fix_settings()
88 notify('Adding ldap defaults')
88 notify('Adding ldap defaults')
89 self.klass.create_ldap_options(skip_existing=True)
89 self.klass.create_ldap_options(skip_existing=True)
90
90
91 def step_4(self):
91 def step_4(self):
92 notify('create permissions and fix groups')
92 notify('create permissions and fix groups')
93 self.klass.create_permissions()
93 self.klass.create_permissions()
94 self.klass.fixup_groups()
94 self.klass.fixup_groups()
95
95
96 def step_5(self):
96 def step_5(self):
97 pass
97 pass
98
98
99 def step_6(self):
99 def step_6(self):
100
100
101 notify('re-checking permissions')
101 notify('re-checking permissions')
102 self.klass.create_permissions()
102 self.klass.create_permissions()
103
103
104 notify('installing new UI options')
104 notify('installing new UI options')
105 sett4 = RhodeCodeSetting('show_public_icon', True)
105 sett4 = RhodeCodeSetting('show_public_icon', True)
106 Session().add(sett4)
106 Session().add(sett4)
107 sett5 = RhodeCodeSetting('show_private_icon', True)
107 sett5 = RhodeCodeSetting('show_private_icon', True)
108 Session().add(sett5)
108 Session().add(sett5)
109 sett6 = RhodeCodeSetting('stylify_metatags', False)
109 sett6 = RhodeCodeSetting('stylify_metatags', False)
110 Session().add(sett6)
110 Session().add(sett6)
111
111
112 notify('fixing old PULL hook')
112 notify('fixing old PULL hook')
113 _pull = RhodeCodeUi.get_by_key('preoutgoing.pull_logger')
113 _pull = RhodeCodeUi.get_by_key('preoutgoing.pull_logger')
114 if _pull:
114 if _pull:
115 _pull.ui_key = RhodeCodeUi.HOOK_PULL
115 _pull.ui_key = RhodeCodeUi.HOOK_PULL
116 Session().add(_pull)
116 Session().add(_pull)
117
117
118 notify('fixing old PUSH hook')
118 notify('fixing old PUSH hook')
119 _push = RhodeCodeUi.get_by_key('pretxnchangegroup.push_logger')
119 _push = RhodeCodeUi.get_by_key('pretxnchangegroup.push_logger')
120 if _push:
120 if _push:
121 _push.ui_key = RhodeCodeUi.HOOK_PUSH
121 _push.ui_key = RhodeCodeUi.HOOK_PUSH
122 Session().add(_push)
122 Session().add(_push)
123
123
124 notify('installing new pre-push hook')
124 notify('installing new pre-push hook')
125 hooks4 = RhodeCodeUi()
125 hooks4 = RhodeCodeUi()
126 hooks4.ui_section = 'hooks'
126 hooks4.ui_section = 'hooks'
127 hooks4.ui_key = RhodeCodeUi.HOOK_PRE_PUSH
127 hooks4.ui_key = RhodeCodeUi.HOOK_PRE_PUSH
128 hooks4.ui_value = 'python:rhodecode.lib.hooks.pre_push'
128 hooks4.ui_value = 'python:rhodecode.lib.hooks.pre_push'
129 Session().add(hooks4)
129 Session().add(hooks4)
130
130
131 notify('installing new pre-pull hook')
131 notify('installing new pre-pull hook')
132 hooks6 = RhodeCodeUi()
132 hooks6 = RhodeCodeUi()
133 hooks6.ui_section = 'hooks'
133 hooks6.ui_section = 'hooks'
134 hooks6.ui_key = RhodeCodeUi.HOOK_PRE_PULL
134 hooks6.ui_key = RhodeCodeUi.HOOK_PRE_PULL
135 hooks6.ui_value = 'python:rhodecode.lib.hooks.pre_pull'
135 hooks6.ui_value = 'python:rhodecode.lib.hooks.pre_pull'
136 Session().add(hooks6)
136 Session().add(hooks6)
137
137
138 notify('installing hgsubversion option')
138 notify('installing hgsubversion option')
139 # enable hgsubversion disabled by default
139 # enable hgsubversion disabled by default
140 hgsubversion = RhodeCodeUi()
140 hgsubversion = RhodeCodeUi()
141 hgsubversion.ui_section = 'extensions'
141 hgsubversion.ui_section = 'extensions'
142 hgsubversion.ui_key = 'hgsubversion'
142 hgsubversion.ui_key = 'hgsubversion'
143 hgsubversion.ui_value = ''
143 hgsubversion.ui_value = ''
144 hgsubversion.ui_active = False
144 hgsubversion.ui_active = False
145 Session().add(hgsubversion)
145 Session().add(hgsubversion)
146
146
147 notify('installing hg git option')
147 notify('installing hg git option')
148 # enable hggit disabled by default
148 # enable hggit disabled by default
149 hggit = RhodeCodeUi()
149 hggit = RhodeCodeUi()
150 hggit.ui_section = 'extensions'
150 hggit.ui_section = 'extensions'
151 hggit.ui_key = 'hggit'
151 hggit.ui_key = 'hggit'
152 hggit.ui_value = ''
152 hggit.ui_value = ''
153 hggit.ui_active = False
153 hggit.ui_active = False
154 Session().add(hggit)
154 Session().add(hggit)
155
155
156 notify('re-check default permissions')
156 notify('re-check default permissions')
157 default_user = User.get_by_username(User.DEFAULT_USER)
157 default_user = User.get_by_username(User.DEFAULT_USER)
158 perm = Permission.get_by_key('hg.fork.repository')
158 perm = Permission.get_by_key('hg.fork.repository')
159 reg_perm = UserToPerm()
159 reg_perm = UserToPerm()
160 reg_perm.user = default_user
160 reg_perm.user = default_user
161 reg_perm.permission = perm
161 reg_perm.permission = perm
162 Session().add(reg_perm)
162 Session().add(reg_perm)
163
163
164 def step_7(self):
164 def step_7(self):
165 perm_fixes = self.klass.reset_permissions(User.DEFAULT_USER)
165 perm_fixes = self.klass.reset_permissions(User.DEFAULT_USER)
166 Session().commit()
166 Session().commit()
167 if perm_fixes:
167 if perm_fixes:
168 notify('There was an inconsistent state of permissions '
168 notify('There was an inconsistent state of permissions '
169 'detected for default user. Permissions are now '
169 'detected for default user. Permissions are now '
170 'reset to the default value for default user. '
170 'reset to the default value for default user. '
171 'Please validate and check default permissions '
171 'Please validate and check default permissions '
172 'in admin panel')
172 'in admin panel')
173
173
174 def step_8(self):
174 def step_8(self):
175 self.klass.create_permissions()
175 self.klass.create_permissions()
176 self.klass.populate_default_permissions()
176 self.klass.populate_default_permissions()
177 self.klass.create_default_options(skip_existing=True)
177 self.klass.create_default_options(skip_existing=True)
178 Session().commit()
178 Session().commit()
179
179
180 def step_9(self):
180 def step_9(self):
181 pass
181 pass
182
182
183 def step_10(self):
183 def step_10(self):
184 pass
184 pass
185
185
186 def step_11(self):
186 def step_11(self):
187 self.klass.update_repo_info()
187 self.klass.update_repo_info()
188
188
189 def step_12(self):
189 def step_12(self):
190 self.klass.create_permissions()
190 self.klass.create_permissions()
191 Session().commit()
191 Session().commit()
192
192
193 self.klass.populate_default_permissions()
193 self.klass.populate_default_permissions()
194 Session().commit()
194 Session().commit()
195
195
196 #fix all usergroups
196 #fix all usergroups
197 ug_model = UserGroupModel()
197 ug_model = UserGroupModel()
198 for ug in UserGroup.get_all():
198 for ug in UserGroup.get_all():
199 perm_obj = ug_model._create_default_perms(ug)
199 perm_obj = ug_model._create_default_perms(ug)
200 Session().add(perm_obj)
200 Session().add(perm_obj)
201 Session().commit()
201 Session().commit()
202
202
203 adm = User.get_first_admin()
203 adm = User.get_first_admin()
204 # fix owners of UserGroup
204 # fix owners of UserGroup
205 for ug in Session().query(UserGroup).all():
205 for ug in Session().query(UserGroup).all():
206 ug.user_id = adm.user_id
206 ug.user_id = adm.user_id
207 Session().add(ug)
207 Session().add(ug)
208 Session().commit()
208 Session().commit()
209
209
210 # fix owners of RepoGroup
210 # fix owners of RepoGroup
211 for ug in Session().query(RepoGroup).all():
211 for ug in Session().query(RepoGroup).all():
212 ug.user_id = adm.user_id
212 ug.user_id = adm.user_id
213 Session().add(ug)
213 Session().add(ug)
214 Session().commit()
214 Session().commit()
215
215
216 def step_13(self):
216 def step_13(self):
217 pass
217 pass
218
218
219
219
220 class DbManage(object):
220 class DbManage(object):
221 def __init__(self, log_sql, dbconf, root, tests=False, cli_args={}):
221 def __init__(self, log_sql, dbconf, root, tests=False, cli_args={}):
222 self.dbname = dbconf.split('/')[-1]
222 self.dbname = dbconf.split('/')[-1]
223 self.tests = tests
223 self.tests = tests
224 self.root = root
224 self.root = root
225 self.dburi = dbconf
225 self.dburi = dbconf
226 self.log_sql = log_sql
226 self.log_sql = log_sql
227 self.db_exists = False
227 self.db_exists = False
228 self.cli_args = cli_args
228 self.cli_args = cli_args
229 self.init_db()
229 self.init_db()
230
230
231 force_ask = self.cli_args.get('force_ask')
231 force_ask = self.cli_args.get('force_ask')
232 if force_ask is not None:
232 if force_ask is not None:
233 global ask_ok
233 global ask_ok
234 ask_ok = lambda *args, **kwargs: force_ask
234 ask_ok = lambda *args, **kwargs: force_ask
235
235
236 def init_db(self):
236 def init_db(self):
237 engine = create_engine(self.dburi, echo=self.log_sql)
237 engine = create_engine(self.dburi, echo=self.log_sql)
238 init_model(engine)
238 init_model(engine)
239 self.sa = Session()
239 self.sa = Session()
240
240
241 def create_tables(self, override=False):
241 def create_tables(self, override=False):
242 """
242 """
243 Create a auth database
243 Create a auth database
244 """
244 """
245
245
246 log.info("Any existing database is going to be destroyed")
246 log.info("Any existing database is going to be destroyed")
247 if self.tests:
247 if self.tests:
248 destroy = True
248 destroy = True
249 else:
249 else:
250 destroy = ask_ok('Are you sure to destroy old database ? [y/n]')
250 destroy = ask_ok('Are you sure to destroy old database ? [y/n]')
251 if not destroy:
251 if not destroy:
252 sys.exit('Nothing tables created')
252 sys.exit('Nothing tables created')
253 if destroy:
253 if destroy:
254 Base.metadata.drop_all()
254 Base.metadata.drop_all()
255
255
256 checkfirst = not override
256 checkfirst = not override
257 Base.metadata.create_all(checkfirst=checkfirst)
257 Base.metadata.create_all(checkfirst=checkfirst)
258 log.info('Created tables for %s' % self.dbname)
258 log.info('Created tables for %s' % self.dbname)
259
259
260 def set_db_version(self):
260 def set_db_version(self):
261 ver = DbMigrateVersion()
261 ver = DbMigrateVersion()
262 ver.version = __dbversion__
262 ver.version = __dbversion__
263 ver.repository_id = 'rhodecode_db_migrations'
263 ver.repository_id = 'rhodecode_db_migrations'
264 ver.repository_path = 'versions'
264 ver.repository_path = 'versions'
265 self.sa.add(ver)
265 self.sa.add(ver)
266 log.info('db version set to: %s' % __dbversion__)
266 log.info('db version set to: %s' % __dbversion__)
267
267
268 def upgrade(self):
268 def upgrade(self):
269 """
269 """
270 Upgrades given database schema to given revision following
270 Upgrades given database schema to given revision following
271 all needed steps, to perform the upgrade
271 all needed steps, to perform the upgrade
272
272
273 """
273 """
274
274
275 from rhodecode.lib.dbmigrate.migrate.versioning import api
275 from rhodecode.lib.dbmigrate.migrate.versioning import api
276 from rhodecode.lib.dbmigrate.migrate.exceptions import \
276 from rhodecode.lib.dbmigrate.migrate.exceptions import \
277 DatabaseNotControlledError
277 DatabaseNotControlledError
278
278
279 if 'sqlite' in self.dburi:
279 if 'sqlite' in self.dburi:
280 print (
280 print (
281 '********************** WARNING **********************\n'
281 '********************** WARNING **********************\n'
282 'Make sure your version of sqlite is at least 3.7.X. \n'
282 'Make sure your version of sqlite is at least 3.7.X. \n'
283 'Earlier versions are known to fail on some migrations\n'
283 'Earlier versions are known to fail on some migrations\n'
284 '*****************************************************\n'
284 '*****************************************************\n'
285 )
285 )
286 upgrade = ask_ok('You are about to perform database upgrade, make '
286 upgrade = ask_ok('You are about to perform database upgrade, make '
287 'sure You backed up your database before. '
287 'sure You backed up your database before. '
288 'Continue ? [y/n]')
288 'Continue ? [y/n]')
289 if not upgrade:
289 if not upgrade:
290 sys.exit('No upgrade performed')
290 sys.exit('No upgrade performed')
291
291
292 repository_path = jn(dn(dn(dn(os.path.realpath(__file__)))),
292 repository_path = jn(dn(dn(dn(os.path.realpath(__file__)))),
293 'rhodecode/lib/dbmigrate')
293 'rhodecode/lib/dbmigrate')
294 db_uri = self.dburi
294 db_uri = self.dburi
295
295
296 try:
296 try:
297 curr_version = api.db_version(db_uri, repository_path)
297 curr_version = api.db_version(db_uri, repository_path)
298 msg = ('Found current database under version'
298 msg = ('Found current database under version'
299 ' control with version %s' % curr_version)
299 ' control with version %s' % curr_version)
300
300
301 except (RuntimeError, DatabaseNotControlledError):
301 except (RuntimeError, DatabaseNotControlledError):
302 curr_version = 1
302 curr_version = 1
303 msg = ('Current database is not under version control. Setting'
303 msg = ('Current database is not under version control. Setting'
304 ' as version %s' % curr_version)
304 ' as version %s' % curr_version)
305 api.version_control(db_uri, repository_path, curr_version)
305 api.version_control(db_uri, repository_path, curr_version)
306
306
307 notify(msg)
307 notify(msg)
308
308
309 if curr_version == __dbversion__:
309 if curr_version == __dbversion__:
310 sys.exit('This database is already at the newest version')
310 sys.exit('This database is already at the newest version')
311
311
312 # clear cache keys
312 # clear cache keys
313 log.info("Clearing cache keys now...")
313 log.info("Clearing cache keys now...")
314 CacheInvalidation.clear_cache()
314 CacheInvalidation.clear_cache()
315
315
316 upgrade_steps = range(curr_version + 1, __dbversion__ + 1)
316 upgrade_steps = range(curr_version + 1, __dbversion__ + 1)
317 notify('attempting to do database upgrade from '
317 notify('attempting to do database upgrade from '
318 'version %s to version %s' % (curr_version, __dbversion__))
318 'version %s to version %s' % (curr_version, __dbversion__))
319
319
320 # CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
320 # CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
321 _step = None
321 _step = None
322 for step in upgrade_steps:
322 for step in upgrade_steps:
323 notify('performing upgrade step %s' % step)
323 notify('performing upgrade step %s' % step)
324 time.sleep(2)
324 time.sleep(2)
325
325
326 api.upgrade(db_uri, repository_path, step)
326 api.upgrade(db_uri, repository_path, step)
327 notify('schema upgrade for step %s completed' % (step,))
327 notify('schema upgrade for step %s completed' % (step,))
328
328
329 fixture = 'step_%s' % step
329 fixture = 'step_%s' % step
330 notify('performing fixture step %s' % fixture)
330 notify('performing fixture step %s' % fixture)
331 getattr(UpgradeSteps(self), fixture)()
331 getattr(UpgradeSteps(self), fixture)()
332 self.sa.commit()
332 self.sa.commit()
333 notify('fixture %s completed' % (fixture,))
333 notify('fixture %s completed' % (fixture,))
334 _step = step
334 _step = step
335
335
336 notify('upgrade to version %s successful' % _step)
336 notify('upgrade to version %s successful' % _step)
337
337
338 def fix_repo_paths(self):
338 def fix_repo_paths(self):
339 """
339 """
340 Fixes a old rhodecode version path into new one without a '*'
340 Fixes a old rhodecode version path into new one without a '*'
341 """
341 """
342
342
343 paths = self.sa.query(RhodeCodeUi)\
343 paths = self.sa.query(RhodeCodeUi)\
344 .filter(RhodeCodeUi.ui_key == '/')\
344 .filter(RhodeCodeUi.ui_key == '/')\
345 .scalar()
345 .scalar()
346
346
347 paths.ui_value = paths.ui_value.replace('*', '')
347 paths.ui_value = paths.ui_value.replace('*', '')
348
348
349 try:
349 try:
350 self.sa.add(paths)
350 self.sa.add(paths)
351 self.sa.commit()
351 self.sa.commit()
352 except Exception:
352 except Exception:
353 self.sa.rollback()
353 self.sa.rollback()
354 raise
354 raise
355
355
356 def fix_default_user(self):
356 def fix_default_user(self):
357 """
357 """
358 Fixes a old default user with some 'nicer' default values,
358 Fixes a old default user with some 'nicer' default values,
359 used mostly for anonymous access
359 used mostly for anonymous access
360 """
360 """
361 def_user = self.sa.query(User)\
361 def_user = self.sa.query(User)\
362 .filter(User.username == 'default')\
362 .filter(User.username == 'default')\
363 .one()
363 .one()
364
364
365 def_user.name = 'Anonymous'
365 def_user.name = 'Anonymous'
366 def_user.lastname = 'User'
366 def_user.lastname = 'User'
367 def_user.email = 'anonymous@rhodecode.org'
367 def_user.email = 'anonymous@rhodecode.org'
368
368
369 try:
369 try:
370 self.sa.add(def_user)
370 self.sa.add(def_user)
371 self.sa.commit()
371 self.sa.commit()
372 except Exception:
372 except Exception:
373 self.sa.rollback()
373 self.sa.rollback()
374 raise
374 raise
375
375
376 def fix_settings(self):
376 def fix_settings(self):
377 """
377 """
378 Fixes rhodecode settings adds ga_code key for google analytics
378 Fixes rhodecode settings adds ga_code key for google analytics
379 """
379 """
380
380
381 hgsettings3 = RhodeCodeSetting('ga_code', '')
381 hgsettings3 = RhodeCodeSetting('ga_code', '')
382
382
383 try:
383 try:
384 self.sa.add(hgsettings3)
384 self.sa.add(hgsettings3)
385 self.sa.commit()
385 self.sa.commit()
386 except Exception:
386 except Exception:
387 self.sa.rollback()
387 self.sa.rollback()
388 raise
388 raise
389
389
390 def admin_prompt(self, second=False):
390 def admin_prompt(self, second=False):
391 if not self.tests:
391 if not self.tests:
392 import getpass
392 import getpass
393
393
394 # defaults
394 # defaults
395 defaults = self.cli_args
395 defaults = self.cli_args
396 username = defaults.get('username')
396 username = defaults.get('username')
397 password = defaults.get('password')
397 password = defaults.get('password')
398 email = defaults.get('email')
398 email = defaults.get('email')
399
399
400 def get_password():
400 def get_password():
401 password = getpass.getpass('Specify admin password '
401 password = getpass.getpass('Specify admin password '
402 '(min 6 chars):')
402 '(min 6 chars):')
403 confirm = getpass.getpass('Confirm password:')
403 confirm = getpass.getpass('Confirm password:')
404
404
405 if password != confirm:
405 if password != confirm:
406 log.error('passwords mismatch')
406 log.error('passwords mismatch')
407 return False
407 return False
408 if len(password) < 6:
408 if len(password) < 6:
409 log.error('password is to short use at least 6 characters')
409 log.error('password is to short use at least 6 characters')
410 return False
410 return False
411
411
412 return password
412 return password
413 if username is None:
413 if username is None:
414 username = raw_input('Specify admin username:')
414 username = raw_input('Specify admin username:')
415 if password is None:
415 if password is None:
416 password = get_password()
416 password = get_password()
417 if not password:
417 if not password:
418 #second try
418 #second try
419 password = get_password()
419 password = get_password()
420 if not password:
420 if not password:
421 sys.exit()
421 sys.exit()
422 if email is None:
422 if email is None:
423 email = raw_input('Specify admin email:')
423 email = raw_input('Specify admin email:')
424 self.create_user(username, password, email, True)
424 self.create_user(username, password, email, True)
425 else:
425 else:
426 log.info('creating admin and regular test users')
426 log.info('creating admin and regular test users')
427 from rhodecode.tests import TEST_USER_ADMIN_LOGIN, \
427 from rhodecode.tests import TEST_USER_ADMIN_LOGIN, \
428 TEST_USER_ADMIN_PASS, TEST_USER_ADMIN_EMAIL, \
428 TEST_USER_ADMIN_PASS, TEST_USER_ADMIN_EMAIL, \
429 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, \
429 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, \
430 TEST_USER_REGULAR_EMAIL, TEST_USER_REGULAR2_LOGIN, \
430 TEST_USER_REGULAR_EMAIL, TEST_USER_REGULAR2_LOGIN, \
431 TEST_USER_REGULAR2_PASS, TEST_USER_REGULAR2_EMAIL
431 TEST_USER_REGULAR2_PASS, TEST_USER_REGULAR2_EMAIL
432
432
433 self.create_user(TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS,
433 self.create_user(TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS,
434 TEST_USER_ADMIN_EMAIL, True)
434 TEST_USER_ADMIN_EMAIL, True)
435
435
436 self.create_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,
436 self.create_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,
437 TEST_USER_REGULAR_EMAIL, False)
437 TEST_USER_REGULAR_EMAIL, False)
438
438
439 self.create_user(TEST_USER_REGULAR2_LOGIN, TEST_USER_REGULAR2_PASS,
439 self.create_user(TEST_USER_REGULAR2_LOGIN, TEST_USER_REGULAR2_PASS,
440 TEST_USER_REGULAR2_EMAIL, False)
440 TEST_USER_REGULAR2_EMAIL, False)
441
441
442 def create_ui_settings(self):
442 def create_ui_settings(self):
443 """
443 """
444 Creates ui settings, fills out hooks
444 Creates ui settings, fills out hooks
445 and disables dotencode
445 and disables dotencode
446 """
446 """
447
447
448 #HOOKS
448 #HOOKS
449 hooks1_key = RhodeCodeUi.HOOK_UPDATE
449 hooks1_key = RhodeCodeUi.HOOK_UPDATE
450 hooks1_ = self.sa.query(RhodeCodeUi)\
450 hooks1_ = self.sa.query(RhodeCodeUi)\
451 .filter(RhodeCodeUi.ui_key == hooks1_key).scalar()
451 .filter(RhodeCodeUi.ui_key == hooks1_key).scalar()
452
452
453 hooks1 = RhodeCodeUi() if hooks1_ is None else hooks1_
453 hooks1 = RhodeCodeUi() if hooks1_ is None else hooks1_
454 hooks1.ui_section = 'hooks'
454 hooks1.ui_section = 'hooks'
455 hooks1.ui_key = hooks1_key
455 hooks1.ui_key = hooks1_key
456 hooks1.ui_value = 'hg update >&2'
456 hooks1.ui_value = 'hg update >&2'
457 hooks1.ui_active = False
457 hooks1.ui_active = False
458 self.sa.add(hooks1)
458 self.sa.add(hooks1)
459
459
460 hooks2_key = RhodeCodeUi.HOOK_REPO_SIZE
460 hooks2_key = RhodeCodeUi.HOOK_REPO_SIZE
461 hooks2_ = self.sa.query(RhodeCodeUi)\
461 hooks2_ = self.sa.query(RhodeCodeUi)\
462 .filter(RhodeCodeUi.ui_key == hooks2_key).scalar()
462 .filter(RhodeCodeUi.ui_key == hooks2_key).scalar()
463 hooks2 = RhodeCodeUi() if hooks2_ is None else hooks2_
463 hooks2 = RhodeCodeUi() if hooks2_ is None else hooks2_
464 hooks2.ui_section = 'hooks'
464 hooks2.ui_section = 'hooks'
465 hooks2.ui_key = hooks2_key
465 hooks2.ui_key = hooks2_key
466 hooks2.ui_value = 'python:rhodecode.lib.hooks.repo_size'
466 hooks2.ui_value = 'python:rhodecode.lib.hooks.repo_size'
467 self.sa.add(hooks2)
467 self.sa.add(hooks2)
468
468
469 hooks3 = RhodeCodeUi()
469 hooks3 = RhodeCodeUi()
470 hooks3.ui_section = 'hooks'
470 hooks3.ui_section = 'hooks'
471 hooks3.ui_key = RhodeCodeUi.HOOK_PUSH
471 hooks3.ui_key = RhodeCodeUi.HOOK_PUSH
472 hooks3.ui_value = 'python:rhodecode.lib.hooks.log_push_action'
472 hooks3.ui_value = 'python:rhodecode.lib.hooks.log_push_action'
473 self.sa.add(hooks3)
473 self.sa.add(hooks3)
474
474
475 hooks4 = RhodeCodeUi()
475 hooks4 = RhodeCodeUi()
476 hooks4.ui_section = 'hooks'
476 hooks4.ui_section = 'hooks'
477 hooks4.ui_key = RhodeCodeUi.HOOK_PRE_PUSH
477 hooks4.ui_key = RhodeCodeUi.HOOK_PRE_PUSH
478 hooks4.ui_value = 'python:rhodecode.lib.hooks.pre_push'
478 hooks4.ui_value = 'python:rhodecode.lib.hooks.pre_push'
479 self.sa.add(hooks4)
479 self.sa.add(hooks4)
480
480
481 hooks5 = RhodeCodeUi()
481 hooks5 = RhodeCodeUi()
482 hooks5.ui_section = 'hooks'
482 hooks5.ui_section = 'hooks'
483 hooks5.ui_key = RhodeCodeUi.HOOK_PULL
483 hooks5.ui_key = RhodeCodeUi.HOOK_PULL
484 hooks5.ui_value = 'python:rhodecode.lib.hooks.log_pull_action'
484 hooks5.ui_value = 'python:rhodecode.lib.hooks.log_pull_action'
485 self.sa.add(hooks5)
485 self.sa.add(hooks5)
486
486
487 hooks6 = RhodeCodeUi()
487 hooks6 = RhodeCodeUi()
488 hooks6.ui_section = 'hooks'
488 hooks6.ui_section = 'hooks'
489 hooks6.ui_key = RhodeCodeUi.HOOK_PRE_PULL
489 hooks6.ui_key = RhodeCodeUi.HOOK_PRE_PULL
490 hooks6.ui_value = 'python:rhodecode.lib.hooks.pre_pull'
490 hooks6.ui_value = 'python:rhodecode.lib.hooks.pre_pull'
491 self.sa.add(hooks6)
491 self.sa.add(hooks6)
492
492
493 # enable largefiles
493 # enable largefiles
494 largefiles = RhodeCodeUi()
494 largefiles = RhodeCodeUi()
495 largefiles.ui_section = 'extensions'
495 largefiles.ui_section = 'extensions'
496 largefiles.ui_key = 'largefiles'
496 largefiles.ui_key = 'largefiles'
497 largefiles.ui_value = ''
497 largefiles.ui_value = ''
498 self.sa.add(largefiles)
498 self.sa.add(largefiles)
499
499
500 # enable hgsubversion disabled by default
500 # enable hgsubversion disabled by default
501 hgsubversion = RhodeCodeUi()
501 hgsubversion = RhodeCodeUi()
502 hgsubversion.ui_section = 'extensions'
502 hgsubversion.ui_section = 'extensions'
503 hgsubversion.ui_key = 'hgsubversion'
503 hgsubversion.ui_key = 'hgsubversion'
504 hgsubversion.ui_value = ''
504 hgsubversion.ui_value = ''
505 hgsubversion.ui_active = False
505 hgsubversion.ui_active = False
506 self.sa.add(hgsubversion)
506 self.sa.add(hgsubversion)
507
507
508 # enable hggit disabled by default
508 # enable hggit disabled by default
509 hggit = RhodeCodeUi()
509 hggit = RhodeCodeUi()
510 hggit.ui_section = 'extensions'
510 hggit.ui_section = 'extensions'
511 hggit.ui_key = 'hggit'
511 hggit.ui_key = 'hggit'
512 hggit.ui_value = ''
512 hggit.ui_value = ''
513 hggit.ui_active = False
513 hggit.ui_active = False
514 self.sa.add(hggit)
514 self.sa.add(hggit)
515
515
516 def create_ldap_options(self, skip_existing=False):
516 def create_ldap_options(self, skip_existing=False):
517 """Creates ldap settings"""
517 """Creates ldap settings"""
518
518
519 for k, v in [('ldap_active', 'false'), ('ldap_host', ''),
519 for k, v in [('ldap_active', 'false'), ('ldap_host', ''),
520 ('ldap_port', '389'), ('ldap_tls_kind', 'PLAIN'),
520 ('ldap_port', '389'), ('ldap_tls_kind', 'PLAIN'),
521 ('ldap_tls_reqcert', ''), ('ldap_dn_user', ''),
521 ('ldap_tls_reqcert', ''), ('ldap_dn_user', ''),
522 ('ldap_dn_pass', ''), ('ldap_base_dn', ''),
522 ('ldap_dn_pass', ''), ('ldap_base_dn', ''),
523 ('ldap_filter', ''), ('ldap_search_scope', ''),
523 ('ldap_filter', ''), ('ldap_search_scope', ''),
524 ('ldap_attr_login', ''), ('ldap_attr_firstname', ''),
524 ('ldap_attr_login', ''), ('ldap_attr_firstname', ''),
525 ('ldap_attr_lastname', ''), ('ldap_attr_email', '')]:
525 ('ldap_attr_lastname', ''), ('ldap_attr_email', '')]:
526
526
527 if skip_existing and RhodeCodeSetting.get_by_name(k) is not None:
527 if skip_existing and RhodeCodeSetting.get_by_name(k) is not None:
528 log.debug('Skipping option %s' % k)
528 log.debug('Skipping option %s' % k)
529 continue
529 continue
530 setting = RhodeCodeSetting(k, v)
530 setting = RhodeCodeSetting(k, v)
531 self.sa.add(setting)
531 self.sa.add(setting)
532
532
533 def create_default_options(self, skip_existing=False):
533 def create_default_options(self, skip_existing=False):
534 """Creates default settings"""
534 """Creates default settings"""
535
535
536 for k, v in [
536 for k, v in [
537 ('default_repo_enable_locking', False),
537 ('default_repo_enable_locking', False),
538 ('default_repo_enable_downloads', False),
538 ('default_repo_enable_downloads', False),
539 ('default_repo_enable_statistics', False),
539 ('default_repo_enable_statistics', False),
540 ('default_repo_private', False),
540 ('default_repo_private', False),
541 ('default_repo_type', 'hg')]:
541 ('default_repo_type', 'hg')]:
542
542
543 if skip_existing and RhodeCodeSetting.get_by_name(k) is not None:
543 if skip_existing and RhodeCodeSetting.get_by_name(k) is not None:
544 log.debug('Skipping option %s' % k)
544 log.debug('Skipping option %s' % k)
545 continue
545 continue
546 setting = RhodeCodeSetting(k, v)
546 setting = RhodeCodeSetting(k, v)
547 self.sa.add(setting)
547 self.sa.add(setting)
548
548
549 def fixup_groups(self):
549 def fixup_groups(self):
550 def_usr = User.get_default_user()
550 def_usr = User.get_default_user()
551 for g in RepoGroup.query().all():
551 for g in RepoGroup.query().all():
552 g.group_name = g.get_new_name(g.name)
552 g.group_name = g.get_new_name(g.name)
553 self.sa.add(g)
553 self.sa.add(g)
554 # get default perm
554 # get default perm
555 default = UserRepoGroupToPerm.query()\
555 default = UserRepoGroupToPerm.query()\
556 .filter(UserRepoGroupToPerm.group == g)\
556 .filter(UserRepoGroupToPerm.group == g)\
557 .filter(UserRepoGroupToPerm.user == def_usr)\
557 .filter(UserRepoGroupToPerm.user == def_usr)\
558 .scalar()
558 .scalar()
559
559
560 if default is None:
560 if default is None:
561 log.debug('missing default permission for group %s adding' % g)
561 log.debug('missing default permission for group %s adding' % g)
562 perm_obj = ReposGroupModel()._create_default_perms(g)
562 perm_obj = ReposGroupModel()._create_default_perms(g)
563 self.sa.add(perm_obj)
563 self.sa.add(perm_obj)
564
564
565 def reset_permissions(self, username):
565 def reset_permissions(self, username):
566 """
566 """
567 Resets permissions to default state, usefull when old systems had
567 Resets permissions to default state, usefull when old systems had
568 bad permissions, we must clean them up
568 bad permissions, we must clean them up
569
569
570 :param username:
570 :param username:
571 """
571 """
572 default_user = User.get_by_username(username)
572 default_user = User.get_by_username(username)
573 if not default_user:
573 if not default_user:
574 return
574 return
575
575
576 u2p = UserToPerm.query()\
576 u2p = UserToPerm.query()\
577 .filter(UserToPerm.user == default_user).all()
577 .filter(UserToPerm.user == default_user).all()
578 fixed = False
578 fixed = False
579 if len(u2p) != len(Permission.DEFAULT_USER_PERMISSIONS):
579 if len(u2p) != len(Permission.DEFAULT_USER_PERMISSIONS):
580 for p in u2p:
580 for p in u2p:
581 Session().delete(p)
581 Session().delete(p)
582 fixed = True
582 fixed = True
583 self.populate_default_permissions()
583 self.populate_default_permissions()
584 return fixed
584 return fixed
585
585
586 def update_repo_info(self):
586 def update_repo_info(self):
587 RepoModel.update_repoinfo()
587 RepoModel.update_repoinfo()
588
588
589 def config_prompt(self, test_repo_path='', retries=3):
589 def config_prompt(self, test_repo_path='', retries=3):
590 defaults = self.cli_args
590 defaults = self.cli_args
591 _path = defaults.get('repos_location')
591 _path = defaults.get('repos_location')
592 if retries == 3:
592 if retries == 3:
593 log.info('Setting up repositories config')
593 log.info('Setting up repositories config')
594
594
595 if _path is not None:
595 if _path is not None:
596 path = _path
596 path = _path
597 elif not self.tests and not test_repo_path:
597 elif not self.tests and not test_repo_path:
598 path = raw_input(
598 path = raw_input(
599 'Enter a valid absolute path to store repositories. '
599 'Enter a valid absolute path to store repositories. '
600 'All repositories in that path will be added automatically:'
600 'All repositories in that path will be added automatically:'
601 )
601 )
602 else:
602 else:
603 path = test_repo_path
603 path = test_repo_path
604 path_ok = True
604 path_ok = True
605
605
606 # check proper dir
606 # check proper dir
607 if not os.path.isdir(path):
607 if not os.path.isdir(path):
608 path_ok = False
608 path_ok = False
609 log.error('Given path %s is not a valid directory' % path)
609 log.error('Given path %s is not a valid directory' % path)
610
610
611 elif not os.path.isabs(path):
611 elif not os.path.isabs(path):
612 path_ok = False
612 path_ok = False
613 log.error('Given path %s is not an absolute path' % path)
613 log.error('Given path %s is not an absolute path' % path)
614
614
615 # check write access
615 # check write access
616 elif not os.access(path, os.W_OK) and path_ok:
616 elif not os.access(path, os.W_OK) and path_ok:
617 path_ok = False
617 path_ok = False
618 log.error('No write permission to given path %s' % path)
618 log.error('No write permission to given path %s' % path)
619
619
620 if retries == 0:
620 if retries == 0:
621 sys.exit('max retries reached')
621 sys.exit('max retries reached')
622 if not path_ok:
622 if not path_ok:
623 retries -= 1
623 retries -= 1
624 return self.config_prompt(test_repo_path, retries)
624 return self.config_prompt(test_repo_path, retries)
625
625
626 real_path = os.path.normpath(os.path.realpath(path))
626 real_path = os.path.normpath(os.path.realpath(path))
627
627
628 if real_path != os.path.normpath(path):
628 if real_path != os.path.normpath(path):
629 if not ask_ok(('Path looks like a symlink, Rhodecode will store '
629 if not ask_ok(('Path looks like a symlink, Rhodecode will store '
630 'given path as %s ? [y/n]') % (real_path)):
630 'given path as %s ? [y/n]') % (real_path)):
631 log.error('Canceled by user')
631 log.error('Canceled by user')
632 sys.exit(-1)
632 sys.exit(-1)
633
633
634 return real_path
634 return real_path
635
635
636 def create_settings(self, path):
636 def create_settings(self, path):
637
637
638 self.create_ui_settings()
638 self.create_ui_settings()
639
639
640 #HG UI OPTIONS
640 ui_config = [
641 web1 = RhodeCodeUi()
641 ('web', 'push_ssl', 'false'),
642 web1.ui_section = 'web'
642 ('web', 'allow_archive', 'gz zip bz2'),
643 web1.ui_key = 'push_ssl'
643 ('web', 'allow_push', '*'),
644 web1.ui_value = 'false'
644 ('web', 'baseurl', '/'),
645
645 ('paths', '/', path),
646 web2 = RhodeCodeUi()
646 #('phases', 'publish', 'false')
647 web2.ui_section = 'web'
647 ]
648 web2.ui_key = 'allow_archive'
648 for section,key,value in ui_config:
649 web2.ui_value = 'gz zip bz2'
649 ui_conf = RhodeCodeUi()
650
650 setattr(ui_conf, 'ui_section', section)
651 web3 = RhodeCodeUi()
651 setattr(ui_conf, 'ui_key', key)
652 web3.ui_section = 'web'
652 setattr(ui_conf, 'ui_value', value)
653 web3.ui_key = 'allow_push'
654 web3.ui_value = '*'
655
656 web4 = RhodeCodeUi()
657 web4.ui_section = 'web'
658 web4.ui_key = 'baseurl'
659 web4.ui_value = '/'
660
653
661 paths = RhodeCodeUi()
654 settings = [
662 paths.ui_section = 'paths'
655 ('realm', 'RhodeCode authentication', unicode),
663 paths.ui_key = '/'
656 ('title', 'RhodeCode', unicode),
664 paths.ui_value = path
657 ('ga_code', '', unicode),
665
658 ('show_public_icon', True, bool),
666 phases = RhodeCodeUi()
659 ('show_private_icon', True, bool),
667 phases.ui_section = 'phases'
660 ('stylify_metatags', False, bool),
668 phases.ui_key = 'publish'
661 ('dashboard_items', 100, int),
669 phases.ui_value = False
662 ]
670
663 for key, val, type_ in settings:
671 sett1 = RhodeCodeSetting('realm', 'RhodeCode authentication')
664 sett = RhodeCodeSetting(key, val)
672 sett2 = RhodeCodeSetting('title', 'RhodeCode')
665 self.sa.add(sett)
673 sett3 = RhodeCodeSetting('ga_code', '')
674
675 sett4 = RhodeCodeSetting('show_public_icon', True)
676 sett5 = RhodeCodeSetting('show_private_icon', True)
677 sett6 = RhodeCodeSetting('stylify_metatags', False)
678
679 self.sa.add(web1)
680 self.sa.add(web2)
681 self.sa.add(web3)
682 self.sa.add(web4)
683 self.sa.add(paths)
684 self.sa.add(sett1)
685 self.sa.add(sett2)
686 self.sa.add(sett3)
687 self.sa.add(sett4)
688 self.sa.add(sett5)
689 self.sa.add(sett6)
690
666
691 self.create_ldap_options()
667 self.create_ldap_options()
692 self.create_default_options()
668 self.create_default_options()
693
669
694 log.info('created ui config')
670 log.info('created ui config')
695
671
696 def create_user(self, username, password, email='', admin=False):
672 def create_user(self, username, password, email='', admin=False):
697 log.info('creating user %s' % username)
673 log.info('creating user %s' % username)
698 UserModel().create_or_update(username, password, email,
674 UserModel().create_or_update(username, password, email,
699 firstname='RhodeCode', lastname='Admin',
675 firstname='RhodeCode', lastname='Admin',
700 active=True, admin=admin)
676 active=True, admin=admin)
701
677
702 def create_default_user(self):
678 def create_default_user(self):
703 log.info('creating default user')
679 log.info('creating default user')
704 # create default user for handling default permissions.
680 # create default user for handling default permissions.
705 UserModel().create_or_update(username='default',
681 UserModel().create_or_update(username='default',
706 password=str(uuid.uuid1())[:8],
682 password=str(uuid.uuid1())[:8],
707 email='anonymous@rhodecode.org',
683 email='anonymous@rhodecode.org',
708 firstname='Anonymous', lastname='User')
684 firstname='Anonymous', lastname='User')
709
685
710 def create_permissions(self):
686 def create_permissions(self):
711 """
687 """
712 Creates all permissions defined in the system
688 Creates all permissions defined in the system
713 """
689 """
714 # module.(access|create|change|delete)_[name]
690 # module.(access|create|change|delete)_[name]
715 # module.(none|read|write|admin)
691 # module.(none|read|write|admin)
716 log.info('creating permissions')
692 log.info('creating permissions')
717 PermissionModel(self.sa).create_permissions()
693 PermissionModel(self.sa).create_permissions()
718
694
719 def populate_default_permissions(self):
695 def populate_default_permissions(self):
720 """
696 """
721 Populate default permissions. It will create only the default
697 Populate default permissions. It will create only the default
722 permissions that are missing, and not alter already defined ones
698 permissions that are missing, and not alter already defined ones
723 """
699 """
724 log.info('creating default user permissions')
700 log.info('creating default user permissions')
725 PermissionModel(self.sa).create_default_permissions(user=User.DEFAULT_USER)
701 PermissionModel(self.sa).create_default_permissions(user=User.DEFAULT_USER)
726
702
727 @staticmethod
703 @staticmethod
728 def check_waitress():
704 def check_waitress():
729 """
705 """
730 Function executed at the end of setup
706 Function executed at the end of setup
731 """
707 """
732 if not __py_version__ >= (2, 6):
708 if not __py_version__ >= (2, 6):
733 notify('Python2.5 detected, please switch '
709 notify('Python2.5 detected, please switch '
734 'egg:waitress#main -> egg:Paste#http '
710 'egg:waitress#main -> egg:Paste#http '
735 'in your .ini file')
711 'in your .ini file')
@@ -1,2223 +1,2228 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 time
27 import time
28 import logging
28 import logging
29 import datetime
29 import datetime
30 import traceback
30 import traceback
31 import hashlib
31 import hashlib
32 import collections
32 import collections
33
33
34 from sqlalchemy import *
34 from sqlalchemy import *
35 from sqlalchemy.ext.hybrid import hybrid_property
35 from sqlalchemy.ext.hybrid import hybrid_property
36 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
36 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
37 from sqlalchemy.exc import DatabaseError
37 from sqlalchemy.exc import DatabaseError
38 from beaker.cache import cache_region, region_invalidate
38 from beaker.cache import cache_region, region_invalidate
39 from webob.exc import HTTPNotFound
39 from webob.exc import HTTPNotFound
40
40
41 from pylons.i18n.translation import lazy_ugettext as _
41 from pylons.i18n.translation import lazy_ugettext as _
42
42
43 from rhodecode.lib.vcs import get_backend
43 from rhodecode.lib.vcs import get_backend
44 from rhodecode.lib.vcs.utils.helpers import get_scm
44 from rhodecode.lib.vcs.utils.helpers import get_scm
45 from rhodecode.lib.vcs.exceptions import VCSError
45 from rhodecode.lib.vcs.exceptions import VCSError
46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
47 from rhodecode.lib.vcs.backends.base import EmptyChangeset
47 from rhodecode.lib.vcs.backends.base import EmptyChangeset
48
48
49 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
49 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
50 safe_unicode, remove_suffix, remove_prefix, time_to_datetime, _set_extras
50 safe_unicode, remove_suffix, remove_prefix, time_to_datetime, _set_extras
51 from rhodecode.lib.compat import json
51 from rhodecode.lib.compat import json
52 from rhodecode.lib.caching_query import FromCache
52 from rhodecode.lib.caching_query import FromCache
53
53
54 from rhodecode.model.meta import Base, Session
54 from rhodecode.model.meta import Base, Session
55
55
56 URL_SEP = '/'
56 URL_SEP = '/'
57 log = logging.getLogger(__name__)
57 log = logging.getLogger(__name__)
58
58
59 #==============================================================================
59 #==============================================================================
60 # BASE CLASSES
60 # BASE CLASSES
61 #==============================================================================
61 #==============================================================================
62
62
63 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
63 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
64
64
65
65
66 class BaseModel(object):
66 class BaseModel(object):
67 """
67 """
68 Base Model for all classess
68 Base Model for all classess
69 """
69 """
70
70
71 @classmethod
71 @classmethod
72 def _get_keys(cls):
72 def _get_keys(cls):
73 """return column names for this model """
73 """return column names for this model """
74 return class_mapper(cls).c.keys()
74 return class_mapper(cls).c.keys()
75
75
76 def get_dict(self):
76 def get_dict(self):
77 """
77 """
78 return dict with keys and values corresponding
78 return dict with keys and values corresponding
79 to this model data """
79 to this model data """
80
80
81 d = {}
81 d = {}
82 for k in self._get_keys():
82 for k in self._get_keys():
83 d[k] = getattr(self, k)
83 d[k] = getattr(self, k)
84
84
85 # also use __json__() if present to get additional fields
85 # also use __json__() if present to get additional fields
86 _json_attr = getattr(self, '__json__', None)
86 _json_attr = getattr(self, '__json__', None)
87 if _json_attr:
87 if _json_attr:
88 # update with attributes from __json__
88 # update with attributes from __json__
89 if callable(_json_attr):
89 if callable(_json_attr):
90 _json_attr = _json_attr()
90 _json_attr = _json_attr()
91 for k, val in _json_attr.iteritems():
91 for k, val in _json_attr.iteritems():
92 d[k] = val
92 d[k] = val
93 return d
93 return d
94
94
95 def get_appstruct(self):
95 def get_appstruct(self):
96 """return list with keys and values tupples corresponding
96 """return list with keys and values tupples corresponding
97 to this model data """
97 to this model data """
98
98
99 l = []
99 l = []
100 for k in self._get_keys():
100 for k in self._get_keys():
101 l.append((k, getattr(self, k),))
101 l.append((k, getattr(self, k),))
102 return l
102 return l
103
103
104 def populate_obj(self, populate_dict):
104 def populate_obj(self, populate_dict):
105 """populate model with data from given populate_dict"""
105 """populate model with data from given populate_dict"""
106
106
107 for k in self._get_keys():
107 for k in self._get_keys():
108 if k in populate_dict:
108 if k in populate_dict:
109 setattr(self, k, populate_dict[k])
109 setattr(self, k, populate_dict[k])
110
110
111 @classmethod
111 @classmethod
112 def query(cls):
112 def query(cls):
113 return Session().query(cls)
113 return Session().query(cls)
114
114
115 @classmethod
115 @classmethod
116 def get(cls, id_):
116 def get(cls, id_):
117 if id_:
117 if id_:
118 return cls.query().get(id_)
118 return cls.query().get(id_)
119
119
120 @classmethod
120 @classmethod
121 def get_or_404(cls, id_):
121 def get_or_404(cls, id_):
122 try:
122 try:
123 id_ = int(id_)
123 id_ = int(id_)
124 except (TypeError, ValueError):
124 except (TypeError, ValueError):
125 raise HTTPNotFound
125 raise HTTPNotFound
126
126
127 res = cls.query().get(id_)
127 res = cls.query().get(id_)
128 if not res:
128 if not res:
129 raise HTTPNotFound
129 raise HTTPNotFound
130 return res
130 return res
131
131
132 @classmethod
132 @classmethod
133 def getAll(cls):
133 def getAll(cls):
134 # deprecated and left for backward compatibility
134 # deprecated and left for backward compatibility
135 return cls.get_all()
135 return cls.get_all()
136
136
137 @classmethod
137 @classmethod
138 def get_all(cls):
138 def get_all(cls):
139 return cls.query().all()
139 return cls.query().all()
140
140
141 @classmethod
141 @classmethod
142 def delete(cls, id_):
142 def delete(cls, id_):
143 obj = cls.query().get(id_)
143 obj = cls.query().get(id_)
144 Session().delete(obj)
144 Session().delete(obj)
145
145
146 def __repr__(self):
146 def __repr__(self):
147 if hasattr(self, '__unicode__'):
147 if hasattr(self, '__unicode__'):
148 # python repr needs to return str
148 # python repr needs to return str
149 return safe_str(self.__unicode__())
149 return safe_str(self.__unicode__())
150 return '<DB:%s>' % (self.__class__.__name__)
150 return '<DB:%s>' % (self.__class__.__name__)
151
151
152
152
153 class RhodeCodeSetting(Base, BaseModel):
153 class RhodeCodeSetting(Base, BaseModel):
154 __tablename__ = 'rhodecode_settings'
154 __tablename__ = 'rhodecode_settings'
155 __table_args__ = (
155 __table_args__ = (
156 UniqueConstraint('app_settings_name'),
156 UniqueConstraint('app_settings_name'),
157 {'extend_existing': True, 'mysql_engine': 'InnoDB',
157 {'extend_existing': True, 'mysql_engine': 'InnoDB',
158 'mysql_charset': 'utf8'}
158 'mysql_charset': 'utf8'}
159 )
159 )
160 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
160 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
161 app_settings_name = Column("app_settings_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
161 app_settings_name = Column("app_settings_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
162 _app_settings_value = Column("app_settings_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
162 _app_settings_value = Column("app_settings_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
163
163
164 def __init__(self, k='', v=''):
164 def __init__(self, k='', v=''):
165 self.app_settings_name = k
165 self.app_settings_name = k
166 self.app_settings_value = v
166 self.app_settings_value = v
167
167
168 @validates('_app_settings_value')
168 @validates('_app_settings_value')
169 def validate_settings_value(self, key, val):
169 def validate_settings_value(self, key, val):
170 assert type(val) == unicode
170 assert type(val) == unicode
171 return val
171 return val
172
172
173 @hybrid_property
173 @hybrid_property
174 def app_settings_value(self):
174 def app_settings_value(self):
175 v = self._app_settings_value
175 v = self._app_settings_value
176 if self.app_settings_name in ["ldap_active",
176 if self.app_settings_name in ["ldap_active",
177 "default_repo_enable_statistics",
177 "default_repo_enable_statistics",
178 "default_repo_enable_locking",
178 "default_repo_enable_locking",
179 "default_repo_private",
179 "default_repo_private",
180 "default_repo_enable_downloads"]:
180 "default_repo_enable_downloads"]:
181 v = str2bool(v)
181 v = str2bool(v)
182 return v
182 return v
183
183
184 @app_settings_value.setter
184 @app_settings_value.setter
185 def app_settings_value(self, val):
185 def app_settings_value(self, val):
186 """
186 """
187 Setter that will always make sure we use unicode in app_settings_value
187 Setter that will always make sure we use unicode in app_settings_value
188
188
189 :param val:
189 :param val:
190 """
190 """
191 self._app_settings_value = safe_unicode(val)
191 self._app_settings_value = safe_unicode(val)
192
192
193 def __unicode__(self):
193 def __unicode__(self):
194 return u"<%s('%s:%s')>" % (
194 return u"<%s('%s:%s')>" % (
195 self.__class__.__name__,
195 self.__class__.__name__,
196 self.app_settings_name, self.app_settings_value
196 self.app_settings_name, self.app_settings_value
197 )
197 )
198
198
199 @classmethod
199 @classmethod
200 def get_by_name(cls, key):
200 def get_by_name(cls, key):
201 return cls.query()\
201 return cls.query()\
202 .filter(cls.app_settings_name == key).scalar()
202 .filter(cls.app_settings_name == key).scalar()
203
203
204 @classmethod
204 @classmethod
205 def get_by_name_or_create(cls, key):
205 def get_by_name_or_create(cls, key):
206 res = cls.get_by_name(key)
206 res = cls.get_by_name(key)
207 if not res:
207 if not res:
208 res = cls(key)
208 res = cls(key)
209 return res
209 return res
210
210
211 @classmethod
211 @classmethod
212 def get_app_settings(cls, cache=False):
212 def get_app_settings(cls, cache=False):
213
213
214 ret = cls.query()
214 ret = cls.query()
215
215
216 if cache:
216 if cache:
217 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
217 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
218
218
219 if not ret:
219 if not ret:
220 raise Exception('Could not get application settings !')
220 raise Exception('Could not get application settings !')
221 settings = {}
221 settings = {}
222 for each in ret:
222 for each in ret:
223 settings['rhodecode_' + each.app_settings_name] = \
223 settings['rhodecode_' + each.app_settings_name] = \
224 each.app_settings_value
224 each.app_settings_value
225
225
226 return settings
226 return settings
227
227
228 @classmethod
228 @classmethod
229 def get_ldap_settings(cls, cache=False):
229 def get_ldap_settings(cls, cache=False):
230 ret = cls.query()\
230 ret = cls.query()\
231 .filter(cls.app_settings_name.startswith('ldap_')).all()
231 .filter(cls.app_settings_name.startswith('ldap_')).all()
232 fd = {}
232 fd = {}
233 for row in ret:
233 for row in ret:
234 fd.update({row.app_settings_name: row.app_settings_value})
234 fd.update({row.app_settings_name: row.app_settings_value})
235
235
236 return fd
236 return fd
237
237
238 @classmethod
238 @classmethod
239 def get_default_repo_settings(cls, cache=False, strip_prefix=False):
239 def get_default_repo_settings(cls, cache=False, strip_prefix=False):
240 ret = cls.query()\
240 ret = cls.query()\
241 .filter(cls.app_settings_name.startswith('default_')).all()
241 .filter(cls.app_settings_name.startswith('default_')).all()
242 fd = {}
242 fd = {}
243 for row in ret:
243 for row in ret:
244 key = row.app_settings_name
244 key = row.app_settings_name
245 if strip_prefix:
245 if strip_prefix:
246 key = remove_prefix(key, prefix='default_')
246 key = remove_prefix(key, prefix='default_')
247 fd.update({key: row.app_settings_value})
247 fd.update({key: row.app_settings_value})
248
248
249 return fd
249 return fd
250
250
251
251
252 class RhodeCodeUi(Base, BaseModel):
252 class RhodeCodeUi(Base, BaseModel):
253 __tablename__ = 'rhodecode_ui'
253 __tablename__ = 'rhodecode_ui'
254 __table_args__ = (
254 __table_args__ = (
255 UniqueConstraint('ui_key'),
255 UniqueConstraint('ui_key'),
256 {'extend_existing': True, 'mysql_engine': 'InnoDB',
256 {'extend_existing': True, 'mysql_engine': 'InnoDB',
257 'mysql_charset': 'utf8'}
257 'mysql_charset': 'utf8'}
258 )
258 )
259
259
260 HOOK_UPDATE = 'changegroup.update'
260 HOOK_UPDATE = 'changegroup.update'
261 HOOK_REPO_SIZE = 'changegroup.repo_size'
261 HOOK_REPO_SIZE = 'changegroup.repo_size'
262 HOOK_PUSH = 'changegroup.push_logger'
262 HOOK_PUSH = 'changegroup.push_logger'
263 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
263 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
264 HOOK_PULL = 'outgoing.pull_logger'
264 HOOK_PULL = 'outgoing.pull_logger'
265 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
265 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
266
266
267 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
267 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
268 ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
268 ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
269 ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
269 ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
270 ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
270 ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
271 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
271 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
272
272
273 # def __init__(self, section='', key='', value=''):
274 # self.ui_section = section
275 # self.ui_key = key
276 # self.ui_value = value
277
273 @classmethod
278 @classmethod
274 def get_by_key(cls, key):
279 def get_by_key(cls, key):
275 return cls.query().filter(cls.ui_key == key).scalar()
280 return cls.query().filter(cls.ui_key == key).scalar()
276
281
277 @classmethod
282 @classmethod
278 def get_builtin_hooks(cls):
283 def get_builtin_hooks(cls):
279 q = cls.query()
284 q = cls.query()
280 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
285 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
281 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
286 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
282 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
287 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
283 return q.all()
288 return q.all()
284
289
285 @classmethod
290 @classmethod
286 def get_custom_hooks(cls):
291 def get_custom_hooks(cls):
287 q = cls.query()
292 q = cls.query()
288 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
293 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
289 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
294 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
290 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
295 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
291 q = q.filter(cls.ui_section == 'hooks')
296 q = q.filter(cls.ui_section == 'hooks')
292 return q.all()
297 return q.all()
293
298
294 @classmethod
299 @classmethod
295 def get_repos_location(cls):
300 def get_repos_location(cls):
296 return cls.get_by_key('/').ui_value
301 return cls.get_by_key('/').ui_value
297
302
298 @classmethod
303 @classmethod
299 def create_or_update_hook(cls, key, val):
304 def create_or_update_hook(cls, key, val):
300 new_ui = cls.get_by_key(key) or cls()
305 new_ui = cls.get_by_key(key) or cls()
301 new_ui.ui_section = 'hooks'
306 new_ui.ui_section = 'hooks'
302 new_ui.ui_active = True
307 new_ui.ui_active = True
303 new_ui.ui_key = key
308 new_ui.ui_key = key
304 new_ui.ui_value = val
309 new_ui.ui_value = val
305
310
306 Session().add(new_ui)
311 Session().add(new_ui)
307
312
308 def __repr__(self):
313 def __repr__(self):
309 return '<DB:%s[%s:%s]>' % (self.__class__.__name__, self.ui_key,
314 return '<DB:%s[%s:%s]>' % (self.__class__.__name__, self.ui_key,
310 self.ui_value)
315 self.ui_value)
311
316
312
317
313 class User(Base, BaseModel):
318 class User(Base, BaseModel):
314 __tablename__ = 'users'
319 __tablename__ = 'users'
315 __table_args__ = (
320 __table_args__ = (
316 UniqueConstraint('username'), UniqueConstraint('email'),
321 UniqueConstraint('username'), UniqueConstraint('email'),
317 Index('u_username_idx', 'username'),
322 Index('u_username_idx', 'username'),
318 Index('u_email_idx', 'email'),
323 Index('u_email_idx', 'email'),
319 {'extend_existing': True, 'mysql_engine': 'InnoDB',
324 {'extend_existing': True, 'mysql_engine': 'InnoDB',
320 'mysql_charset': 'utf8'}
325 'mysql_charset': 'utf8'}
321 )
326 )
322 DEFAULT_USER = 'default'
327 DEFAULT_USER = 'default'
323
328
324 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
329 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
325 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
330 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
326 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
331 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
327 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
332 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
328 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
333 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
329 name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
334 name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
330 lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
335 lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
331 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
336 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
332 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
337 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
333 ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
338 ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
334 api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
339 api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
335 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
340 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
336
341
337 user_log = relationship('UserLog')
342 user_log = relationship('UserLog')
338 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
343 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
339
344
340 repositories = relationship('Repository')
345 repositories = relationship('Repository')
341 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
346 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
342 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
347 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
343
348
344 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
349 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
345 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
350 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
346
351
347 group_member = relationship('UserGroupMember', cascade='all')
352 group_member = relationship('UserGroupMember', cascade='all')
348
353
349 notifications = relationship('UserNotification', cascade='all')
354 notifications = relationship('UserNotification', cascade='all')
350 # notifications assigned to this user
355 # notifications assigned to this user
351 user_created_notifications = relationship('Notification', cascade='all')
356 user_created_notifications = relationship('Notification', cascade='all')
352 # comments created by this user
357 # comments created by this user
353 user_comments = relationship('ChangesetComment', cascade='all')
358 user_comments = relationship('ChangesetComment', cascade='all')
354 #extra emails for this user
359 #extra emails for this user
355 user_emails = relationship('UserEmailMap', cascade='all')
360 user_emails = relationship('UserEmailMap', cascade='all')
356
361
357 @hybrid_property
362 @hybrid_property
358 def email(self):
363 def email(self):
359 return self._email
364 return self._email
360
365
361 @email.setter
366 @email.setter
362 def email(self, val):
367 def email(self, val):
363 self._email = val.lower() if val else None
368 self._email = val.lower() if val else None
364
369
365 @property
370 @property
366 def firstname(self):
371 def firstname(self):
367 # alias for future
372 # alias for future
368 return self.name
373 return self.name
369
374
370 @property
375 @property
371 def emails(self):
376 def emails(self):
372 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
377 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
373 return [self.email] + [x.email for x in other]
378 return [self.email] + [x.email for x in other]
374
379
375 @property
380 @property
376 def ip_addresses(self):
381 def ip_addresses(self):
377 ret = UserIpMap.query().filter(UserIpMap.user == self).all()
382 ret = UserIpMap.query().filter(UserIpMap.user == self).all()
378 return [x.ip_addr for x in ret]
383 return [x.ip_addr for x in ret]
379
384
380 @property
385 @property
381 def username_and_name(self):
386 def username_and_name(self):
382 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
387 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
383
388
384 @property
389 @property
385 def full_name(self):
390 def full_name(self):
386 return '%s %s' % (self.firstname, self.lastname)
391 return '%s %s' % (self.firstname, self.lastname)
387
392
388 @property
393 @property
389 def full_name_or_username(self):
394 def full_name_or_username(self):
390 return ('%s %s' % (self.firstname, self.lastname)
395 return ('%s %s' % (self.firstname, self.lastname)
391 if (self.firstname and self.lastname) else self.username)
396 if (self.firstname and self.lastname) else self.username)
392
397
393 @property
398 @property
394 def full_contact(self):
399 def full_contact(self):
395 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
400 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
396
401
397 @property
402 @property
398 def short_contact(self):
403 def short_contact(self):
399 return '%s %s' % (self.firstname, self.lastname)
404 return '%s %s' % (self.firstname, self.lastname)
400
405
401 @property
406 @property
402 def is_admin(self):
407 def is_admin(self):
403 return self.admin
408 return self.admin
404
409
405 @property
410 @property
406 def AuthUser(self):
411 def AuthUser(self):
407 """
412 """
408 Returns instance of AuthUser for this user
413 Returns instance of AuthUser for this user
409 """
414 """
410 from rhodecode.lib.auth import AuthUser
415 from rhodecode.lib.auth import AuthUser
411 return AuthUser(user_id=self.user_id, api_key=self.api_key,
416 return AuthUser(user_id=self.user_id, api_key=self.api_key,
412 username=self.username)
417 username=self.username)
413
418
414 def __unicode__(self):
419 def __unicode__(self):
415 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
420 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
416 self.user_id, self.username)
421 self.user_id, self.username)
417
422
418 @classmethod
423 @classmethod
419 def get_by_username(cls, username, case_insensitive=False, cache=False):
424 def get_by_username(cls, username, case_insensitive=False, cache=False):
420 if case_insensitive:
425 if case_insensitive:
421 q = cls.query().filter(cls.username.ilike(username))
426 q = cls.query().filter(cls.username.ilike(username))
422 else:
427 else:
423 q = cls.query().filter(cls.username == username)
428 q = cls.query().filter(cls.username == username)
424
429
425 if cache:
430 if cache:
426 q = q.options(FromCache(
431 q = q.options(FromCache(
427 "sql_cache_short",
432 "sql_cache_short",
428 "get_user_%s" % _hash_key(username)
433 "get_user_%s" % _hash_key(username)
429 )
434 )
430 )
435 )
431 return q.scalar()
436 return q.scalar()
432
437
433 @classmethod
438 @classmethod
434 def get_by_api_key(cls, api_key, cache=False):
439 def get_by_api_key(cls, api_key, cache=False):
435 q = cls.query().filter(cls.api_key == api_key)
440 q = cls.query().filter(cls.api_key == api_key)
436
441
437 if cache:
442 if cache:
438 q = q.options(FromCache("sql_cache_short",
443 q = q.options(FromCache("sql_cache_short",
439 "get_api_key_%s" % api_key))
444 "get_api_key_%s" % api_key))
440 return q.scalar()
445 return q.scalar()
441
446
442 @classmethod
447 @classmethod
443 def get_by_email(cls, email, case_insensitive=False, cache=False):
448 def get_by_email(cls, email, case_insensitive=False, cache=False):
444 if case_insensitive:
449 if case_insensitive:
445 q = cls.query().filter(cls.email.ilike(email))
450 q = cls.query().filter(cls.email.ilike(email))
446 else:
451 else:
447 q = cls.query().filter(cls.email == email)
452 q = cls.query().filter(cls.email == email)
448
453
449 if cache:
454 if cache:
450 q = q.options(FromCache("sql_cache_short",
455 q = q.options(FromCache("sql_cache_short",
451 "get_email_key_%s" % email))
456 "get_email_key_%s" % email))
452
457
453 ret = q.scalar()
458 ret = q.scalar()
454 if ret is None:
459 if ret is None:
455 q = UserEmailMap.query()
460 q = UserEmailMap.query()
456 # try fetching in alternate email map
461 # try fetching in alternate email map
457 if case_insensitive:
462 if case_insensitive:
458 q = q.filter(UserEmailMap.email.ilike(email))
463 q = q.filter(UserEmailMap.email.ilike(email))
459 else:
464 else:
460 q = q.filter(UserEmailMap.email == email)
465 q = q.filter(UserEmailMap.email == email)
461 q = q.options(joinedload(UserEmailMap.user))
466 q = q.options(joinedload(UserEmailMap.user))
462 if cache:
467 if cache:
463 q = q.options(FromCache("sql_cache_short",
468 q = q.options(FromCache("sql_cache_short",
464 "get_email_map_key_%s" % email))
469 "get_email_map_key_%s" % email))
465 ret = getattr(q.scalar(), 'user', None)
470 ret = getattr(q.scalar(), 'user', None)
466
471
467 return ret
472 return ret
468
473
469 @classmethod
474 @classmethod
470 def get_from_cs_author(cls, author):
475 def get_from_cs_author(cls, author):
471 """
476 """
472 Tries to get User objects out of commit author string
477 Tries to get User objects out of commit author string
473
478
474 :param author:
479 :param author:
475 """
480 """
476 from rhodecode.lib.helpers import email, author_name
481 from rhodecode.lib.helpers import email, author_name
477 # Valid email in the attribute passed, see if they're in the system
482 # Valid email in the attribute passed, see if they're in the system
478 _email = email(author)
483 _email = email(author)
479 if _email:
484 if _email:
480 user = cls.get_by_email(_email, case_insensitive=True)
485 user = cls.get_by_email(_email, case_insensitive=True)
481 if user:
486 if user:
482 return user
487 return user
483 # Maybe we can match by username?
488 # Maybe we can match by username?
484 _author = author_name(author)
489 _author = author_name(author)
485 user = cls.get_by_username(_author, case_insensitive=True)
490 user = cls.get_by_username(_author, case_insensitive=True)
486 if user:
491 if user:
487 return user
492 return user
488
493
489 def update_lastlogin(self):
494 def update_lastlogin(self):
490 """Update user lastlogin"""
495 """Update user lastlogin"""
491 self.last_login = datetime.datetime.now()
496 self.last_login = datetime.datetime.now()
492 Session().add(self)
497 Session().add(self)
493 log.debug('updated user %s lastlogin' % self.username)
498 log.debug('updated user %s lastlogin' % self.username)
494
499
495 @classmethod
500 @classmethod
496 def get_first_admin(cls):
501 def get_first_admin(cls):
497 user = User.query().filter(User.admin == True).first()
502 user = User.query().filter(User.admin == True).first()
498 if user is None:
503 if user is None:
499 raise Exception('Missing administrative account!')
504 raise Exception('Missing administrative account!')
500 return user
505 return user
501
506
502 @classmethod
507 @classmethod
503 def get_default_user(cls, cache=False):
508 def get_default_user(cls, cache=False):
504 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
509 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
505 if user is None:
510 if user is None:
506 raise Exception('Missing default account!')
511 raise Exception('Missing default account!')
507 return user
512 return user
508
513
509 def get_api_data(self):
514 def get_api_data(self):
510 """
515 """
511 Common function for generating user related data for API
516 Common function for generating user related data for API
512 """
517 """
513 user = self
518 user = self
514 data = dict(
519 data = dict(
515 user_id=user.user_id,
520 user_id=user.user_id,
516 username=user.username,
521 username=user.username,
517 firstname=user.name,
522 firstname=user.name,
518 lastname=user.lastname,
523 lastname=user.lastname,
519 email=user.email,
524 email=user.email,
520 emails=user.emails,
525 emails=user.emails,
521 api_key=user.api_key,
526 api_key=user.api_key,
522 active=user.active,
527 active=user.active,
523 admin=user.admin,
528 admin=user.admin,
524 ldap_dn=user.ldap_dn,
529 ldap_dn=user.ldap_dn,
525 last_login=user.last_login,
530 last_login=user.last_login,
526 ip_addresses=user.ip_addresses
531 ip_addresses=user.ip_addresses
527 )
532 )
528 return data
533 return data
529
534
530 def __json__(self):
535 def __json__(self):
531 data = dict(
536 data = dict(
532 full_name=self.full_name,
537 full_name=self.full_name,
533 full_name_or_username=self.full_name_or_username,
538 full_name_or_username=self.full_name_or_username,
534 short_contact=self.short_contact,
539 short_contact=self.short_contact,
535 full_contact=self.full_contact
540 full_contact=self.full_contact
536 )
541 )
537 data.update(self.get_api_data())
542 data.update(self.get_api_data())
538 return data
543 return data
539
544
540
545
541 class UserEmailMap(Base, BaseModel):
546 class UserEmailMap(Base, BaseModel):
542 __tablename__ = 'user_email_map'
547 __tablename__ = 'user_email_map'
543 __table_args__ = (
548 __table_args__ = (
544 Index('uem_email_idx', 'email'),
549 Index('uem_email_idx', 'email'),
545 UniqueConstraint('email'),
550 UniqueConstraint('email'),
546 {'extend_existing': True, 'mysql_engine': 'InnoDB',
551 {'extend_existing': True, 'mysql_engine': 'InnoDB',
547 'mysql_charset': 'utf8'}
552 'mysql_charset': 'utf8'}
548 )
553 )
549 __mapper_args__ = {}
554 __mapper_args__ = {}
550
555
551 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
556 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
552 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
557 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
553 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
558 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
554 user = relationship('User', lazy='joined')
559 user = relationship('User', lazy='joined')
555
560
556 @validates('_email')
561 @validates('_email')
557 def validate_email(self, key, email):
562 def validate_email(self, key, email):
558 # check if this email is not main one
563 # check if this email is not main one
559 main_email = Session().query(User).filter(User.email == email).scalar()
564 main_email = Session().query(User).filter(User.email == email).scalar()
560 if main_email is not None:
565 if main_email is not None:
561 raise AttributeError('email %s is present is user table' % email)
566 raise AttributeError('email %s is present is user table' % email)
562 return email
567 return email
563
568
564 @hybrid_property
569 @hybrid_property
565 def email(self):
570 def email(self):
566 return self._email
571 return self._email
567
572
568 @email.setter
573 @email.setter
569 def email(self, val):
574 def email(self, val):
570 self._email = val.lower() if val else None
575 self._email = val.lower() if val else None
571
576
572
577
573 class UserIpMap(Base, BaseModel):
578 class UserIpMap(Base, BaseModel):
574 __tablename__ = 'user_ip_map'
579 __tablename__ = 'user_ip_map'
575 __table_args__ = (
580 __table_args__ = (
576 UniqueConstraint('user_id', 'ip_addr'),
581 UniqueConstraint('user_id', 'ip_addr'),
577 {'extend_existing': True, 'mysql_engine': 'InnoDB',
582 {'extend_existing': True, 'mysql_engine': 'InnoDB',
578 'mysql_charset': 'utf8'}
583 'mysql_charset': 'utf8'}
579 )
584 )
580 __mapper_args__ = {}
585 __mapper_args__ = {}
581
586
582 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
587 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
583 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
588 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
584 ip_addr = Column("ip_addr", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
589 ip_addr = Column("ip_addr", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
585 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
590 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
586 user = relationship('User', lazy='joined')
591 user = relationship('User', lazy='joined')
587
592
588 @classmethod
593 @classmethod
589 def _get_ip_range(cls, ip_addr):
594 def _get_ip_range(cls, ip_addr):
590 from rhodecode.lib import ipaddr
595 from rhodecode.lib import ipaddr
591 net = ipaddr.IPNetwork(address=ip_addr)
596 net = ipaddr.IPNetwork(address=ip_addr)
592 return [str(net.network), str(net.broadcast)]
597 return [str(net.network), str(net.broadcast)]
593
598
594 def __json__(self):
599 def __json__(self):
595 return dict(
600 return dict(
596 ip_addr=self.ip_addr,
601 ip_addr=self.ip_addr,
597 ip_range=self._get_ip_range(self.ip_addr)
602 ip_range=self._get_ip_range(self.ip_addr)
598 )
603 )
599
604
600
605
601 class UserLog(Base, BaseModel):
606 class UserLog(Base, BaseModel):
602 __tablename__ = 'user_logs'
607 __tablename__ = 'user_logs'
603 __table_args__ = (
608 __table_args__ = (
604 {'extend_existing': True, 'mysql_engine': 'InnoDB',
609 {'extend_existing': True, 'mysql_engine': 'InnoDB',
605 'mysql_charset': 'utf8'},
610 'mysql_charset': 'utf8'},
606 )
611 )
607 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
612 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
608 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
613 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
609 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
614 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
610 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
615 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
611 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
616 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
612 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
617 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
613 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
618 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
614 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
619 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
615
620
616 def __unicode__(self):
621 def __unicode__(self):
617 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
622 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
618 self.repository_name,
623 self.repository_name,
619 self.action)
624 self.action)
620
625
621 @property
626 @property
622 def action_as_day(self):
627 def action_as_day(self):
623 return datetime.date(*self.action_date.timetuple()[:3])
628 return datetime.date(*self.action_date.timetuple()[:3])
624
629
625 user = relationship('User')
630 user = relationship('User')
626 repository = relationship('Repository', cascade='')
631 repository = relationship('Repository', cascade='')
627
632
628
633
629 class UserGroup(Base, BaseModel):
634 class UserGroup(Base, BaseModel):
630 __tablename__ = 'users_groups'
635 __tablename__ = 'users_groups'
631 __table_args__ = (
636 __table_args__ = (
632 {'extend_existing': True, 'mysql_engine': 'InnoDB',
637 {'extend_existing': True, 'mysql_engine': 'InnoDB',
633 'mysql_charset': 'utf8'},
638 'mysql_charset': 'utf8'},
634 )
639 )
635
640
636 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
641 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
637 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
642 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
638 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
643 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
639 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
644 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
640 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
645 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
641
646
642 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
647 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
643 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
648 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
644 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
649 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
645 users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
650 users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
646 user_user_group_to_perm = relationship('UserUserGroupToPerm ', cascade='all')
651 user_user_group_to_perm = relationship('UserUserGroupToPerm ', cascade='all')
647 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm ', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
652 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm ', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
648
653
649 user = relationship('User')
654 user = relationship('User')
650
655
651 def __unicode__(self):
656 def __unicode__(self):
652 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
657 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
653 self.users_group_id,
658 self.users_group_id,
654 self.users_group_name)
659 self.users_group_name)
655
660
656 @classmethod
661 @classmethod
657 def get_by_group_name(cls, group_name, cache=False,
662 def get_by_group_name(cls, group_name, cache=False,
658 case_insensitive=False):
663 case_insensitive=False):
659 if case_insensitive:
664 if case_insensitive:
660 q = cls.query().filter(cls.users_group_name.ilike(group_name))
665 q = cls.query().filter(cls.users_group_name.ilike(group_name))
661 else:
666 else:
662 q = cls.query().filter(cls.users_group_name == group_name)
667 q = cls.query().filter(cls.users_group_name == group_name)
663 if cache:
668 if cache:
664 q = q.options(FromCache(
669 q = q.options(FromCache(
665 "sql_cache_short",
670 "sql_cache_short",
666 "get_user_%s" % _hash_key(group_name)
671 "get_user_%s" % _hash_key(group_name)
667 )
672 )
668 )
673 )
669 return q.scalar()
674 return q.scalar()
670
675
671 @classmethod
676 @classmethod
672 def get(cls, users_group_id, cache=False):
677 def get(cls, users_group_id, cache=False):
673 users_group = cls.query()
678 users_group = cls.query()
674 if cache:
679 if cache:
675 users_group = users_group.options(FromCache("sql_cache_short",
680 users_group = users_group.options(FromCache("sql_cache_short",
676 "get_users_group_%s" % users_group_id))
681 "get_users_group_%s" % users_group_id))
677 return users_group.get(users_group_id)
682 return users_group.get(users_group_id)
678
683
679 def get_api_data(self):
684 def get_api_data(self):
680 users_group = self
685 users_group = self
681
686
682 data = dict(
687 data = dict(
683 users_group_id=users_group.users_group_id,
688 users_group_id=users_group.users_group_id,
684 group_name=users_group.users_group_name,
689 group_name=users_group.users_group_name,
685 active=users_group.users_group_active,
690 active=users_group.users_group_active,
686 )
691 )
687
692
688 return data
693 return data
689
694
690
695
691 class UserGroupMember(Base, BaseModel):
696 class UserGroupMember(Base, BaseModel):
692 __tablename__ = 'users_groups_members'
697 __tablename__ = 'users_groups_members'
693 __table_args__ = (
698 __table_args__ = (
694 {'extend_existing': True, 'mysql_engine': 'InnoDB',
699 {'extend_existing': True, 'mysql_engine': 'InnoDB',
695 'mysql_charset': 'utf8'},
700 'mysql_charset': 'utf8'},
696 )
701 )
697
702
698 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
703 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
699 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
704 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
700 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
705 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
701
706
702 user = relationship('User', lazy='joined')
707 user = relationship('User', lazy='joined')
703 users_group = relationship('UserGroup')
708 users_group = relationship('UserGroup')
704
709
705 def __init__(self, gr_id='', u_id=''):
710 def __init__(self, gr_id='', u_id=''):
706 self.users_group_id = gr_id
711 self.users_group_id = gr_id
707 self.user_id = u_id
712 self.user_id = u_id
708
713
709
714
710 class RepositoryField(Base, BaseModel):
715 class RepositoryField(Base, BaseModel):
711 __tablename__ = 'repositories_fields'
716 __tablename__ = 'repositories_fields'
712 __table_args__ = (
717 __table_args__ = (
713 UniqueConstraint('repository_id', 'field_key'), # no-multi field
718 UniqueConstraint('repository_id', 'field_key'), # no-multi field
714 {'extend_existing': True, 'mysql_engine': 'InnoDB',
719 {'extend_existing': True, 'mysql_engine': 'InnoDB',
715 'mysql_charset': 'utf8'},
720 'mysql_charset': 'utf8'},
716 )
721 )
717 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
722 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
718
723
719 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
724 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
720 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
725 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
721 field_key = Column("field_key", String(250, convert_unicode=False, assert_unicode=None))
726 field_key = Column("field_key", String(250, convert_unicode=False, assert_unicode=None))
722 field_label = Column("field_label", String(1024, convert_unicode=False, assert_unicode=None), nullable=False)
727 field_label = Column("field_label", String(1024, convert_unicode=False, assert_unicode=None), nullable=False)
723 field_value = Column("field_value", String(10000, convert_unicode=False, assert_unicode=None), nullable=False)
728 field_value = Column("field_value", String(10000, convert_unicode=False, assert_unicode=None), nullable=False)
724 field_desc = Column("field_desc", String(1024, convert_unicode=False, assert_unicode=None), nullable=False)
729 field_desc = Column("field_desc", String(1024, convert_unicode=False, assert_unicode=None), nullable=False)
725 field_type = Column("field_type", String(256), nullable=False, unique=None)
730 field_type = Column("field_type", String(256), nullable=False, unique=None)
726 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
731 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
727
732
728 repository = relationship('Repository')
733 repository = relationship('Repository')
729
734
730 @property
735 @property
731 def field_key_prefixed(self):
736 def field_key_prefixed(self):
732 return 'ex_%s' % self.field_key
737 return 'ex_%s' % self.field_key
733
738
734 @classmethod
739 @classmethod
735 def un_prefix_key(cls, key):
740 def un_prefix_key(cls, key):
736 if key.startswith(cls.PREFIX):
741 if key.startswith(cls.PREFIX):
737 return key[len(cls.PREFIX):]
742 return key[len(cls.PREFIX):]
738 return key
743 return key
739
744
740 @classmethod
745 @classmethod
741 def get_by_key_name(cls, key, repo):
746 def get_by_key_name(cls, key, repo):
742 row = cls.query()\
747 row = cls.query()\
743 .filter(cls.repository == repo)\
748 .filter(cls.repository == repo)\
744 .filter(cls.field_key == key).scalar()
749 .filter(cls.field_key == key).scalar()
745 return row
750 return row
746
751
747
752
748 class Repository(Base, BaseModel):
753 class Repository(Base, BaseModel):
749 __tablename__ = 'repositories'
754 __tablename__ = 'repositories'
750 __table_args__ = (
755 __table_args__ = (
751 UniqueConstraint('repo_name'),
756 UniqueConstraint('repo_name'),
752 Index('r_repo_name_idx', 'repo_name'),
757 Index('r_repo_name_idx', 'repo_name'),
753 {'extend_existing': True, 'mysql_engine': 'InnoDB',
758 {'extend_existing': True, 'mysql_engine': 'InnoDB',
754 'mysql_charset': 'utf8'},
759 'mysql_charset': 'utf8'},
755 )
760 )
756
761
757 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
762 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
758 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
763 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
759 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
764 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
760 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
765 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
761 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
766 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
762 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
767 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
763 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
768 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
764 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
769 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
765 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
770 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
766 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
771 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
767 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
772 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
768 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
773 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
769 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
774 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
770 _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
775 _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
771 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
776 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
772
777
773 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
778 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
774 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
779 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
775
780
776 user = relationship('User')
781 user = relationship('User')
777 fork = relationship('Repository', remote_side=repo_id)
782 fork = relationship('Repository', remote_side=repo_id)
778 group = relationship('RepoGroup')
783 group = relationship('RepoGroup')
779 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
784 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
780 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
785 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
781 stats = relationship('Statistics', cascade='all', uselist=False)
786 stats = relationship('Statistics', cascade='all', uselist=False)
782
787
783 followers = relationship('UserFollowing',
788 followers = relationship('UserFollowing',
784 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
789 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
785 cascade='all')
790 cascade='all')
786 extra_fields = relationship('RepositoryField',
791 extra_fields = relationship('RepositoryField',
787 cascade="all, delete, delete-orphan")
792 cascade="all, delete, delete-orphan")
788
793
789 logs = relationship('UserLog')
794 logs = relationship('UserLog')
790 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
795 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
791
796
792 pull_requests_org = relationship('PullRequest',
797 pull_requests_org = relationship('PullRequest',
793 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
798 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
794 cascade="all, delete, delete-orphan")
799 cascade="all, delete, delete-orphan")
795
800
796 pull_requests_other = relationship('PullRequest',
801 pull_requests_other = relationship('PullRequest',
797 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
802 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
798 cascade="all, delete, delete-orphan")
803 cascade="all, delete, delete-orphan")
799
804
800 def __unicode__(self):
805 def __unicode__(self):
801 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
806 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
802 self.repo_name)
807 self.repo_name)
803
808
804 @hybrid_property
809 @hybrid_property
805 def locked(self):
810 def locked(self):
806 # always should return [user_id, timelocked]
811 # always should return [user_id, timelocked]
807 if self._locked:
812 if self._locked:
808 _lock_info = self._locked.split(':')
813 _lock_info = self._locked.split(':')
809 return int(_lock_info[0]), _lock_info[1]
814 return int(_lock_info[0]), _lock_info[1]
810 return [None, None]
815 return [None, None]
811
816
812 @locked.setter
817 @locked.setter
813 def locked(self, val):
818 def locked(self, val):
814 if val and isinstance(val, (list, tuple)):
819 if val and isinstance(val, (list, tuple)):
815 self._locked = ':'.join(map(str, val))
820 self._locked = ':'.join(map(str, val))
816 else:
821 else:
817 self._locked = None
822 self._locked = None
818
823
819 @hybrid_property
824 @hybrid_property
820 def changeset_cache(self):
825 def changeset_cache(self):
821 from rhodecode.lib.vcs.backends.base import EmptyChangeset
826 from rhodecode.lib.vcs.backends.base import EmptyChangeset
822 dummy = EmptyChangeset().__json__()
827 dummy = EmptyChangeset().__json__()
823 if not self._changeset_cache:
828 if not self._changeset_cache:
824 return dummy
829 return dummy
825 try:
830 try:
826 return json.loads(self._changeset_cache)
831 return json.loads(self._changeset_cache)
827 except TypeError:
832 except TypeError:
828 return dummy
833 return dummy
829
834
830 @changeset_cache.setter
835 @changeset_cache.setter
831 def changeset_cache(self, val):
836 def changeset_cache(self, val):
832 try:
837 try:
833 self._changeset_cache = json.dumps(val)
838 self._changeset_cache = json.dumps(val)
834 except Exception:
839 except Exception:
835 log.error(traceback.format_exc())
840 log.error(traceback.format_exc())
836
841
837 @classmethod
842 @classmethod
838 def url_sep(cls):
843 def url_sep(cls):
839 return URL_SEP
844 return URL_SEP
840
845
841 @classmethod
846 @classmethod
842 def normalize_repo_name(cls, repo_name):
847 def normalize_repo_name(cls, repo_name):
843 """
848 """
844 Normalizes os specific repo_name to the format internally stored inside
849 Normalizes os specific repo_name to the format internally stored inside
845 dabatabase using URL_SEP
850 dabatabase using URL_SEP
846
851
847 :param cls:
852 :param cls:
848 :param repo_name:
853 :param repo_name:
849 """
854 """
850 return cls.url_sep().join(repo_name.split(os.sep))
855 return cls.url_sep().join(repo_name.split(os.sep))
851
856
852 @classmethod
857 @classmethod
853 def get_by_repo_name(cls, repo_name):
858 def get_by_repo_name(cls, repo_name):
854 q = Session().query(cls).filter(cls.repo_name == repo_name)
859 q = Session().query(cls).filter(cls.repo_name == repo_name)
855 q = q.options(joinedload(Repository.fork))\
860 q = q.options(joinedload(Repository.fork))\
856 .options(joinedload(Repository.user))\
861 .options(joinedload(Repository.user))\
857 .options(joinedload(Repository.group))
862 .options(joinedload(Repository.group))
858 return q.scalar()
863 return q.scalar()
859
864
860 @classmethod
865 @classmethod
861 def get_by_full_path(cls, repo_full_path):
866 def get_by_full_path(cls, repo_full_path):
862 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
867 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
863 repo_name = cls.normalize_repo_name(repo_name)
868 repo_name = cls.normalize_repo_name(repo_name)
864 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
869 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
865
870
866 @classmethod
871 @classmethod
867 def get_repo_forks(cls, repo_id):
872 def get_repo_forks(cls, repo_id):
868 return cls.query().filter(Repository.fork_id == repo_id)
873 return cls.query().filter(Repository.fork_id == repo_id)
869
874
870 @classmethod
875 @classmethod
871 def base_path(cls):
876 def base_path(cls):
872 """
877 """
873 Returns base path when all repos are stored
878 Returns base path when all repos are stored
874
879
875 :param cls:
880 :param cls:
876 """
881 """
877 q = Session().query(RhodeCodeUi)\
882 q = Session().query(RhodeCodeUi)\
878 .filter(RhodeCodeUi.ui_key == cls.url_sep())
883 .filter(RhodeCodeUi.ui_key == cls.url_sep())
879 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
884 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
880 return q.one().ui_value
885 return q.one().ui_value
881
886
882 @property
887 @property
883 def forks(self):
888 def forks(self):
884 """
889 """
885 Return forks of this repo
890 Return forks of this repo
886 """
891 """
887 return Repository.get_repo_forks(self.repo_id)
892 return Repository.get_repo_forks(self.repo_id)
888
893
889 @property
894 @property
890 def parent(self):
895 def parent(self):
891 """
896 """
892 Returns fork parent
897 Returns fork parent
893 """
898 """
894 return self.fork
899 return self.fork
895
900
896 @property
901 @property
897 def just_name(self):
902 def just_name(self):
898 return self.repo_name.split(Repository.url_sep())[-1]
903 return self.repo_name.split(Repository.url_sep())[-1]
899
904
900 @property
905 @property
901 def groups_with_parents(self):
906 def groups_with_parents(self):
902 groups = []
907 groups = []
903 if self.group is None:
908 if self.group is None:
904 return groups
909 return groups
905
910
906 cur_gr = self.group
911 cur_gr = self.group
907 groups.insert(0, cur_gr)
912 groups.insert(0, cur_gr)
908 while 1:
913 while 1:
909 gr = getattr(cur_gr, 'parent_group', None)
914 gr = getattr(cur_gr, 'parent_group', None)
910 cur_gr = cur_gr.parent_group
915 cur_gr = cur_gr.parent_group
911 if gr is None:
916 if gr is None:
912 break
917 break
913 groups.insert(0, gr)
918 groups.insert(0, gr)
914
919
915 return groups
920 return groups
916
921
917 @property
922 @property
918 def groups_and_repo(self):
923 def groups_and_repo(self):
919 return self.groups_with_parents, self.just_name, self.repo_name
924 return self.groups_with_parents, self.just_name, self.repo_name
920
925
921 @LazyProperty
926 @LazyProperty
922 def repo_path(self):
927 def repo_path(self):
923 """
928 """
924 Returns base full path for that repository means where it actually
929 Returns base full path for that repository means where it actually
925 exists on a filesystem
930 exists on a filesystem
926 """
931 """
927 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
932 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
928 Repository.url_sep())
933 Repository.url_sep())
929 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
934 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
930 return q.one().ui_value
935 return q.one().ui_value
931
936
932 @property
937 @property
933 def repo_full_path(self):
938 def repo_full_path(self):
934 p = [self.repo_path]
939 p = [self.repo_path]
935 # we need to split the name by / since this is how we store the
940 # we need to split the name by / since this is how we store the
936 # names in the database, but that eventually needs to be converted
941 # names in the database, but that eventually needs to be converted
937 # into a valid system path
942 # into a valid system path
938 p += self.repo_name.split(Repository.url_sep())
943 p += self.repo_name.split(Repository.url_sep())
939 return os.path.join(*map(safe_unicode, p))
944 return os.path.join(*map(safe_unicode, p))
940
945
941 @property
946 @property
942 def cache_keys(self):
947 def cache_keys(self):
943 """
948 """
944 Returns associated cache keys for that repo
949 Returns associated cache keys for that repo
945 """
950 """
946 return CacheInvalidation.query()\
951 return CacheInvalidation.query()\
947 .filter(CacheInvalidation.cache_args == self.repo_name)\
952 .filter(CacheInvalidation.cache_args == self.repo_name)\
948 .order_by(CacheInvalidation.cache_key)\
953 .order_by(CacheInvalidation.cache_key)\
949 .all()
954 .all()
950
955
951 def get_new_name(self, repo_name):
956 def get_new_name(self, repo_name):
952 """
957 """
953 returns new full repository name based on assigned group and new new
958 returns new full repository name based on assigned group and new new
954
959
955 :param group_name:
960 :param group_name:
956 """
961 """
957 path_prefix = self.group.full_path_splitted if self.group else []
962 path_prefix = self.group.full_path_splitted if self.group else []
958 return Repository.url_sep().join(path_prefix + [repo_name])
963 return Repository.url_sep().join(path_prefix + [repo_name])
959
964
960 @property
965 @property
961 def _ui(self):
966 def _ui(self):
962 """
967 """
963 Creates an db based ui object for this repository
968 Creates an db based ui object for this repository
964 """
969 """
965 from rhodecode.lib.utils import make_ui
970 from rhodecode.lib.utils import make_ui
966 return make_ui('db', clear_session=False)
971 return make_ui('db', clear_session=False)
967
972
968 @classmethod
973 @classmethod
969 def is_valid(cls, repo_name):
974 def is_valid(cls, repo_name):
970 """
975 """
971 returns True if given repo name is a valid filesystem repository
976 returns True if given repo name is a valid filesystem repository
972
977
973 :param cls:
978 :param cls:
974 :param repo_name:
979 :param repo_name:
975 """
980 """
976 from rhodecode.lib.utils import is_valid_repo
981 from rhodecode.lib.utils import is_valid_repo
977
982
978 return is_valid_repo(repo_name, cls.base_path())
983 return is_valid_repo(repo_name, cls.base_path())
979
984
980 def get_api_data(self):
985 def get_api_data(self):
981 """
986 """
982 Common function for generating repo api data
987 Common function for generating repo api data
983
988
984 """
989 """
985 repo = self
990 repo = self
986 data = dict(
991 data = dict(
987 repo_id=repo.repo_id,
992 repo_id=repo.repo_id,
988 repo_name=repo.repo_name,
993 repo_name=repo.repo_name,
989 repo_type=repo.repo_type,
994 repo_type=repo.repo_type,
990 clone_uri=repo.clone_uri,
995 clone_uri=repo.clone_uri,
991 private=repo.private,
996 private=repo.private,
992 created_on=repo.created_on,
997 created_on=repo.created_on,
993 description=repo.description,
998 description=repo.description,
994 landing_rev=repo.landing_rev,
999 landing_rev=repo.landing_rev,
995 owner=repo.user.username,
1000 owner=repo.user.username,
996 fork_of=repo.fork.repo_name if repo.fork else None,
1001 fork_of=repo.fork.repo_name if repo.fork else None,
997 enable_statistics=repo.enable_statistics,
1002 enable_statistics=repo.enable_statistics,
998 enable_locking=repo.enable_locking,
1003 enable_locking=repo.enable_locking,
999 enable_downloads=repo.enable_downloads,
1004 enable_downloads=repo.enable_downloads,
1000 last_changeset=repo.changeset_cache,
1005 last_changeset=repo.changeset_cache,
1001 locked_by=User.get(self.locked[0]).get_api_data() \
1006 locked_by=User.get(self.locked[0]).get_api_data() \
1002 if self.locked[0] else None,
1007 if self.locked[0] else None,
1003 locked_date=time_to_datetime(self.locked[1]) \
1008 locked_date=time_to_datetime(self.locked[1]) \
1004 if self.locked[1] else None
1009 if self.locked[1] else None
1005 )
1010 )
1006 rc_config = RhodeCodeSetting.get_app_settings()
1011 rc_config = RhodeCodeSetting.get_app_settings()
1007 repository_fields = str2bool(rc_config.get('rhodecode_repository_fields'))
1012 repository_fields = str2bool(rc_config.get('rhodecode_repository_fields'))
1008 if repository_fields:
1013 if repository_fields:
1009 for f in self.extra_fields:
1014 for f in self.extra_fields:
1010 data[f.field_key_prefixed] = f.field_value
1015 data[f.field_key_prefixed] = f.field_value
1011
1016
1012 return data
1017 return data
1013
1018
1014 @classmethod
1019 @classmethod
1015 def lock(cls, repo, user_id, lock_time=None):
1020 def lock(cls, repo, user_id, lock_time=None):
1016 if not lock_time:
1021 if not lock_time:
1017 lock_time = time.time()
1022 lock_time = time.time()
1018 repo.locked = [user_id, lock_time]
1023 repo.locked = [user_id, lock_time]
1019 Session().add(repo)
1024 Session().add(repo)
1020 Session().commit()
1025 Session().commit()
1021
1026
1022 @classmethod
1027 @classmethod
1023 def unlock(cls, repo):
1028 def unlock(cls, repo):
1024 repo.locked = None
1029 repo.locked = None
1025 Session().add(repo)
1030 Session().add(repo)
1026 Session().commit()
1031 Session().commit()
1027
1032
1028 @classmethod
1033 @classmethod
1029 def getlock(cls, repo):
1034 def getlock(cls, repo):
1030 return repo.locked
1035 return repo.locked
1031
1036
1032 @property
1037 @property
1033 def last_db_change(self):
1038 def last_db_change(self):
1034 return self.updated_on
1039 return self.updated_on
1035
1040
1036 def clone_url(self, **override):
1041 def clone_url(self, **override):
1037 from pylons import url
1042 from pylons import url
1038 from urlparse import urlparse
1043 from urlparse import urlparse
1039 import urllib
1044 import urllib
1040 parsed_url = urlparse(url('home', qualified=True))
1045 parsed_url = urlparse(url('home', qualified=True))
1041 default_clone_uri = '%(scheme)s://%(user)s%(pass)s%(netloc)s%(prefix)s%(path)s'
1046 default_clone_uri = '%(scheme)s://%(user)s%(pass)s%(netloc)s%(prefix)s%(path)s'
1042 decoded_path = safe_unicode(urllib.unquote(parsed_url.path))
1047 decoded_path = safe_unicode(urllib.unquote(parsed_url.path))
1043 args = {
1048 args = {
1044 'user': '',
1049 'user': '',
1045 'pass': '',
1050 'pass': '',
1046 'scheme': parsed_url.scheme,
1051 'scheme': parsed_url.scheme,
1047 'netloc': parsed_url.netloc,
1052 'netloc': parsed_url.netloc,
1048 'prefix': decoded_path,
1053 'prefix': decoded_path,
1049 'path': self.repo_name
1054 'path': self.repo_name
1050 }
1055 }
1051
1056
1052 args.update(override)
1057 args.update(override)
1053 return default_clone_uri % args
1058 return default_clone_uri % args
1054
1059
1055 #==========================================================================
1060 #==========================================================================
1056 # SCM PROPERTIES
1061 # SCM PROPERTIES
1057 #==========================================================================
1062 #==========================================================================
1058
1063
1059 def get_changeset(self, rev=None):
1064 def get_changeset(self, rev=None):
1060 return get_changeset_safe(self.scm_instance, rev)
1065 return get_changeset_safe(self.scm_instance, rev)
1061
1066
1062 def get_landing_changeset(self):
1067 def get_landing_changeset(self):
1063 """
1068 """
1064 Returns landing changeset, or if that doesn't exist returns the tip
1069 Returns landing changeset, or if that doesn't exist returns the tip
1065 """
1070 """
1066 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
1071 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
1067 return cs
1072 return cs
1068
1073
1069 def update_changeset_cache(self, cs_cache=None):
1074 def update_changeset_cache(self, cs_cache=None):
1070 """
1075 """
1071 Update cache of last changeset for repository, keys should be::
1076 Update cache of last changeset for repository, keys should be::
1072
1077
1073 short_id
1078 short_id
1074 raw_id
1079 raw_id
1075 revision
1080 revision
1076 message
1081 message
1077 date
1082 date
1078 author
1083 author
1079
1084
1080 :param cs_cache:
1085 :param cs_cache:
1081 """
1086 """
1082 from rhodecode.lib.vcs.backends.base import BaseChangeset
1087 from rhodecode.lib.vcs.backends.base import BaseChangeset
1083 if cs_cache is None:
1088 if cs_cache is None:
1084 cs_cache = EmptyChangeset()
1089 cs_cache = EmptyChangeset()
1085 # use no-cache version here
1090 # use no-cache version here
1086 scm_repo = self.scm_instance_no_cache()
1091 scm_repo = self.scm_instance_no_cache()
1087 if scm_repo:
1092 if scm_repo:
1088 cs_cache = scm_repo.get_changeset()
1093 cs_cache = scm_repo.get_changeset()
1089
1094
1090 if isinstance(cs_cache, BaseChangeset):
1095 if isinstance(cs_cache, BaseChangeset):
1091 cs_cache = cs_cache.__json__()
1096 cs_cache = cs_cache.__json__()
1092
1097
1093 if (cs_cache != self.changeset_cache or not self.changeset_cache):
1098 if (cs_cache != self.changeset_cache or not self.changeset_cache):
1094 _default = datetime.datetime.fromtimestamp(0)
1099 _default = datetime.datetime.fromtimestamp(0)
1095 last_change = cs_cache.get('date') or _default
1100 last_change = cs_cache.get('date') or _default
1096 log.debug('updated repo %s with new cs cache %s'
1101 log.debug('updated repo %s with new cs cache %s'
1097 % (self.repo_name, cs_cache))
1102 % (self.repo_name, cs_cache))
1098 self.updated_on = last_change
1103 self.updated_on = last_change
1099 self.changeset_cache = cs_cache
1104 self.changeset_cache = cs_cache
1100 Session().add(self)
1105 Session().add(self)
1101 Session().commit()
1106 Session().commit()
1102 else:
1107 else:
1103 log.debug('Skipping repo:%s already with latest changes'
1108 log.debug('Skipping repo:%s already with latest changes'
1104 % self.repo_name)
1109 % self.repo_name)
1105
1110
1106 @property
1111 @property
1107 def tip(self):
1112 def tip(self):
1108 return self.get_changeset('tip')
1113 return self.get_changeset('tip')
1109
1114
1110 @property
1115 @property
1111 def author(self):
1116 def author(self):
1112 return self.tip.author
1117 return self.tip.author
1113
1118
1114 @property
1119 @property
1115 def last_change(self):
1120 def last_change(self):
1116 return self.scm_instance.last_change
1121 return self.scm_instance.last_change
1117
1122
1118 def get_comments(self, revisions=None):
1123 def get_comments(self, revisions=None):
1119 """
1124 """
1120 Returns comments for this repository grouped by revisions
1125 Returns comments for this repository grouped by revisions
1121
1126
1122 :param revisions: filter query by revisions only
1127 :param revisions: filter query by revisions only
1123 """
1128 """
1124 cmts = ChangesetComment.query()\
1129 cmts = ChangesetComment.query()\
1125 .filter(ChangesetComment.repo == self)
1130 .filter(ChangesetComment.repo == self)
1126 if revisions:
1131 if revisions:
1127 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
1132 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
1128 grouped = collections.defaultdict(list)
1133 grouped = collections.defaultdict(list)
1129 for cmt in cmts.all():
1134 for cmt in cmts.all():
1130 grouped[cmt.revision].append(cmt)
1135 grouped[cmt.revision].append(cmt)
1131 return grouped
1136 return grouped
1132
1137
1133 def statuses(self, revisions=None):
1138 def statuses(self, revisions=None):
1134 """
1139 """
1135 Returns statuses for this repository
1140 Returns statuses for this repository
1136
1141
1137 :param revisions: list of revisions to get statuses for
1142 :param revisions: list of revisions to get statuses for
1138 """
1143 """
1139
1144
1140 statuses = ChangesetStatus.query()\
1145 statuses = ChangesetStatus.query()\
1141 .filter(ChangesetStatus.repo == self)\
1146 .filter(ChangesetStatus.repo == self)\
1142 .filter(ChangesetStatus.version == 0)
1147 .filter(ChangesetStatus.version == 0)
1143 if revisions:
1148 if revisions:
1144 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
1149 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
1145 grouped = {}
1150 grouped = {}
1146
1151
1147 #maybe we have open new pullrequest without a status ?
1152 #maybe we have open new pullrequest without a status ?
1148 stat = ChangesetStatus.STATUS_UNDER_REVIEW
1153 stat = ChangesetStatus.STATUS_UNDER_REVIEW
1149 status_lbl = ChangesetStatus.get_status_lbl(stat)
1154 status_lbl = ChangesetStatus.get_status_lbl(stat)
1150 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
1155 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
1151 for rev in pr.revisions:
1156 for rev in pr.revisions:
1152 pr_id = pr.pull_request_id
1157 pr_id = pr.pull_request_id
1153 pr_repo = pr.other_repo.repo_name
1158 pr_repo = pr.other_repo.repo_name
1154 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
1159 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
1155
1160
1156 for stat in statuses.all():
1161 for stat in statuses.all():
1157 pr_id = pr_repo = None
1162 pr_id = pr_repo = None
1158 if stat.pull_request:
1163 if stat.pull_request:
1159 pr_id = stat.pull_request.pull_request_id
1164 pr_id = stat.pull_request.pull_request_id
1160 pr_repo = stat.pull_request.other_repo.repo_name
1165 pr_repo = stat.pull_request.other_repo.repo_name
1161 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
1166 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
1162 pr_id, pr_repo]
1167 pr_id, pr_repo]
1163 return grouped
1168 return grouped
1164
1169
1165 def _repo_size(self):
1170 def _repo_size(self):
1166 from rhodecode.lib import helpers as h
1171 from rhodecode.lib import helpers as h
1167 log.debug('calculating repository size...')
1172 log.debug('calculating repository size...')
1168 return h.format_byte_size(self.scm_instance.size)
1173 return h.format_byte_size(self.scm_instance.size)
1169
1174
1170 #==========================================================================
1175 #==========================================================================
1171 # SCM CACHE INSTANCE
1176 # SCM CACHE INSTANCE
1172 #==========================================================================
1177 #==========================================================================
1173
1178
1174 def set_invalidate(self):
1179 def set_invalidate(self):
1175 """
1180 """
1176 Mark caches of this repo as invalid.
1181 Mark caches of this repo as invalid.
1177 """
1182 """
1178 CacheInvalidation.set_invalidate(self.repo_name)
1183 CacheInvalidation.set_invalidate(self.repo_name)
1179
1184
1180 def scm_instance_no_cache(self):
1185 def scm_instance_no_cache(self):
1181 return self.__get_instance()
1186 return self.__get_instance()
1182
1187
1183 @property
1188 @property
1184 def scm_instance(self):
1189 def scm_instance(self):
1185 import rhodecode
1190 import rhodecode
1186 full_cache = str2bool(rhodecode.CONFIG.get('vcs_full_cache'))
1191 full_cache = str2bool(rhodecode.CONFIG.get('vcs_full_cache'))
1187 if full_cache:
1192 if full_cache:
1188 return self.scm_instance_cached()
1193 return self.scm_instance_cached()
1189 return self.__get_instance()
1194 return self.__get_instance()
1190
1195
1191 def scm_instance_cached(self, valid_cache_keys=None):
1196 def scm_instance_cached(self, valid_cache_keys=None):
1192 @cache_region('long_term')
1197 @cache_region('long_term')
1193 def _c(repo_name):
1198 def _c(repo_name):
1194 return self.__get_instance()
1199 return self.__get_instance()
1195 rn = self.repo_name
1200 rn = self.repo_name
1196
1201
1197 valid = CacheInvalidation.test_and_set_valid(rn, None, valid_cache_keys=valid_cache_keys)
1202 valid = CacheInvalidation.test_and_set_valid(rn, None, valid_cache_keys=valid_cache_keys)
1198 if not valid:
1203 if not valid:
1199 log.debug('Cache for %s invalidated, getting new object' % (rn))
1204 log.debug('Cache for %s invalidated, getting new object' % (rn))
1200 region_invalidate(_c, None, rn)
1205 region_invalidate(_c, None, rn)
1201 else:
1206 else:
1202 log.debug('Getting obj for %s from cache' % (rn))
1207 log.debug('Getting obj for %s from cache' % (rn))
1203 return _c(rn)
1208 return _c(rn)
1204
1209
1205 def __get_instance(self):
1210 def __get_instance(self):
1206 repo_full_path = self.repo_full_path
1211 repo_full_path = self.repo_full_path
1207 try:
1212 try:
1208 alias = get_scm(repo_full_path)[0]
1213 alias = get_scm(repo_full_path)[0]
1209 log.debug('Creating instance of %s repository from %s'
1214 log.debug('Creating instance of %s repository from %s'
1210 % (alias, repo_full_path))
1215 % (alias, repo_full_path))
1211 backend = get_backend(alias)
1216 backend = get_backend(alias)
1212 except VCSError:
1217 except VCSError:
1213 log.error(traceback.format_exc())
1218 log.error(traceback.format_exc())
1214 log.error('Perhaps this repository is in db and not in '
1219 log.error('Perhaps this repository is in db and not in '
1215 'filesystem run rescan repositories with '
1220 'filesystem run rescan repositories with '
1216 '"destroy old data " option from admin panel')
1221 '"destroy old data " option from admin panel')
1217 return
1222 return
1218
1223
1219 if alias == 'hg':
1224 if alias == 'hg':
1220
1225
1221 repo = backend(safe_str(repo_full_path), create=False,
1226 repo = backend(safe_str(repo_full_path), create=False,
1222 baseui=self._ui)
1227 baseui=self._ui)
1223 # skip hidden web repository
1228 # skip hidden web repository
1224 if repo._get_hidden():
1229 if repo._get_hidden():
1225 return
1230 return
1226 else:
1231 else:
1227 repo = backend(repo_full_path, create=False)
1232 repo = backend(repo_full_path, create=False)
1228
1233
1229 return repo
1234 return repo
1230
1235
1231
1236
1232 class RepoGroup(Base, BaseModel):
1237 class RepoGroup(Base, BaseModel):
1233 __tablename__ = 'groups'
1238 __tablename__ = 'groups'
1234 __table_args__ = (
1239 __table_args__ = (
1235 UniqueConstraint('group_name', 'group_parent_id'),
1240 UniqueConstraint('group_name', 'group_parent_id'),
1236 CheckConstraint('group_id != group_parent_id'),
1241 CheckConstraint('group_id != group_parent_id'),
1237 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1242 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1238 'mysql_charset': 'utf8'},
1243 'mysql_charset': 'utf8'},
1239 )
1244 )
1240 __mapper_args__ = {'order_by': 'group_name'}
1245 __mapper_args__ = {'order_by': 'group_name'}
1241
1246
1242 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1247 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1243 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
1248 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
1244 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
1249 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
1245 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1250 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1246 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
1251 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
1247 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
1252 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
1248
1253
1249 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
1254 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
1250 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
1255 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
1251 parent_group = relationship('RepoGroup', remote_side=group_id)
1256 parent_group = relationship('RepoGroup', remote_side=group_id)
1252 user = relationship('User')
1257 user = relationship('User')
1253
1258
1254 def __init__(self, group_name='', parent_group=None):
1259 def __init__(self, group_name='', parent_group=None):
1255 self.group_name = group_name
1260 self.group_name = group_name
1256 self.parent_group = parent_group
1261 self.parent_group = parent_group
1257
1262
1258 def __unicode__(self):
1263 def __unicode__(self):
1259 return u"<%s('id:%s:%s')>" % (self.__class__.__name__, self.group_id,
1264 return u"<%s('id:%s:%s')>" % (self.__class__.__name__, self.group_id,
1260 self.group_name)
1265 self.group_name)
1261
1266
1262 @classmethod
1267 @classmethod
1263 def groups_choices(cls, groups=None, show_empty_group=True):
1268 def groups_choices(cls, groups=None, show_empty_group=True):
1264 from webhelpers.html import literal as _literal
1269 from webhelpers.html import literal as _literal
1265 if not groups:
1270 if not groups:
1266 groups = cls.query().all()
1271 groups = cls.query().all()
1267
1272
1268 repo_groups = []
1273 repo_groups = []
1269 if show_empty_group:
1274 if show_empty_group:
1270 repo_groups = [('-1', '-- %s --' % _('top level'))]
1275 repo_groups = [('-1', '-- %s --' % _('top level'))]
1271 sep = ' &raquo; '
1276 sep = ' &raquo; '
1272 _name = lambda k: _literal(sep.join(k))
1277 _name = lambda k: _literal(sep.join(k))
1273
1278
1274 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
1279 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
1275 for x in groups])
1280 for x in groups])
1276
1281
1277 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
1282 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
1278 return repo_groups
1283 return repo_groups
1279
1284
1280 @classmethod
1285 @classmethod
1281 def url_sep(cls):
1286 def url_sep(cls):
1282 return URL_SEP
1287 return URL_SEP
1283
1288
1284 @classmethod
1289 @classmethod
1285 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
1290 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
1286 if case_insensitive:
1291 if case_insensitive:
1287 gr = cls.query()\
1292 gr = cls.query()\
1288 .filter(cls.group_name.ilike(group_name))
1293 .filter(cls.group_name.ilike(group_name))
1289 else:
1294 else:
1290 gr = cls.query()\
1295 gr = cls.query()\
1291 .filter(cls.group_name == group_name)
1296 .filter(cls.group_name == group_name)
1292 if cache:
1297 if cache:
1293 gr = gr.options(FromCache(
1298 gr = gr.options(FromCache(
1294 "sql_cache_short",
1299 "sql_cache_short",
1295 "get_group_%s" % _hash_key(group_name)
1300 "get_group_%s" % _hash_key(group_name)
1296 )
1301 )
1297 )
1302 )
1298 return gr.scalar()
1303 return gr.scalar()
1299
1304
1300 @property
1305 @property
1301 def parents(self):
1306 def parents(self):
1302 parents_recursion_limit = 5
1307 parents_recursion_limit = 5
1303 groups = []
1308 groups = []
1304 if self.parent_group is None:
1309 if self.parent_group is None:
1305 return groups
1310 return groups
1306 cur_gr = self.parent_group
1311 cur_gr = self.parent_group
1307 groups.insert(0, cur_gr)
1312 groups.insert(0, cur_gr)
1308 cnt = 0
1313 cnt = 0
1309 while 1:
1314 while 1:
1310 cnt += 1
1315 cnt += 1
1311 gr = getattr(cur_gr, 'parent_group', None)
1316 gr = getattr(cur_gr, 'parent_group', None)
1312 cur_gr = cur_gr.parent_group
1317 cur_gr = cur_gr.parent_group
1313 if gr is None:
1318 if gr is None:
1314 break
1319 break
1315 if cnt == parents_recursion_limit:
1320 if cnt == parents_recursion_limit:
1316 # this will prevent accidental infinit loops
1321 # this will prevent accidental infinit loops
1317 log.error('group nested more than %s' %
1322 log.error('group nested more than %s' %
1318 parents_recursion_limit)
1323 parents_recursion_limit)
1319 break
1324 break
1320
1325
1321 groups.insert(0, gr)
1326 groups.insert(0, gr)
1322 return groups
1327 return groups
1323
1328
1324 @property
1329 @property
1325 def children(self):
1330 def children(self):
1326 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1331 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1327
1332
1328 @property
1333 @property
1329 def name(self):
1334 def name(self):
1330 return self.group_name.split(RepoGroup.url_sep())[-1]
1335 return self.group_name.split(RepoGroup.url_sep())[-1]
1331
1336
1332 @property
1337 @property
1333 def full_path(self):
1338 def full_path(self):
1334 return self.group_name
1339 return self.group_name
1335
1340
1336 @property
1341 @property
1337 def full_path_splitted(self):
1342 def full_path_splitted(self):
1338 return self.group_name.split(RepoGroup.url_sep())
1343 return self.group_name.split(RepoGroup.url_sep())
1339
1344
1340 @property
1345 @property
1341 def repositories(self):
1346 def repositories(self):
1342 return Repository.query()\
1347 return Repository.query()\
1343 .filter(Repository.group == self)\
1348 .filter(Repository.group == self)\
1344 .order_by(Repository.repo_name)
1349 .order_by(Repository.repo_name)
1345
1350
1346 @property
1351 @property
1347 def repositories_recursive_count(self):
1352 def repositories_recursive_count(self):
1348 cnt = self.repositories.count()
1353 cnt = self.repositories.count()
1349
1354
1350 def children_count(group):
1355 def children_count(group):
1351 cnt = 0
1356 cnt = 0
1352 for child in group.children:
1357 for child in group.children:
1353 cnt += child.repositories.count()
1358 cnt += child.repositories.count()
1354 cnt += children_count(child)
1359 cnt += children_count(child)
1355 return cnt
1360 return cnt
1356
1361
1357 return cnt + children_count(self)
1362 return cnt + children_count(self)
1358
1363
1359 def _recursive_objects(self, include_repos=True):
1364 def _recursive_objects(self, include_repos=True):
1360 all_ = []
1365 all_ = []
1361
1366
1362 def _get_members(root_gr):
1367 def _get_members(root_gr):
1363 if include_repos:
1368 if include_repos:
1364 for r in root_gr.repositories:
1369 for r in root_gr.repositories:
1365 all_.append(r)
1370 all_.append(r)
1366 childs = root_gr.children.all()
1371 childs = root_gr.children.all()
1367 if childs:
1372 if childs:
1368 for gr in childs:
1373 for gr in childs:
1369 all_.append(gr)
1374 all_.append(gr)
1370 _get_members(gr)
1375 _get_members(gr)
1371
1376
1372 _get_members(self)
1377 _get_members(self)
1373 return [self] + all_
1378 return [self] + all_
1374
1379
1375 def recursive_groups_and_repos(self):
1380 def recursive_groups_and_repos(self):
1376 """
1381 """
1377 Recursive return all groups, with repositories in those groups
1382 Recursive return all groups, with repositories in those groups
1378 """
1383 """
1379 return self._recursive_objects()
1384 return self._recursive_objects()
1380
1385
1381 def recursive_groups(self):
1386 def recursive_groups(self):
1382 """
1387 """
1383 Returns all children groups for this group including children of children
1388 Returns all children groups for this group including children of children
1384 """
1389 """
1385 return self._recursive_objects(include_repos=False)
1390 return self._recursive_objects(include_repos=False)
1386
1391
1387 def get_new_name(self, group_name):
1392 def get_new_name(self, group_name):
1388 """
1393 """
1389 returns new full group name based on parent and new name
1394 returns new full group name based on parent and new name
1390
1395
1391 :param group_name:
1396 :param group_name:
1392 """
1397 """
1393 path_prefix = (self.parent_group.full_path_splitted if
1398 path_prefix = (self.parent_group.full_path_splitted if
1394 self.parent_group else [])
1399 self.parent_group else [])
1395 return RepoGroup.url_sep().join(path_prefix + [group_name])
1400 return RepoGroup.url_sep().join(path_prefix + [group_name])
1396
1401
1397
1402
1398 class Permission(Base, BaseModel):
1403 class Permission(Base, BaseModel):
1399 __tablename__ = 'permissions'
1404 __tablename__ = 'permissions'
1400 __table_args__ = (
1405 __table_args__ = (
1401 Index('p_perm_name_idx', 'permission_name'),
1406 Index('p_perm_name_idx', 'permission_name'),
1402 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1407 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1403 'mysql_charset': 'utf8'},
1408 'mysql_charset': 'utf8'},
1404 )
1409 )
1405 PERMS = [
1410 PERMS = [
1406 ('hg.admin', _('RhodeCode Administrator')),
1411 ('hg.admin', _('RhodeCode Administrator')),
1407
1412
1408 ('repository.none', _('Repository no access')),
1413 ('repository.none', _('Repository no access')),
1409 ('repository.read', _('Repository read access')),
1414 ('repository.read', _('Repository read access')),
1410 ('repository.write', _('Repository write access')),
1415 ('repository.write', _('Repository write access')),
1411 ('repository.admin', _('Repository admin access')),
1416 ('repository.admin', _('Repository admin access')),
1412
1417
1413 ('group.none', _('Repository group no access')),
1418 ('group.none', _('Repository group no access')),
1414 ('group.read', _('Repository group read access')),
1419 ('group.read', _('Repository group read access')),
1415 ('group.write', _('Repository group write access')),
1420 ('group.write', _('Repository group write access')),
1416 ('group.admin', _('Repository group admin access')),
1421 ('group.admin', _('Repository group admin access')),
1417
1422
1418 ('usergroup.none', _('User group no access')),
1423 ('usergroup.none', _('User group no access')),
1419 ('usergroup.read', _('User group read access')),
1424 ('usergroup.read', _('User group read access')),
1420 ('usergroup.write', _('User group write access')),
1425 ('usergroup.write', _('User group write access')),
1421 ('usergroup.admin', _('User group admin access')),
1426 ('usergroup.admin', _('User group admin access')),
1422
1427
1423 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
1428 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
1424 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
1429 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
1425
1430
1426 ('hg.usergroup.create.false', _('User Group creation disabled')),
1431 ('hg.usergroup.create.false', _('User Group creation disabled')),
1427 ('hg.usergroup.create.true', _('User Group creation enabled')),
1432 ('hg.usergroup.create.true', _('User Group creation enabled')),
1428
1433
1429 ('hg.create.none', _('Repository creation disabled')),
1434 ('hg.create.none', _('Repository creation disabled')),
1430 ('hg.create.repository', _('Repository creation enabled')),
1435 ('hg.create.repository', _('Repository creation enabled')),
1431
1436
1432 ('hg.fork.none', _('Repository forking disabled')),
1437 ('hg.fork.none', _('Repository forking disabled')),
1433 ('hg.fork.repository', _('Repository forking enabled')),
1438 ('hg.fork.repository', _('Repository forking enabled')),
1434
1439
1435 ('hg.register.none', _('Registration disabled')),
1440 ('hg.register.none', _('Registration disabled')),
1436 ('hg.register.manual_activate', _('User Registration with manual account activation')),
1441 ('hg.register.manual_activate', _('User Registration with manual account activation')),
1437 ('hg.register.auto_activate', _('User Registration with automatic account activation')),
1442 ('hg.register.auto_activate', _('User Registration with automatic account activation')),
1438
1443
1439 ('hg.extern_activate.manual', _('Manual activation of external account')),
1444 ('hg.extern_activate.manual', _('Manual activation of external account')),
1440 ('hg.extern_activate.auto', _('Automatic activation of external account')),
1445 ('hg.extern_activate.auto', _('Automatic activation of external account')),
1441
1446
1442 ]
1447 ]
1443
1448
1444 #definition of system default permissions for DEFAULT user
1449 #definition of system default permissions for DEFAULT user
1445 DEFAULT_USER_PERMISSIONS = [
1450 DEFAULT_USER_PERMISSIONS = [
1446 'repository.read',
1451 'repository.read',
1447 'group.read',
1452 'group.read',
1448 'usergroup.read',
1453 'usergroup.read',
1449 'hg.create.repository',
1454 'hg.create.repository',
1450 'hg.fork.repository',
1455 'hg.fork.repository',
1451 'hg.register.manual_activate',
1456 'hg.register.manual_activate',
1452 'hg.extern_activate.auto',
1457 'hg.extern_activate.auto',
1453 ]
1458 ]
1454
1459
1455 # defines which permissions are more important higher the more important
1460 # defines which permissions are more important higher the more important
1456 # Weight defines which permissions are more important.
1461 # Weight defines which permissions are more important.
1457 # The higher number the more important.
1462 # The higher number the more important.
1458 PERM_WEIGHTS = {
1463 PERM_WEIGHTS = {
1459 'repository.none': 0,
1464 'repository.none': 0,
1460 'repository.read': 1,
1465 'repository.read': 1,
1461 'repository.write': 3,
1466 'repository.write': 3,
1462 'repository.admin': 4,
1467 'repository.admin': 4,
1463
1468
1464 'group.none': 0,
1469 'group.none': 0,
1465 'group.read': 1,
1470 'group.read': 1,
1466 'group.write': 3,
1471 'group.write': 3,
1467 'group.admin': 4,
1472 'group.admin': 4,
1468
1473
1469 'usergroup.none': 0,
1474 'usergroup.none': 0,
1470 'usergroup.read': 1,
1475 'usergroup.read': 1,
1471 'usergroup.write': 3,
1476 'usergroup.write': 3,
1472 'usergroup.admin': 4,
1477 'usergroup.admin': 4,
1473 'hg.repogroup.create.false': 0,
1478 'hg.repogroup.create.false': 0,
1474 'hg.repogroup.create.true': 1,
1479 'hg.repogroup.create.true': 1,
1475
1480
1476 'hg.usergroup.create.false': 0,
1481 'hg.usergroup.create.false': 0,
1477 'hg.usergroup.create.true': 1,
1482 'hg.usergroup.create.true': 1,
1478
1483
1479 'hg.fork.none': 0,
1484 'hg.fork.none': 0,
1480 'hg.fork.repository': 1,
1485 'hg.fork.repository': 1,
1481 'hg.create.none': 0,
1486 'hg.create.none': 0,
1482 'hg.create.repository': 1
1487 'hg.create.repository': 1
1483 }
1488 }
1484
1489
1485 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1490 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1486 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1491 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1487 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1492 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1488
1493
1489 def __unicode__(self):
1494 def __unicode__(self):
1490 return u"<%s('%s:%s')>" % (
1495 return u"<%s('%s:%s')>" % (
1491 self.__class__.__name__, self.permission_id, self.permission_name
1496 self.__class__.__name__, self.permission_id, self.permission_name
1492 )
1497 )
1493
1498
1494 @classmethod
1499 @classmethod
1495 def get_by_key(cls, key):
1500 def get_by_key(cls, key):
1496 return cls.query().filter(cls.permission_name == key).scalar()
1501 return cls.query().filter(cls.permission_name == key).scalar()
1497
1502
1498 @classmethod
1503 @classmethod
1499 def get_default_perms(cls, default_user_id):
1504 def get_default_perms(cls, default_user_id):
1500 q = Session().query(UserRepoToPerm, Repository, cls)\
1505 q = Session().query(UserRepoToPerm, Repository, cls)\
1501 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1506 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1502 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1507 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1503 .filter(UserRepoToPerm.user_id == default_user_id)
1508 .filter(UserRepoToPerm.user_id == default_user_id)
1504
1509
1505 return q.all()
1510 return q.all()
1506
1511
1507 @classmethod
1512 @classmethod
1508 def get_default_group_perms(cls, default_user_id):
1513 def get_default_group_perms(cls, default_user_id):
1509 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1514 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1510 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1515 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1511 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1516 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1512 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1517 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1513
1518
1514 return q.all()
1519 return q.all()
1515
1520
1516 @classmethod
1521 @classmethod
1517 def get_default_user_group_perms(cls, default_user_id):
1522 def get_default_user_group_perms(cls, default_user_id):
1518 q = Session().query(UserUserGroupToPerm, UserGroup, cls)\
1523 q = Session().query(UserUserGroupToPerm, UserGroup, cls)\
1519 .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id))\
1524 .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id))\
1520 .join((cls, UserUserGroupToPerm.permission_id == cls.permission_id))\
1525 .join((cls, UserUserGroupToPerm.permission_id == cls.permission_id))\
1521 .filter(UserUserGroupToPerm.user_id == default_user_id)
1526 .filter(UserUserGroupToPerm.user_id == default_user_id)
1522
1527
1523 return q.all()
1528 return q.all()
1524
1529
1525
1530
1526 class UserRepoToPerm(Base, BaseModel):
1531 class UserRepoToPerm(Base, BaseModel):
1527 __tablename__ = 'repo_to_perm'
1532 __tablename__ = 'repo_to_perm'
1528 __table_args__ = (
1533 __table_args__ = (
1529 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1534 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1530 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1535 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1531 'mysql_charset': 'utf8'}
1536 'mysql_charset': 'utf8'}
1532 )
1537 )
1533 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1538 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1534 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1539 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1535 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1540 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1536 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1541 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1537
1542
1538 user = relationship('User')
1543 user = relationship('User')
1539 repository = relationship('Repository')
1544 repository = relationship('Repository')
1540 permission = relationship('Permission')
1545 permission = relationship('Permission')
1541
1546
1542 @classmethod
1547 @classmethod
1543 def create(cls, user, repository, permission):
1548 def create(cls, user, repository, permission):
1544 n = cls()
1549 n = cls()
1545 n.user = user
1550 n.user = user
1546 n.repository = repository
1551 n.repository = repository
1547 n.permission = permission
1552 n.permission = permission
1548 Session().add(n)
1553 Session().add(n)
1549 return n
1554 return n
1550
1555
1551 def __unicode__(self):
1556 def __unicode__(self):
1552 return u'<%s => %s >' % (self.user, self.repository)
1557 return u'<%s => %s >' % (self.user, self.repository)
1553
1558
1554
1559
1555 class UserUserGroupToPerm(Base, BaseModel):
1560 class UserUserGroupToPerm(Base, BaseModel):
1556 __tablename__ = 'user_user_group_to_perm'
1561 __tablename__ = 'user_user_group_to_perm'
1557 __table_args__ = (
1562 __table_args__ = (
1558 UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
1563 UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
1559 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1564 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1560 'mysql_charset': 'utf8'}
1565 'mysql_charset': 'utf8'}
1561 )
1566 )
1562 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1567 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1563 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1568 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1564 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1569 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1565 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1570 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1566
1571
1567 user = relationship('User')
1572 user = relationship('User')
1568 user_group = relationship('UserGroup')
1573 user_group = relationship('UserGroup')
1569 permission = relationship('Permission')
1574 permission = relationship('Permission')
1570
1575
1571 @classmethod
1576 @classmethod
1572 def create(cls, user, user_group, permission):
1577 def create(cls, user, user_group, permission):
1573 n = cls()
1578 n = cls()
1574 n.user = user
1579 n.user = user
1575 n.user_group = user_group
1580 n.user_group = user_group
1576 n.permission = permission
1581 n.permission = permission
1577 Session().add(n)
1582 Session().add(n)
1578 return n
1583 return n
1579
1584
1580 def __unicode__(self):
1585 def __unicode__(self):
1581 return u'<%s => %s >' % (self.user, self.user_group)
1586 return u'<%s => %s >' % (self.user, self.user_group)
1582
1587
1583
1588
1584 class UserToPerm(Base, BaseModel):
1589 class UserToPerm(Base, BaseModel):
1585 __tablename__ = 'user_to_perm'
1590 __tablename__ = 'user_to_perm'
1586 __table_args__ = (
1591 __table_args__ = (
1587 UniqueConstraint('user_id', 'permission_id'),
1592 UniqueConstraint('user_id', 'permission_id'),
1588 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1593 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1589 'mysql_charset': 'utf8'}
1594 'mysql_charset': 'utf8'}
1590 )
1595 )
1591 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1596 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1592 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1597 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1593 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1598 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1594
1599
1595 user = relationship('User')
1600 user = relationship('User')
1596 permission = relationship('Permission', lazy='joined')
1601 permission = relationship('Permission', lazy='joined')
1597
1602
1598 def __unicode__(self):
1603 def __unicode__(self):
1599 return u'<%s => %s >' % (self.user, self.permission)
1604 return u'<%s => %s >' % (self.user, self.permission)
1600
1605
1601
1606
1602 class UserGroupRepoToPerm(Base, BaseModel):
1607 class UserGroupRepoToPerm(Base, BaseModel):
1603 __tablename__ = 'users_group_repo_to_perm'
1608 __tablename__ = 'users_group_repo_to_perm'
1604 __table_args__ = (
1609 __table_args__ = (
1605 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1610 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1606 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1611 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1607 'mysql_charset': 'utf8'}
1612 'mysql_charset': 'utf8'}
1608 )
1613 )
1609 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1614 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1610 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1615 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1611 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1616 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1612 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1617 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1613
1618
1614 users_group = relationship('UserGroup')
1619 users_group = relationship('UserGroup')
1615 permission = relationship('Permission')
1620 permission = relationship('Permission')
1616 repository = relationship('Repository')
1621 repository = relationship('Repository')
1617
1622
1618 @classmethod
1623 @classmethod
1619 def create(cls, users_group, repository, permission):
1624 def create(cls, users_group, repository, permission):
1620 n = cls()
1625 n = cls()
1621 n.users_group = users_group
1626 n.users_group = users_group
1622 n.repository = repository
1627 n.repository = repository
1623 n.permission = permission
1628 n.permission = permission
1624 Session().add(n)
1629 Session().add(n)
1625 return n
1630 return n
1626
1631
1627 def __unicode__(self):
1632 def __unicode__(self):
1628 return u'<UserGroupRepoToPerm:%s => %s >' % (self.users_group, self.repository)
1633 return u'<UserGroupRepoToPerm:%s => %s >' % (self.users_group, self.repository)
1629
1634
1630
1635
1631 class UserGroupUserGroupToPerm(Base, BaseModel):
1636 class UserGroupUserGroupToPerm(Base, BaseModel):
1632 __tablename__ = 'user_group_user_group_to_perm'
1637 __tablename__ = 'user_group_user_group_to_perm'
1633 __table_args__ = (
1638 __table_args__ = (
1634 UniqueConstraint('target_user_group_id', 'user_group_id', 'permission_id'),
1639 UniqueConstraint('target_user_group_id', 'user_group_id', 'permission_id'),
1635 CheckConstraint('target_user_group_id != user_group_id'),
1640 CheckConstraint('target_user_group_id != user_group_id'),
1636 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1641 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1637 'mysql_charset': 'utf8'}
1642 'mysql_charset': 'utf8'}
1638 )
1643 )
1639 user_group_user_group_to_perm_id = Column("user_group_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1644 user_group_user_group_to_perm_id = Column("user_group_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1640 target_user_group_id = Column("target_user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1645 target_user_group_id = Column("target_user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1641 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1646 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1642 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1647 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1643
1648
1644 target_user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
1649 target_user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
1645 user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
1650 user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
1646 permission = relationship('Permission')
1651 permission = relationship('Permission')
1647
1652
1648 @classmethod
1653 @classmethod
1649 def create(cls, target_user_group, user_group, permission):
1654 def create(cls, target_user_group, user_group, permission):
1650 n = cls()
1655 n = cls()
1651 n.target_user_group = target_user_group
1656 n.target_user_group = target_user_group
1652 n.user_group = user_group
1657 n.user_group = user_group
1653 n.permission = permission
1658 n.permission = permission
1654 Session().add(n)
1659 Session().add(n)
1655 return n
1660 return n
1656
1661
1657 def __unicode__(self):
1662 def __unicode__(self):
1658 return u'<UserGroupUserGroup:%s => %s >' % (self.target_user_group, self.user_group)
1663 return u'<UserGroupUserGroup:%s => %s >' % (self.target_user_group, self.user_group)
1659
1664
1660
1665
1661 class UserGroupToPerm(Base, BaseModel):
1666 class UserGroupToPerm(Base, BaseModel):
1662 __tablename__ = 'users_group_to_perm'
1667 __tablename__ = 'users_group_to_perm'
1663 __table_args__ = (
1668 __table_args__ = (
1664 UniqueConstraint('users_group_id', 'permission_id',),
1669 UniqueConstraint('users_group_id', 'permission_id',),
1665 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1670 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1666 'mysql_charset': 'utf8'}
1671 'mysql_charset': 'utf8'}
1667 )
1672 )
1668 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1673 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1669 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1674 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1670 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1675 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1671
1676
1672 users_group = relationship('UserGroup')
1677 users_group = relationship('UserGroup')
1673 permission = relationship('Permission')
1678 permission = relationship('Permission')
1674
1679
1675
1680
1676 class UserRepoGroupToPerm(Base, BaseModel):
1681 class UserRepoGroupToPerm(Base, BaseModel):
1677 __tablename__ = 'user_repo_group_to_perm'
1682 __tablename__ = 'user_repo_group_to_perm'
1678 __table_args__ = (
1683 __table_args__ = (
1679 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1684 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1680 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1685 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1681 'mysql_charset': 'utf8'}
1686 'mysql_charset': 'utf8'}
1682 )
1687 )
1683
1688
1684 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1689 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1685 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1690 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1686 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1691 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1687 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1692 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1688
1693
1689 user = relationship('User')
1694 user = relationship('User')
1690 group = relationship('RepoGroup')
1695 group = relationship('RepoGroup')
1691 permission = relationship('Permission')
1696 permission = relationship('Permission')
1692
1697
1693
1698
1694 class UserGroupRepoGroupToPerm(Base, BaseModel):
1699 class UserGroupRepoGroupToPerm(Base, BaseModel):
1695 __tablename__ = 'users_group_repo_group_to_perm'
1700 __tablename__ = 'users_group_repo_group_to_perm'
1696 __table_args__ = (
1701 __table_args__ = (
1697 UniqueConstraint('users_group_id', 'group_id'),
1702 UniqueConstraint('users_group_id', 'group_id'),
1698 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1703 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1699 'mysql_charset': 'utf8'}
1704 'mysql_charset': 'utf8'}
1700 )
1705 )
1701
1706
1702 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)
1707 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)
1703 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1708 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1704 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1709 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1705 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1710 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1706
1711
1707 users_group = relationship('UserGroup')
1712 users_group = relationship('UserGroup')
1708 permission = relationship('Permission')
1713 permission = relationship('Permission')
1709 group = relationship('RepoGroup')
1714 group = relationship('RepoGroup')
1710
1715
1711
1716
1712 class Statistics(Base, BaseModel):
1717 class Statistics(Base, BaseModel):
1713 __tablename__ = 'statistics'
1718 __tablename__ = 'statistics'
1714 __table_args__ = (
1719 __table_args__ = (
1715 UniqueConstraint('repository_id'),
1720 UniqueConstraint('repository_id'),
1716 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1721 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1717 'mysql_charset': 'utf8'}
1722 'mysql_charset': 'utf8'}
1718 )
1723 )
1719 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1724 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1720 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1725 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1721 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1726 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1722 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1727 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1723 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1728 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1724 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1729 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1725
1730
1726 repository = relationship('Repository', single_parent=True)
1731 repository = relationship('Repository', single_parent=True)
1727
1732
1728
1733
1729 class UserFollowing(Base, BaseModel):
1734 class UserFollowing(Base, BaseModel):
1730 __tablename__ = 'user_followings'
1735 __tablename__ = 'user_followings'
1731 __table_args__ = (
1736 __table_args__ = (
1732 UniqueConstraint('user_id', 'follows_repository_id'),
1737 UniqueConstraint('user_id', 'follows_repository_id'),
1733 UniqueConstraint('user_id', 'follows_user_id'),
1738 UniqueConstraint('user_id', 'follows_user_id'),
1734 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1739 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1735 'mysql_charset': 'utf8'}
1740 'mysql_charset': 'utf8'}
1736 )
1741 )
1737
1742
1738 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1743 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1739 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1744 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1740 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1745 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1741 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1746 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1742 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1747 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1743
1748
1744 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1749 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1745
1750
1746 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1751 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1747 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1752 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1748
1753
1749 @classmethod
1754 @classmethod
1750 def get_repo_followers(cls, repo_id):
1755 def get_repo_followers(cls, repo_id):
1751 return cls.query().filter(cls.follows_repo_id == repo_id)
1756 return cls.query().filter(cls.follows_repo_id == repo_id)
1752
1757
1753
1758
1754 class CacheInvalidation(Base, BaseModel):
1759 class CacheInvalidation(Base, BaseModel):
1755 __tablename__ = 'cache_invalidation'
1760 __tablename__ = 'cache_invalidation'
1756 __table_args__ = (
1761 __table_args__ = (
1757 UniqueConstraint('cache_key'),
1762 UniqueConstraint('cache_key'),
1758 Index('key_idx', 'cache_key'),
1763 Index('key_idx', 'cache_key'),
1759 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1764 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1760 'mysql_charset': 'utf8'},
1765 'mysql_charset': 'utf8'},
1761 )
1766 )
1762 # cache_id, not used
1767 # cache_id, not used
1763 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1768 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1764 # cache_key as created by _get_cache_key
1769 # cache_key as created by _get_cache_key
1765 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1770 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1766 # cache_args is a repo_name
1771 # cache_args is a repo_name
1767 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1772 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1768 # instance sets cache_active True when it is caching,
1773 # instance sets cache_active True when it is caching,
1769 # other instances set cache_active to False to indicate that this cache is invalid
1774 # other instances set cache_active to False to indicate that this cache is invalid
1770 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1775 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1771
1776
1772 def __init__(self, cache_key, repo_name=''):
1777 def __init__(self, cache_key, repo_name=''):
1773 self.cache_key = cache_key
1778 self.cache_key = cache_key
1774 self.cache_args = repo_name
1779 self.cache_args = repo_name
1775 self.cache_active = False
1780 self.cache_active = False
1776
1781
1777 def __unicode__(self):
1782 def __unicode__(self):
1778 return u"<%s('%s:%s[%s]')>" % (self.__class__.__name__,
1783 return u"<%s('%s:%s[%s]')>" % (self.__class__.__name__,
1779 self.cache_id, self.cache_key, self.cache_active)
1784 self.cache_id, self.cache_key, self.cache_active)
1780
1785
1781 def _cache_key_partition(self):
1786 def _cache_key_partition(self):
1782 prefix, repo_name, suffix = self.cache_key.partition(self.cache_args)
1787 prefix, repo_name, suffix = self.cache_key.partition(self.cache_args)
1783 return prefix, repo_name, suffix
1788 return prefix, repo_name, suffix
1784
1789
1785 def get_prefix(self):
1790 def get_prefix(self):
1786 """
1791 """
1787 get prefix that might have been used in _get_cache_key to
1792 get prefix that might have been used in _get_cache_key to
1788 generate self.cache_key. Only used for informational purposes
1793 generate self.cache_key. Only used for informational purposes
1789 in repo_edit.html.
1794 in repo_edit.html.
1790 """
1795 """
1791 # prefix, repo_name, suffix
1796 # prefix, repo_name, suffix
1792 return self._cache_key_partition()[0]
1797 return self._cache_key_partition()[0]
1793
1798
1794 def get_suffix(self):
1799 def get_suffix(self):
1795 """
1800 """
1796 get suffix that might have been used in _get_cache_key to
1801 get suffix that might have been used in _get_cache_key to
1797 generate self.cache_key. Only used for informational purposes
1802 generate self.cache_key. Only used for informational purposes
1798 in repo_edit.html.
1803 in repo_edit.html.
1799 """
1804 """
1800 # prefix, repo_name, suffix
1805 # prefix, repo_name, suffix
1801 return self._cache_key_partition()[2]
1806 return self._cache_key_partition()[2]
1802
1807
1803 @classmethod
1808 @classmethod
1804 def clear_cache(cls):
1809 def clear_cache(cls):
1805 """
1810 """
1806 Delete all cache keys from database.
1811 Delete all cache keys from database.
1807 Should only be run when all instances are down and all entries thus stale.
1812 Should only be run when all instances are down and all entries thus stale.
1808 """
1813 """
1809 cls.query().delete()
1814 cls.query().delete()
1810 Session().commit()
1815 Session().commit()
1811
1816
1812 @classmethod
1817 @classmethod
1813 def _get_cache_key(cls, key):
1818 def _get_cache_key(cls, key):
1814 """
1819 """
1815 Wrapper for generating a unique cache key for this instance and "key".
1820 Wrapper for generating a unique cache key for this instance and "key".
1816 key must / will start with a repo_name which will be stored in .cache_args .
1821 key must / will start with a repo_name which will be stored in .cache_args .
1817 """
1822 """
1818 import rhodecode
1823 import rhodecode
1819 prefix = rhodecode.CONFIG.get('instance_id', '')
1824 prefix = rhodecode.CONFIG.get('instance_id', '')
1820 return "%s%s" % (prefix, key)
1825 return "%s%s" % (prefix, key)
1821
1826
1822 @classmethod
1827 @classmethod
1823 def set_invalidate(cls, repo_name):
1828 def set_invalidate(cls, repo_name):
1824 """
1829 """
1825 Mark all caches of a repo as invalid in the database.
1830 Mark all caches of a repo as invalid in the database.
1826 """
1831 """
1827 inv_objs = Session().query(cls).filter(cls.cache_args == repo_name).all()
1832 inv_objs = Session().query(cls).filter(cls.cache_args == repo_name).all()
1828
1833
1829 try:
1834 try:
1830 for inv_obj in inv_objs:
1835 for inv_obj in inv_objs:
1831 log.debug('marking %s key for invalidation based on repo_name=%s'
1836 log.debug('marking %s key for invalidation based on repo_name=%s'
1832 % (inv_obj, safe_str(repo_name)))
1837 % (inv_obj, safe_str(repo_name)))
1833 inv_obj.cache_active = False
1838 inv_obj.cache_active = False
1834 Session().add(inv_obj)
1839 Session().add(inv_obj)
1835 Session().commit()
1840 Session().commit()
1836 except Exception:
1841 except Exception:
1837 log.error(traceback.format_exc())
1842 log.error(traceback.format_exc())
1838 Session().rollback()
1843 Session().rollback()
1839
1844
1840 @classmethod
1845 @classmethod
1841 def test_and_set_valid(cls, repo_name, kind, valid_cache_keys=None):
1846 def test_and_set_valid(cls, repo_name, kind, valid_cache_keys=None):
1842 """
1847 """
1843 Mark this cache key as active and currently cached.
1848 Mark this cache key as active and currently cached.
1844 Return True if the existing cache registration still was valid.
1849 Return True if the existing cache registration still was valid.
1845 Return False to indicate that it had been invalidated and caches should be refreshed.
1850 Return False to indicate that it had been invalidated and caches should be refreshed.
1846 """
1851 """
1847
1852
1848 key = (repo_name + '_' + kind) if kind else repo_name
1853 key = (repo_name + '_' + kind) if kind else repo_name
1849 cache_key = cls._get_cache_key(key)
1854 cache_key = cls._get_cache_key(key)
1850
1855
1851 if valid_cache_keys and cache_key in valid_cache_keys:
1856 if valid_cache_keys and cache_key in valid_cache_keys:
1852 return True
1857 return True
1853
1858
1854 try:
1859 try:
1855 inv_obj = cls.query().filter(cls.cache_key == cache_key).scalar()
1860 inv_obj = cls.query().filter(cls.cache_key == cache_key).scalar()
1856 if not inv_obj:
1861 if not inv_obj:
1857 inv_obj = CacheInvalidation(cache_key, repo_name)
1862 inv_obj = CacheInvalidation(cache_key, repo_name)
1858 was_valid = inv_obj.cache_active
1863 was_valid = inv_obj.cache_active
1859 inv_obj.cache_active = True
1864 inv_obj.cache_active = True
1860 Session().add(inv_obj)
1865 Session().add(inv_obj)
1861 Session().commit()
1866 Session().commit()
1862 return was_valid
1867 return was_valid
1863 except Exception:
1868 except Exception:
1864 log.error(traceback.format_exc())
1869 log.error(traceback.format_exc())
1865 Session().rollback()
1870 Session().rollback()
1866 return False
1871 return False
1867
1872
1868 @classmethod
1873 @classmethod
1869 def get_valid_cache_keys(cls):
1874 def get_valid_cache_keys(cls):
1870 """
1875 """
1871 Return opaque object with information of which caches still are valid
1876 Return opaque object with information of which caches still are valid
1872 and can be used without checking for invalidation.
1877 and can be used without checking for invalidation.
1873 """
1878 """
1874 return set(inv_obj.cache_key for inv_obj in cls.query().filter(cls.cache_active).all())
1879 return set(inv_obj.cache_key for inv_obj in cls.query().filter(cls.cache_active).all())
1875
1880
1876
1881
1877 class ChangesetComment(Base, BaseModel):
1882 class ChangesetComment(Base, BaseModel):
1878 __tablename__ = 'changeset_comments'
1883 __tablename__ = 'changeset_comments'
1879 __table_args__ = (
1884 __table_args__ = (
1880 Index('cc_revision_idx', 'revision'),
1885 Index('cc_revision_idx', 'revision'),
1881 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1886 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1882 'mysql_charset': 'utf8'},
1887 'mysql_charset': 'utf8'},
1883 )
1888 )
1884 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1889 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1885 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1890 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1886 revision = Column('revision', String(40), nullable=True)
1891 revision = Column('revision', String(40), nullable=True)
1887 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1892 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1888 line_no = Column('line_no', Unicode(10), nullable=True)
1893 line_no = Column('line_no', Unicode(10), nullable=True)
1889 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1894 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1890 f_path = Column('f_path', Unicode(1000), nullable=True)
1895 f_path = Column('f_path', Unicode(1000), nullable=True)
1891 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1896 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1892 text = Column('text', UnicodeText(25000), nullable=False)
1897 text = Column('text', UnicodeText(25000), nullable=False)
1893 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1898 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1894 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1899 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1895
1900
1896 author = relationship('User', lazy='joined')
1901 author = relationship('User', lazy='joined')
1897 repo = relationship('Repository')
1902 repo = relationship('Repository')
1898 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1903 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1899 pull_request = relationship('PullRequest', lazy='joined')
1904 pull_request = relationship('PullRequest', lazy='joined')
1900
1905
1901 @classmethod
1906 @classmethod
1902 def get_users(cls, revision=None, pull_request_id=None):
1907 def get_users(cls, revision=None, pull_request_id=None):
1903 """
1908 """
1904 Returns user associated with this ChangesetComment. ie those
1909 Returns user associated with this ChangesetComment. ie those
1905 who actually commented
1910 who actually commented
1906
1911
1907 :param cls:
1912 :param cls:
1908 :param revision:
1913 :param revision:
1909 """
1914 """
1910 q = Session().query(User)\
1915 q = Session().query(User)\
1911 .join(ChangesetComment.author)
1916 .join(ChangesetComment.author)
1912 if revision:
1917 if revision:
1913 q = q.filter(cls.revision == revision)
1918 q = q.filter(cls.revision == revision)
1914 elif pull_request_id:
1919 elif pull_request_id:
1915 q = q.filter(cls.pull_request_id == pull_request_id)
1920 q = q.filter(cls.pull_request_id == pull_request_id)
1916 return q.all()
1921 return q.all()
1917
1922
1918
1923
1919 class ChangesetStatus(Base, BaseModel):
1924 class ChangesetStatus(Base, BaseModel):
1920 __tablename__ = 'changeset_statuses'
1925 __tablename__ = 'changeset_statuses'
1921 __table_args__ = (
1926 __table_args__ = (
1922 Index('cs_revision_idx', 'revision'),
1927 Index('cs_revision_idx', 'revision'),
1923 Index('cs_version_idx', 'version'),
1928 Index('cs_version_idx', 'version'),
1924 UniqueConstraint('repo_id', 'revision', 'version'),
1929 UniqueConstraint('repo_id', 'revision', 'version'),
1925 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1930 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1926 'mysql_charset': 'utf8'}
1931 'mysql_charset': 'utf8'}
1927 )
1932 )
1928 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1933 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1929 STATUS_APPROVED = 'approved'
1934 STATUS_APPROVED = 'approved'
1930 STATUS_REJECTED = 'rejected'
1935 STATUS_REJECTED = 'rejected'
1931 STATUS_UNDER_REVIEW = 'under_review'
1936 STATUS_UNDER_REVIEW = 'under_review'
1932
1937
1933 STATUSES = [
1938 STATUSES = [
1934 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1939 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1935 (STATUS_APPROVED, _("Approved")),
1940 (STATUS_APPROVED, _("Approved")),
1936 (STATUS_REJECTED, _("Rejected")),
1941 (STATUS_REJECTED, _("Rejected")),
1937 (STATUS_UNDER_REVIEW, _("Under Review")),
1942 (STATUS_UNDER_REVIEW, _("Under Review")),
1938 ]
1943 ]
1939
1944
1940 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1945 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1941 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1946 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1942 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1947 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1943 revision = Column('revision', String(40), nullable=False)
1948 revision = Column('revision', String(40), nullable=False)
1944 status = Column('status', String(128), nullable=False, default=DEFAULT)
1949 status = Column('status', String(128), nullable=False, default=DEFAULT)
1945 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1950 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1946 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1951 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1947 version = Column('version', Integer(), nullable=False, default=0)
1952 version = Column('version', Integer(), nullable=False, default=0)
1948 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1953 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1949
1954
1950 author = relationship('User', lazy='joined')
1955 author = relationship('User', lazy='joined')
1951 repo = relationship('Repository')
1956 repo = relationship('Repository')
1952 comment = relationship('ChangesetComment', lazy='joined')
1957 comment = relationship('ChangesetComment', lazy='joined')
1953 pull_request = relationship('PullRequest', lazy='joined')
1958 pull_request = relationship('PullRequest', lazy='joined')
1954
1959
1955 def __unicode__(self):
1960 def __unicode__(self):
1956 return u"<%s('%s:%s')>" % (
1961 return u"<%s('%s:%s')>" % (
1957 self.__class__.__name__,
1962 self.__class__.__name__,
1958 self.status, self.author
1963 self.status, self.author
1959 )
1964 )
1960
1965
1961 @classmethod
1966 @classmethod
1962 def get_status_lbl(cls, value):
1967 def get_status_lbl(cls, value):
1963 return dict(cls.STATUSES).get(value)
1968 return dict(cls.STATUSES).get(value)
1964
1969
1965 @property
1970 @property
1966 def status_lbl(self):
1971 def status_lbl(self):
1967 return ChangesetStatus.get_status_lbl(self.status)
1972 return ChangesetStatus.get_status_lbl(self.status)
1968
1973
1969
1974
1970 class PullRequest(Base, BaseModel):
1975 class PullRequest(Base, BaseModel):
1971 __tablename__ = 'pull_requests'
1976 __tablename__ = 'pull_requests'
1972 __table_args__ = (
1977 __table_args__ = (
1973 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1978 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1974 'mysql_charset': 'utf8'},
1979 'mysql_charset': 'utf8'},
1975 )
1980 )
1976
1981
1977 STATUS_NEW = u'new'
1982 STATUS_NEW = u'new'
1978 STATUS_OPEN = u'open'
1983 STATUS_OPEN = u'open'
1979 STATUS_CLOSED = u'closed'
1984 STATUS_CLOSED = u'closed'
1980
1985
1981 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1986 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1982 title = Column('title', Unicode(256), nullable=True)
1987 title = Column('title', Unicode(256), nullable=True)
1983 description = Column('description', UnicodeText(10240), nullable=True)
1988 description = Column('description', UnicodeText(10240), nullable=True)
1984 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1989 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1985 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1990 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1986 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1991 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1987 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1992 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1988 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1993 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1989 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1994 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1990 org_ref = Column('org_ref', Unicode(256), nullable=False)
1995 org_ref = Column('org_ref', Unicode(256), nullable=False)
1991 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1996 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1992 other_ref = Column('other_ref', Unicode(256), nullable=False)
1997 other_ref = Column('other_ref', Unicode(256), nullable=False)
1993
1998
1994 @hybrid_property
1999 @hybrid_property
1995 def revisions(self):
2000 def revisions(self):
1996 return self._revisions.split(':')
2001 return self._revisions.split(':')
1997
2002
1998 @revisions.setter
2003 @revisions.setter
1999 def revisions(self, val):
2004 def revisions(self, val):
2000 self._revisions = ':'.join(val)
2005 self._revisions = ':'.join(val)
2001
2006
2002 @property
2007 @property
2003 def org_ref_parts(self):
2008 def org_ref_parts(self):
2004 return self.org_ref.split(':')
2009 return self.org_ref.split(':')
2005
2010
2006 @property
2011 @property
2007 def other_ref_parts(self):
2012 def other_ref_parts(self):
2008 return self.other_ref.split(':')
2013 return self.other_ref.split(':')
2009
2014
2010 author = relationship('User', lazy='joined')
2015 author = relationship('User', lazy='joined')
2011 reviewers = relationship('PullRequestReviewers',
2016 reviewers = relationship('PullRequestReviewers',
2012 cascade="all, delete, delete-orphan")
2017 cascade="all, delete, delete-orphan")
2013 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
2018 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
2014 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
2019 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
2015 statuses = relationship('ChangesetStatus')
2020 statuses = relationship('ChangesetStatus')
2016 comments = relationship('ChangesetComment',
2021 comments = relationship('ChangesetComment',
2017 cascade="all, delete, delete-orphan")
2022 cascade="all, delete, delete-orphan")
2018
2023
2019 def is_closed(self):
2024 def is_closed(self):
2020 return self.status == self.STATUS_CLOSED
2025 return self.status == self.STATUS_CLOSED
2021
2026
2022 @property
2027 @property
2023 def last_review_status(self):
2028 def last_review_status(self):
2024 return self.statuses[-1].status if self.statuses else ''
2029 return self.statuses[-1].status if self.statuses else ''
2025
2030
2026 def __json__(self):
2031 def __json__(self):
2027 return dict(
2032 return dict(
2028 revisions=self.revisions
2033 revisions=self.revisions
2029 )
2034 )
2030
2035
2031
2036
2032 class PullRequestReviewers(Base, BaseModel):
2037 class PullRequestReviewers(Base, BaseModel):
2033 __tablename__ = 'pull_request_reviewers'
2038 __tablename__ = 'pull_request_reviewers'
2034 __table_args__ = (
2039 __table_args__ = (
2035 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2040 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2036 'mysql_charset': 'utf8'},
2041 'mysql_charset': 'utf8'},
2037 )
2042 )
2038
2043
2039 def __init__(self, user=None, pull_request=None):
2044 def __init__(self, user=None, pull_request=None):
2040 self.user = user
2045 self.user = user
2041 self.pull_request = pull_request
2046 self.pull_request = pull_request
2042
2047
2043 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
2048 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
2044 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
2049 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
2045 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
2050 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
2046
2051
2047 user = relationship('User')
2052 user = relationship('User')
2048 pull_request = relationship('PullRequest')
2053 pull_request = relationship('PullRequest')
2049
2054
2050
2055
2051 class Notification(Base, BaseModel):
2056 class Notification(Base, BaseModel):
2052 __tablename__ = 'notifications'
2057 __tablename__ = 'notifications'
2053 __table_args__ = (
2058 __table_args__ = (
2054 Index('notification_type_idx', 'type'),
2059 Index('notification_type_idx', 'type'),
2055 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2060 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2056 'mysql_charset': 'utf8'},
2061 'mysql_charset': 'utf8'},
2057 )
2062 )
2058
2063
2059 TYPE_CHANGESET_COMMENT = u'cs_comment'
2064 TYPE_CHANGESET_COMMENT = u'cs_comment'
2060 TYPE_MESSAGE = u'message'
2065 TYPE_MESSAGE = u'message'
2061 TYPE_MENTION = u'mention'
2066 TYPE_MENTION = u'mention'
2062 TYPE_REGISTRATION = u'registration'
2067 TYPE_REGISTRATION = u'registration'
2063 TYPE_PULL_REQUEST = u'pull_request'
2068 TYPE_PULL_REQUEST = u'pull_request'
2064 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
2069 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
2065
2070
2066 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
2071 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
2067 subject = Column('subject', Unicode(512), nullable=True)
2072 subject = Column('subject', Unicode(512), nullable=True)
2068 body = Column('body', UnicodeText(50000), nullable=True)
2073 body = Column('body', UnicodeText(50000), nullable=True)
2069 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
2074 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
2070 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
2075 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
2071 type_ = Column('type', Unicode(256))
2076 type_ = Column('type', Unicode(256))
2072
2077
2073 created_by_user = relationship('User')
2078 created_by_user = relationship('User')
2074 notifications_to_users = relationship('UserNotification', lazy='joined',
2079 notifications_to_users = relationship('UserNotification', lazy='joined',
2075 cascade="all, delete, delete-orphan")
2080 cascade="all, delete, delete-orphan")
2076
2081
2077 @property
2082 @property
2078 def recipients(self):
2083 def recipients(self):
2079 return [x.user for x in UserNotification.query()\
2084 return [x.user for x in UserNotification.query()\
2080 .filter(UserNotification.notification == self)\
2085 .filter(UserNotification.notification == self)\
2081 .order_by(UserNotification.user_id.asc()).all()]
2086 .order_by(UserNotification.user_id.asc()).all()]
2082
2087
2083 @classmethod
2088 @classmethod
2084 def create(cls, created_by, subject, body, recipients, type_=None):
2089 def create(cls, created_by, subject, body, recipients, type_=None):
2085 if type_ is None:
2090 if type_ is None:
2086 type_ = Notification.TYPE_MESSAGE
2091 type_ = Notification.TYPE_MESSAGE
2087
2092
2088 notification = cls()
2093 notification = cls()
2089 notification.created_by_user = created_by
2094 notification.created_by_user = created_by
2090 notification.subject = subject
2095 notification.subject = subject
2091 notification.body = body
2096 notification.body = body
2092 notification.type_ = type_
2097 notification.type_ = type_
2093 notification.created_on = datetime.datetime.now()
2098 notification.created_on = datetime.datetime.now()
2094
2099
2095 for u in recipients:
2100 for u in recipients:
2096 assoc = UserNotification()
2101 assoc = UserNotification()
2097 assoc.notification = notification
2102 assoc.notification = notification
2098 u.notifications.append(assoc)
2103 u.notifications.append(assoc)
2099 Session().add(notification)
2104 Session().add(notification)
2100 return notification
2105 return notification
2101
2106
2102 @property
2107 @property
2103 def description(self):
2108 def description(self):
2104 from rhodecode.model.notification import NotificationModel
2109 from rhodecode.model.notification import NotificationModel
2105 return NotificationModel().make_description(self)
2110 return NotificationModel().make_description(self)
2106
2111
2107
2112
2108 class UserNotification(Base, BaseModel):
2113 class UserNotification(Base, BaseModel):
2109 __tablename__ = 'user_to_notification'
2114 __tablename__ = 'user_to_notification'
2110 __table_args__ = (
2115 __table_args__ = (
2111 UniqueConstraint('user_id', 'notification_id'),
2116 UniqueConstraint('user_id', 'notification_id'),
2112 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2117 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2113 'mysql_charset': 'utf8'}
2118 'mysql_charset': 'utf8'}
2114 )
2119 )
2115 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
2120 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
2116 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
2121 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
2117 read = Column('read', Boolean, default=False)
2122 read = Column('read', Boolean, default=False)
2118 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
2123 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
2119
2124
2120 user = relationship('User', lazy="joined")
2125 user = relationship('User', lazy="joined")
2121 notification = relationship('Notification', lazy="joined",
2126 notification = relationship('Notification', lazy="joined",
2122 order_by=lambda: Notification.created_on.desc(),)
2127 order_by=lambda: Notification.created_on.desc(),)
2123
2128
2124 def mark_as_read(self):
2129 def mark_as_read(self):
2125 self.read = True
2130 self.read = True
2126 Session().add(self)
2131 Session().add(self)
2127
2132
2128
2133
2129 class Gist(Base, BaseModel):
2134 class Gist(Base, BaseModel):
2130 __tablename__ = 'gists'
2135 __tablename__ = 'gists'
2131 __table_args__ = (
2136 __table_args__ = (
2132 Index('g_gist_access_id_idx', 'gist_access_id'),
2137 Index('g_gist_access_id_idx', 'gist_access_id'),
2133 Index('g_created_on_idx', 'created_on'),
2138 Index('g_created_on_idx', 'created_on'),
2134 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2139 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2135 'mysql_charset': 'utf8'}
2140 'mysql_charset': 'utf8'}
2136 )
2141 )
2137 GIST_PUBLIC = u'public'
2142 GIST_PUBLIC = u'public'
2138 GIST_PRIVATE = u'private'
2143 GIST_PRIVATE = u'private'
2139
2144
2140 gist_id = Column('gist_id', Integer(), primary_key=True)
2145 gist_id = Column('gist_id', Integer(), primary_key=True)
2141 gist_access_id = Column('gist_access_id', Unicode(250))
2146 gist_access_id = Column('gist_access_id', Unicode(250))
2142 gist_description = Column('gist_description', UnicodeText(1024))
2147 gist_description = Column('gist_description', UnicodeText(1024))
2143 gist_owner = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=True)
2148 gist_owner = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=True)
2144 gist_expires = Column('gist_expires', Float(), nullable=False)
2149 gist_expires = Column('gist_expires', Float(), nullable=False)
2145 gist_type = Column('gist_type', Unicode(128), nullable=False)
2150 gist_type = Column('gist_type', Unicode(128), nullable=False)
2146 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
2151 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
2147 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
2152 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
2148
2153
2149 owner = relationship('User')
2154 owner = relationship('User')
2150
2155
2151 @classmethod
2156 @classmethod
2152 def get_or_404(cls, id_):
2157 def get_or_404(cls, id_):
2153 res = cls.query().filter(cls.gist_access_id == id_).scalar()
2158 res = cls.query().filter(cls.gist_access_id == id_).scalar()
2154 if not res:
2159 if not res:
2155 raise HTTPNotFound
2160 raise HTTPNotFound
2156 return res
2161 return res
2157
2162
2158 @classmethod
2163 @classmethod
2159 def get_by_access_id(cls, gist_access_id):
2164 def get_by_access_id(cls, gist_access_id):
2160 return cls.query().filter(cls.gist_access_id == gist_access_id).scalar()
2165 return cls.query().filter(cls.gist_access_id == gist_access_id).scalar()
2161
2166
2162 def gist_url(self):
2167 def gist_url(self):
2163 import rhodecode
2168 import rhodecode
2164 alias_url = rhodecode.CONFIG.get('gist_alias_url')
2169 alias_url = rhodecode.CONFIG.get('gist_alias_url')
2165 if alias_url:
2170 if alias_url:
2166 return alias_url.replace('{gistid}', self.gist_access_id)
2171 return alias_url.replace('{gistid}', self.gist_access_id)
2167
2172
2168 from pylons import url
2173 from pylons import url
2169 return url('gist', gist_id=self.gist_access_id, qualified=True)
2174 return url('gist', gist_id=self.gist_access_id, qualified=True)
2170
2175
2171 @classmethod
2176 @classmethod
2172 def base_path(cls):
2177 def base_path(cls):
2173 """
2178 """
2174 Returns base path when all gists are stored
2179 Returns base path when all gists are stored
2175
2180
2176 :param cls:
2181 :param cls:
2177 """
2182 """
2178 from rhodecode.model.gist import GIST_STORE_LOC
2183 from rhodecode.model.gist import GIST_STORE_LOC
2179 q = Session().query(RhodeCodeUi)\
2184 q = Session().query(RhodeCodeUi)\
2180 .filter(RhodeCodeUi.ui_key == URL_SEP)
2185 .filter(RhodeCodeUi.ui_key == URL_SEP)
2181 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
2186 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
2182 return os.path.join(q.one().ui_value, GIST_STORE_LOC)
2187 return os.path.join(q.one().ui_value, GIST_STORE_LOC)
2183
2188
2184 def get_api_data(self):
2189 def get_api_data(self):
2185 """
2190 """
2186 Common function for generating gist related data for API
2191 Common function for generating gist related data for API
2187 """
2192 """
2188 gist = self
2193 gist = self
2189 data = dict(
2194 data = dict(
2190 gist_id=gist.gist_id,
2195 gist_id=gist.gist_id,
2191 type=gist.gist_type,
2196 type=gist.gist_type,
2192 access_id=gist.gist_access_id,
2197 access_id=gist.gist_access_id,
2193 description=gist.gist_description,
2198 description=gist.gist_description,
2194 url=gist.gist_url(),
2199 url=gist.gist_url(),
2195 expires=gist.gist_expires,
2200 expires=gist.gist_expires,
2196 created_on=gist.created_on,
2201 created_on=gist.created_on,
2197 )
2202 )
2198 return data
2203 return data
2199
2204
2200 def __json__(self):
2205 def __json__(self):
2201 data = dict(
2206 data = dict(
2202 )
2207 )
2203 data.update(self.get_api_data())
2208 data.update(self.get_api_data())
2204 return data
2209 return data
2205 ## SCM functions
2210 ## SCM functions
2206
2211
2207 @property
2212 @property
2208 def scm_instance(self):
2213 def scm_instance(self):
2209 from rhodecode.lib.vcs import get_repo
2214 from rhodecode.lib.vcs import get_repo
2210 base_path = self.base_path()
2215 base_path = self.base_path()
2211 return get_repo(os.path.join(*map(safe_str,
2216 return get_repo(os.path.join(*map(safe_str,
2212 [base_path, self.gist_access_id])))
2217 [base_path, self.gist_access_id])))
2213
2218
2214
2219
2215 class DbMigrateVersion(Base, BaseModel):
2220 class DbMigrateVersion(Base, BaseModel):
2216 __tablename__ = 'db_migrate_version'
2221 __tablename__ = 'db_migrate_version'
2217 __table_args__ = (
2222 __table_args__ = (
2218 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2223 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2219 'mysql_charset': 'utf8'},
2224 'mysql_charset': 'utf8'},
2220 )
2225 )
2221 repository_id = Column('repository_id', String(250), primary_key=True)
2226 repository_id = Column('repository_id', String(250), primary_key=True)
2222 repository_path = Column('repository_path', Text)
2227 repository_path = Column('repository_path', Text)
2223 version = Column('version', Integer)
2228 version = Column('version', Integer)
General Comments 0
You need to be logged in to leave comments. Login now