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