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