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