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