##// END OF EJS Templates
Starting RhodeCode 1.4 Branch
marcink -
r2214:2fd474e6 codereview
parent child Browse files
Show More
@@ -0,0 +1,28
1 # -*- coding: utf-8 -*-
2 """
3 rhodecode.model.db_1_4_0
4 ~~~~~~~~~~~~~~~~~~~~~~~~
5
6 Database Models for RhodeCode <=1.4.X
7
8 :created_on: Apr 08, 2010
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
12 """
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
17 #
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
22 #
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
26 #TODO: replace that will db.py content after 1.5 Release
27
28 from rhodecode.model.db import * No newline at end of file
@@ -1,94 +1,94
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, 3, 5, 'b')
29 VERSION = (1, 4, 0, '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__ = 5 # defines current db version for migrations
41 __dbversion__ = 6 # 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
45
46 PLATFORM_WIN = ('Windows')
46 PLATFORM_WIN = ('Windows')
47 PLATFORM_OTHERS = ('Linux', 'Darwin', 'FreeBSD', 'OpenBSD', 'SunOS')
47 PLATFORM_OTHERS = ('Linux', 'Darwin', 'FreeBSD', 'OpenBSD', 'SunOS')
48
48
49 requirements = [
49 requirements = [
50 "Pylons==1.0.0",
50 "Pylons==1.0.0",
51 "Beaker==1.6.3",
51 "Beaker==1.6.3",
52 "WebHelpers==1.3",
52 "WebHelpers==1.3",
53 "formencode==1.2.4",
53 "formencode==1.2.4",
54 "SQLAlchemy==0.7.6",
54 "SQLAlchemy==0.7.6",
55 "Mako==0.7.0",
55 "Mako==0.7.0",
56 "pygments>=1.4",
56 "pygments>=1.4",
57 "whoosh>=2.3.0,<2.4",
57 "whoosh>=2.3.0,<2.4",
58 "celery>=2.2.5,<2.3",
58 "celery>=2.2.5,<2.3",
59 "babel",
59 "babel",
60 "python-dateutil>=1.5.0,<2.0.0",
60 "python-dateutil>=1.5.0,<2.0.0",
61 "dulwich>=0.8.5,<0.9.0",
61 "dulwich>=0.8.5,<0.9.0",
62 "webob==1.0.8",
62 "webob==1.0.8",
63 "markdown==2.1.1",
63 "markdown==2.1.1",
64 "docutils==0.8.1",
64 "docutils==0.8.1",
65 ]
65 ]
66
66
67 if __py_version__ < (2, 6):
67 if __py_version__ < (2, 6):
68 requirements.append("simplejson")
68 requirements.append("simplejson")
69 requirements.append("pysqlite")
69 requirements.append("pysqlite")
70
70
71 if __platform__ in PLATFORM_WIN:
71 if __platform__ in PLATFORM_WIN:
72 requirements.append("mercurial>=2.2,<2.3")
72 requirements.append("mercurial>=2.2,<2.3")
73 else:
73 else:
74 requirements.append("py-bcrypt")
74 requirements.append("py-bcrypt")
75 requirements.append("mercurial>=2.2,<2.3")
75 requirements.append("mercurial>=2.2,<2.3")
76
76
77
77
78 def get_version():
78 def get_version():
79 """Returns shorter version (digit parts only) as string."""
79 """Returns shorter version (digit parts only) as string."""
80
80
81 return '.'.join((str(each) for each in VERSION[:3]))
81 return '.'.join((str(each) for each in VERSION[:3]))
82
82
83 BACKENDS = {
83 BACKENDS = {
84 'hg': 'Mercurial repository',
84 'hg': 'Mercurial repository',
85 'git': 'Git repository',
85 'git': 'Git repository',
86 }
86 }
87
87
88 CELERY_ON = False
88 CELERY_ON = False
89
89
90 # link to config for pylons
90 # link to config for pylons
91 CONFIG = {}
91 CONFIG = {}
92
92
93 # Linked module for extensions
93 # Linked module for extensions
94 EXTENSIONS = {}
94 EXTENSIONS = {}
@@ -1,527 +1,529
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 from rhodecode.model import meta
34 from rhodecode.model import meta
35
35
36 from rhodecode.model.user import UserModel
36 from rhodecode.model.user import UserModel
37 from rhodecode.lib.utils import ask_ok
37 from rhodecode.lib.utils import ask_ok
38 from rhodecode.model import init_model
38 from rhodecode.model import init_model
39 from rhodecode.model.db import User, Permission, RhodeCodeUi, \
39 from rhodecode.model.db import User, Permission, RhodeCodeUi, \
40 RhodeCodeSetting, UserToPerm, DbMigrateVersion, RepoGroup,\
40 RhodeCodeSetting, UserToPerm, DbMigrateVersion, RepoGroup,\
41 UserRepoGroupToPerm
41 UserRepoGroupToPerm
42
42
43 from sqlalchemy.engine import create_engine
43 from sqlalchemy.engine import create_engine
44 from rhodecode.model.repos_group import ReposGroupModel
44 from rhodecode.model.repos_group import ReposGroupModel
45
45
46 log = logging.getLogger(__name__)
46 log = logging.getLogger(__name__)
47
47
48
48
49 class DbManage(object):
49 class DbManage(object):
50 def __init__(self, log_sql, dbconf, root, tests=False):
50 def __init__(self, log_sql, dbconf, root, tests=False):
51 self.dbname = dbconf.split('/')[-1]
51 self.dbname = dbconf.split('/')[-1]
52 self.tests = tests
52 self.tests = tests
53 self.root = root
53 self.root = root
54 self.dburi = dbconf
54 self.dburi = dbconf
55 self.log_sql = log_sql
55 self.log_sql = log_sql
56 self.db_exists = False
56 self.db_exists = False
57 self.init_db()
57 self.init_db()
58
58
59 def init_db(self):
59 def init_db(self):
60 engine = create_engine(self.dburi, echo=self.log_sql)
60 engine = create_engine(self.dburi, echo=self.log_sql)
61 init_model(engine)
61 init_model(engine)
62 self.sa = meta.Session
62 self.sa = meta.Session
63
63
64 def create_tables(self, override=False):
64 def create_tables(self, override=False):
65 """
65 """
66 Create a auth database
66 Create a auth database
67 """
67 """
68
68
69 log.info("Any existing database is going to be destroyed")
69 log.info("Any existing database is going to be destroyed")
70 if self.tests:
70 if self.tests:
71 destroy = True
71 destroy = True
72 else:
72 else:
73 destroy = ask_ok('Are you sure to destroy old database ? [y/n]')
73 destroy = ask_ok('Are you sure to destroy old database ? [y/n]')
74 if not destroy:
74 if not destroy:
75 sys.exit()
75 sys.exit()
76 if destroy:
76 if destroy:
77 meta.Base.metadata.drop_all()
77 meta.Base.metadata.drop_all()
78
78
79 checkfirst = not override
79 checkfirst = not override
80 meta.Base.metadata.create_all(checkfirst=checkfirst)
80 meta.Base.metadata.create_all(checkfirst=checkfirst)
81 log.info('Created tables for %s' % self.dbname)
81 log.info('Created tables for %s' % self.dbname)
82
82
83 def set_db_version(self):
83 def set_db_version(self):
84 ver = DbMigrateVersion()
84 ver = DbMigrateVersion()
85 ver.version = __dbversion__
85 ver.version = __dbversion__
86 ver.repository_id = 'rhodecode_db_migrations'
86 ver.repository_id = 'rhodecode_db_migrations'
87 ver.repository_path = 'versions'
87 ver.repository_path = 'versions'
88 self.sa.add(ver)
88 self.sa.add(ver)
89 log.info('db version set to: %s' % __dbversion__)
89 log.info('db version set to: %s' % __dbversion__)
90
90
91 def upgrade(self):
91 def upgrade(self):
92 """
92 """
93 Upgrades given database schema to given revision following
93 Upgrades given database schema to given revision following
94 all needed steps, to perform the upgrade
94 all needed steps, to perform the upgrade
95
95
96 """
96 """
97
97
98 from rhodecode.lib.dbmigrate.migrate.versioning import api
98 from rhodecode.lib.dbmigrate.migrate.versioning import api
99 from rhodecode.lib.dbmigrate.migrate.exceptions import \
99 from rhodecode.lib.dbmigrate.migrate.exceptions import \
100 DatabaseNotControlledError
100 DatabaseNotControlledError
101
101
102 if 'sqlite' in self.dburi:
102 if 'sqlite' in self.dburi:
103 print (
103 print (
104 '********************** WARNING **********************\n'
104 '********************** WARNING **********************\n'
105 'Make sure your version of sqlite is at least 3.7.X. \n'
105 'Make sure your version of sqlite is at least 3.7.X. \n'
106 'Earlier versions are known to fail on some migrations\n'
106 'Earlier versions are known to fail on some migrations\n'
107 '*****************************************************\n'
107 '*****************************************************\n'
108 )
108 )
109 upgrade = ask_ok('You are about to perform database upgrade, make '
109 upgrade = ask_ok('You are about to perform database upgrade, make '
110 'sure You backed up your database before. '
110 'sure You backed up your database before. '
111 'Continue ? [y/n]')
111 'Continue ? [y/n]')
112 if not upgrade:
112 if not upgrade:
113 sys.exit('Nothing done')
113 sys.exit('Nothing done')
114
114
115 repository_path = jn(dn(dn(dn(os.path.realpath(__file__)))),
115 repository_path = jn(dn(dn(dn(os.path.realpath(__file__)))),
116 'rhodecode/lib/dbmigrate')
116 'rhodecode/lib/dbmigrate')
117 db_uri = self.dburi
117 db_uri = self.dburi
118
118
119 try:
119 try:
120 curr_version = api.db_version(db_uri, repository_path)
120 curr_version = api.db_version(db_uri, repository_path)
121 msg = ('Found current database under version'
121 msg = ('Found current database under version'
122 ' control with version %s' % curr_version)
122 ' control with version %s' % curr_version)
123
123
124 except (RuntimeError, DatabaseNotControlledError):
124 except (RuntimeError, DatabaseNotControlledError):
125 curr_version = 1
125 curr_version = 1
126 msg = ('Current database is not under version control. Setting'
126 msg = ('Current database is not under version control. Setting'
127 ' as version %s' % curr_version)
127 ' as version %s' % curr_version)
128 api.version_control(db_uri, repository_path, curr_version)
128 api.version_control(db_uri, repository_path, curr_version)
129
129
130 print (msg)
130 print (msg)
131
131
132 if curr_version == __dbversion__:
132 if curr_version == __dbversion__:
133 sys.exit('This database is already at the newest version')
133 sys.exit('This database is already at the newest version')
134
134
135 #======================================================================
135 #======================================================================
136 # UPGRADE STEPS
136 # UPGRADE STEPS
137 #======================================================================
137 #======================================================================
138 class UpgradeSteps(object):
138 class UpgradeSteps(object):
139 """
139 """
140 Those steps follow schema versions so for example schema
140 Those steps follow schema versions so for example schema
141 for example schema with seq 002 == step_2 and so on.
141 for example schema with seq 002 == step_2 and so on.
142 """
142 """
143
143
144 def __init__(self, klass):
144 def __init__(self, klass):
145 self.klass = klass
145 self.klass = klass
146
146
147 def step_0(self):
147 def step_0(self):
148 # step 0 is the schema upgrade, and than follow proper upgrades
148 # step 0 is the schema upgrade, and than follow proper upgrades
149 print ('attempting to do database upgrade to version %s' \
149 print ('attempting to do database upgrade to version %s' \
150 % __dbversion__)
150 % __dbversion__)
151 api.upgrade(db_uri, repository_path, __dbversion__)
151 api.upgrade(db_uri, repository_path, __dbversion__)
152 print ('Schema upgrade completed')
152 print ('Schema upgrade completed')
153
153
154 def step_1(self):
154 def step_1(self):
155 pass
155 pass
156
156
157 def step_2(self):
157 def step_2(self):
158 print ('Patching repo paths for newer version of RhodeCode')
158 print ('Patching repo paths for newer version of RhodeCode')
159 self.klass.fix_repo_paths()
159 self.klass.fix_repo_paths()
160
160
161 print ('Patching default user of RhodeCode')
161 print ('Patching default user of RhodeCode')
162 self.klass.fix_default_user()
162 self.klass.fix_default_user()
163
163
164 log.info('Changing ui settings')
164 log.info('Changing ui settings')
165 self.klass.create_ui_settings()
165 self.klass.create_ui_settings()
166
166
167 def step_3(self):
167 def step_3(self):
168 print ('Adding additional settings into RhodeCode db')
168 print ('Adding additional settings into RhodeCode db')
169 self.klass.fix_settings()
169 self.klass.fix_settings()
170 print ('Adding ldap defaults')
170 print ('Adding ldap defaults')
171 self.klass.create_ldap_options(skip_existing=True)
171 self.klass.create_ldap_options(skip_existing=True)
172
172
173 def step_4(self):
173 def step_4(self):
174 print ('create permissions and fix groups')
174 print ('create permissions and fix groups')
175 self.klass.create_permissions()
175 self.klass.create_permissions()
176 self.klass.fixup_groups()
176 self.klass.fixup_groups()
177
177
178 def step_5(self):
178 def step_5(self):
179 pass
179 pass
180
180
181 def step_6(self):
182 pass
181 upgrade_steps = [0] + range(curr_version + 1, __dbversion__ + 1)
183 upgrade_steps = [0] + range(curr_version + 1, __dbversion__ + 1)
182
184
183 # CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
185 # CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
184 for step in upgrade_steps:
186 for step in upgrade_steps:
185 print ('performing upgrade step %s' % step)
187 print ('performing upgrade step %s' % step)
186 getattr(UpgradeSteps(self), 'step_%s' % step)()
188 getattr(UpgradeSteps(self), 'step_%s' % step)()
187 self.sa.commit()
189 self.sa.commit()
188
190
189 def fix_repo_paths(self):
191 def fix_repo_paths(self):
190 """
192 """
191 Fixes a old rhodecode version path into new one without a '*'
193 Fixes a old rhodecode version path into new one without a '*'
192 """
194 """
193
195
194 paths = self.sa.query(RhodeCodeUi)\
196 paths = self.sa.query(RhodeCodeUi)\
195 .filter(RhodeCodeUi.ui_key == '/')\
197 .filter(RhodeCodeUi.ui_key == '/')\
196 .scalar()
198 .scalar()
197
199
198 paths.ui_value = paths.ui_value.replace('*', '')
200 paths.ui_value = paths.ui_value.replace('*', '')
199
201
200 try:
202 try:
201 self.sa.add(paths)
203 self.sa.add(paths)
202 self.sa.commit()
204 self.sa.commit()
203 except:
205 except:
204 self.sa.rollback()
206 self.sa.rollback()
205 raise
207 raise
206
208
207 def fix_default_user(self):
209 def fix_default_user(self):
208 """
210 """
209 Fixes a old default user with some 'nicer' default values,
211 Fixes a old default user with some 'nicer' default values,
210 used mostly for anonymous access
212 used mostly for anonymous access
211 """
213 """
212 def_user = self.sa.query(User)\
214 def_user = self.sa.query(User)\
213 .filter(User.username == 'default')\
215 .filter(User.username == 'default')\
214 .one()
216 .one()
215
217
216 def_user.name = 'Anonymous'
218 def_user.name = 'Anonymous'
217 def_user.lastname = 'User'
219 def_user.lastname = 'User'
218 def_user.email = 'anonymous@rhodecode.org'
220 def_user.email = 'anonymous@rhodecode.org'
219
221
220 try:
222 try:
221 self.sa.add(def_user)
223 self.sa.add(def_user)
222 self.sa.commit()
224 self.sa.commit()
223 except:
225 except:
224 self.sa.rollback()
226 self.sa.rollback()
225 raise
227 raise
226
228
227 def fix_settings(self):
229 def fix_settings(self):
228 """
230 """
229 Fixes rhodecode settings adds ga_code key for google analytics
231 Fixes rhodecode settings adds ga_code key for google analytics
230 """
232 """
231
233
232 hgsettings3 = RhodeCodeSetting('ga_code', '')
234 hgsettings3 = RhodeCodeSetting('ga_code', '')
233
235
234 try:
236 try:
235 self.sa.add(hgsettings3)
237 self.sa.add(hgsettings3)
236 self.sa.commit()
238 self.sa.commit()
237 except:
239 except:
238 self.sa.rollback()
240 self.sa.rollback()
239 raise
241 raise
240
242
241 def admin_prompt(self, second=False):
243 def admin_prompt(self, second=False):
242 if not self.tests:
244 if not self.tests:
243 import getpass
245 import getpass
244
246
245 def get_password():
247 def get_password():
246 password = getpass.getpass('Specify admin password '
248 password = getpass.getpass('Specify admin password '
247 '(min 6 chars):')
249 '(min 6 chars):')
248 confirm = getpass.getpass('Confirm password:')
250 confirm = getpass.getpass('Confirm password:')
249
251
250 if password != confirm:
252 if password != confirm:
251 log.error('passwords mismatch')
253 log.error('passwords mismatch')
252 return False
254 return False
253 if len(password) < 6:
255 if len(password) < 6:
254 log.error('password is to short use at least 6 characters')
256 log.error('password is to short use at least 6 characters')
255 return False
257 return False
256
258
257 return password
259 return password
258
260
259 username = raw_input('Specify admin username:')
261 username = raw_input('Specify admin username:')
260
262
261 password = get_password()
263 password = get_password()
262 if not password:
264 if not password:
263 #second try
265 #second try
264 password = get_password()
266 password = get_password()
265 if not password:
267 if not password:
266 sys.exit()
268 sys.exit()
267
269
268 email = raw_input('Specify admin email:')
270 email = raw_input('Specify admin email:')
269 self.create_user(username, password, email, True)
271 self.create_user(username, password, email, True)
270 else:
272 else:
271 log.info('creating admin and regular test users')
273 log.info('creating admin and regular test users')
272 from rhodecode.tests import TEST_USER_ADMIN_LOGIN,\
274 from rhodecode.tests import TEST_USER_ADMIN_LOGIN,\
273 TEST_USER_ADMIN_PASS, TEST_USER_ADMIN_EMAIL,\
275 TEST_USER_ADMIN_PASS, TEST_USER_ADMIN_EMAIL,\
274 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,\
276 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,\
275 TEST_USER_REGULAR_EMAIL, TEST_USER_REGULAR2_LOGIN, \
277 TEST_USER_REGULAR_EMAIL, TEST_USER_REGULAR2_LOGIN, \
276 TEST_USER_REGULAR2_PASS, TEST_USER_REGULAR2_EMAIL
278 TEST_USER_REGULAR2_PASS, TEST_USER_REGULAR2_EMAIL
277
279
278 self.create_user(TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS,
280 self.create_user(TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS,
279 TEST_USER_ADMIN_EMAIL, True)
281 TEST_USER_ADMIN_EMAIL, True)
280
282
281 self.create_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,
283 self.create_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,
282 TEST_USER_REGULAR_EMAIL, False)
284 TEST_USER_REGULAR_EMAIL, False)
283
285
284 self.create_user(TEST_USER_REGULAR2_LOGIN, TEST_USER_REGULAR2_PASS,
286 self.create_user(TEST_USER_REGULAR2_LOGIN, TEST_USER_REGULAR2_PASS,
285 TEST_USER_REGULAR2_EMAIL, False)
287 TEST_USER_REGULAR2_EMAIL, False)
286
288
287 def create_ui_settings(self):
289 def create_ui_settings(self):
288 """
290 """
289 Creates ui settings, fills out hooks
291 Creates ui settings, fills out hooks
290 and disables dotencode
292 and disables dotencode
291 """
293 """
292
294
293 #HOOKS
295 #HOOKS
294 hooks1_key = RhodeCodeUi.HOOK_UPDATE
296 hooks1_key = RhodeCodeUi.HOOK_UPDATE
295 hooks1_ = self.sa.query(RhodeCodeUi)\
297 hooks1_ = self.sa.query(RhodeCodeUi)\
296 .filter(RhodeCodeUi.ui_key == hooks1_key).scalar()
298 .filter(RhodeCodeUi.ui_key == hooks1_key).scalar()
297
299
298 hooks1 = RhodeCodeUi() if hooks1_ is None else hooks1_
300 hooks1 = RhodeCodeUi() if hooks1_ is None else hooks1_
299 hooks1.ui_section = 'hooks'
301 hooks1.ui_section = 'hooks'
300 hooks1.ui_key = hooks1_key
302 hooks1.ui_key = hooks1_key
301 hooks1.ui_value = 'hg update >&2'
303 hooks1.ui_value = 'hg update >&2'
302 hooks1.ui_active = False
304 hooks1.ui_active = False
303
305
304 hooks2_key = RhodeCodeUi.HOOK_REPO_SIZE
306 hooks2_key = RhodeCodeUi.HOOK_REPO_SIZE
305 hooks2_ = self.sa.query(RhodeCodeUi)\
307 hooks2_ = self.sa.query(RhodeCodeUi)\
306 .filter(RhodeCodeUi.ui_key == hooks2_key).scalar()
308 .filter(RhodeCodeUi.ui_key == hooks2_key).scalar()
307
309
308 hooks2 = RhodeCodeUi() if hooks2_ is None else hooks2_
310 hooks2 = RhodeCodeUi() if hooks2_ is None else hooks2_
309 hooks2.ui_section = 'hooks'
311 hooks2.ui_section = 'hooks'
310 hooks2.ui_key = hooks2_key
312 hooks2.ui_key = hooks2_key
311 hooks2.ui_value = 'python:rhodecode.lib.hooks.repo_size'
313 hooks2.ui_value = 'python:rhodecode.lib.hooks.repo_size'
312
314
313 hooks3 = RhodeCodeUi()
315 hooks3 = RhodeCodeUi()
314 hooks3.ui_section = 'hooks'
316 hooks3.ui_section = 'hooks'
315 hooks3.ui_key = RhodeCodeUi.HOOK_PUSH
317 hooks3.ui_key = RhodeCodeUi.HOOK_PUSH
316 hooks3.ui_value = 'python:rhodecode.lib.hooks.log_push_action'
318 hooks3.ui_value = 'python:rhodecode.lib.hooks.log_push_action'
317
319
318 hooks4 = RhodeCodeUi()
320 hooks4 = RhodeCodeUi()
319 hooks4.ui_section = 'hooks'
321 hooks4.ui_section = 'hooks'
320 hooks4.ui_key = RhodeCodeUi.HOOK_PULL
322 hooks4.ui_key = RhodeCodeUi.HOOK_PULL
321 hooks4.ui_value = 'python:rhodecode.lib.hooks.log_pull_action'
323 hooks4.ui_value = 'python:rhodecode.lib.hooks.log_pull_action'
322
324
323 # For mercurial 1.7 set backward comapatibility with format
325 # For mercurial 1.7 set backward comapatibility with format
324 dotencode_disable = RhodeCodeUi()
326 dotencode_disable = RhodeCodeUi()
325 dotencode_disable.ui_section = 'format'
327 dotencode_disable.ui_section = 'format'
326 dotencode_disable.ui_key = 'dotencode'
328 dotencode_disable.ui_key = 'dotencode'
327 dotencode_disable.ui_value = 'false'
329 dotencode_disable.ui_value = 'false'
328
330
329 # enable largefiles
331 # enable largefiles
330 largefiles = RhodeCodeUi()
332 largefiles = RhodeCodeUi()
331 largefiles.ui_section = 'extensions'
333 largefiles.ui_section = 'extensions'
332 largefiles.ui_key = 'largefiles'
334 largefiles.ui_key = 'largefiles'
333 largefiles.ui_value = ''
335 largefiles.ui_value = ''
334
336
335 self.sa.add(hooks1)
337 self.sa.add(hooks1)
336 self.sa.add(hooks2)
338 self.sa.add(hooks2)
337 self.sa.add(hooks3)
339 self.sa.add(hooks3)
338 self.sa.add(hooks4)
340 self.sa.add(hooks4)
339 self.sa.add(largefiles)
341 self.sa.add(largefiles)
340
342
341 def create_ldap_options(self, skip_existing=False):
343 def create_ldap_options(self, skip_existing=False):
342 """Creates ldap settings"""
344 """Creates ldap settings"""
343
345
344 for k, v in [('ldap_active', 'false'), ('ldap_host', ''),
346 for k, v in [('ldap_active', 'false'), ('ldap_host', ''),
345 ('ldap_port', '389'), ('ldap_tls_kind', 'PLAIN'),
347 ('ldap_port', '389'), ('ldap_tls_kind', 'PLAIN'),
346 ('ldap_tls_reqcert', ''), ('ldap_dn_user', ''),
348 ('ldap_tls_reqcert', ''), ('ldap_dn_user', ''),
347 ('ldap_dn_pass', ''), ('ldap_base_dn', ''),
349 ('ldap_dn_pass', ''), ('ldap_base_dn', ''),
348 ('ldap_filter', ''), ('ldap_search_scope', ''),
350 ('ldap_filter', ''), ('ldap_search_scope', ''),
349 ('ldap_attr_login', ''), ('ldap_attr_firstname', ''),
351 ('ldap_attr_login', ''), ('ldap_attr_firstname', ''),
350 ('ldap_attr_lastname', ''), ('ldap_attr_email', '')]:
352 ('ldap_attr_lastname', ''), ('ldap_attr_email', '')]:
351
353
352 if skip_existing and RhodeCodeSetting.get_by_name(k) != None:
354 if skip_existing and RhodeCodeSetting.get_by_name(k) != None:
353 log.debug('Skipping option %s' % k)
355 log.debug('Skipping option %s' % k)
354 continue
356 continue
355 setting = RhodeCodeSetting(k, v)
357 setting = RhodeCodeSetting(k, v)
356 self.sa.add(setting)
358 self.sa.add(setting)
357
359
358 def fixup_groups(self):
360 def fixup_groups(self):
359 def_usr = User.get_by_username('default')
361 def_usr = User.get_by_username('default')
360 for g in RepoGroup.query().all():
362 for g in RepoGroup.query().all():
361 g.group_name = g.get_new_name(g.name)
363 g.group_name = g.get_new_name(g.name)
362 self.sa.add(g)
364 self.sa.add(g)
363 # get default perm
365 # get default perm
364 default = UserRepoGroupToPerm.query()\
366 default = UserRepoGroupToPerm.query()\
365 .filter(UserRepoGroupToPerm.group == g)\
367 .filter(UserRepoGroupToPerm.group == g)\
366 .filter(UserRepoGroupToPerm.user == def_usr)\
368 .filter(UserRepoGroupToPerm.user == def_usr)\
367 .scalar()
369 .scalar()
368
370
369 if default is None:
371 if default is None:
370 log.debug('missing default permission for group %s adding' % g)
372 log.debug('missing default permission for group %s adding' % g)
371 ReposGroupModel()._create_default_perms(g)
373 ReposGroupModel()._create_default_perms(g)
372
374
373 def config_prompt(self, test_repo_path='', retries=3):
375 def config_prompt(self, test_repo_path='', retries=3):
374 if retries == 3:
376 if retries == 3:
375 log.info('Setting up repositories config')
377 log.info('Setting up repositories config')
376
378
377 if not self.tests and not test_repo_path:
379 if not self.tests and not test_repo_path:
378 path = raw_input(
380 path = raw_input(
379 'Enter a valid absolute path to store repositories. '
381 'Enter a valid absolute path to store repositories. '
380 'All repositories in that path will be added automatically:'
382 'All repositories in that path will be added automatically:'
381 )
383 )
382 else:
384 else:
383 path = test_repo_path
385 path = test_repo_path
384 path_ok = True
386 path_ok = True
385
387
386 # check proper dir
388 # check proper dir
387 if not os.path.isdir(path):
389 if not os.path.isdir(path):
388 path_ok = False
390 path_ok = False
389 log.error('Given path %s is not a valid directory' % path)
391 log.error('Given path %s is not a valid directory' % path)
390
392
391 elif not os.path.isabs(path):
393 elif not os.path.isabs(path):
392 path_ok = False
394 path_ok = False
393 log.error('Given path %s is not an absolute path' % path)
395 log.error('Given path %s is not an absolute path' % path)
394
396
395 # check write access
397 # check write access
396 elif not os.access(path, os.W_OK) and path_ok:
398 elif not os.access(path, os.W_OK) and path_ok:
397 path_ok = False
399 path_ok = False
398 log.error('No write permission to given path %s' % path)
400 log.error('No write permission to given path %s' % path)
399
401
400 if retries == 0:
402 if retries == 0:
401 sys.exit('max retries reached')
403 sys.exit('max retries reached')
402 if path_ok is False:
404 if path_ok is False:
403 retries -= 1
405 retries -= 1
404 return self.config_prompt(test_repo_path, retries)
406 return self.config_prompt(test_repo_path, retries)
405
407
406 return path
408 return path
407
409
408 def create_settings(self, path):
410 def create_settings(self, path):
409
411
410 self.create_ui_settings()
412 self.create_ui_settings()
411
413
412 #HG UI OPTIONS
414 #HG UI OPTIONS
413 web1 = RhodeCodeUi()
415 web1 = RhodeCodeUi()
414 web1.ui_section = 'web'
416 web1.ui_section = 'web'
415 web1.ui_key = 'push_ssl'
417 web1.ui_key = 'push_ssl'
416 web1.ui_value = 'false'
418 web1.ui_value = 'false'
417
419
418 web2 = RhodeCodeUi()
420 web2 = RhodeCodeUi()
419 web2.ui_section = 'web'
421 web2.ui_section = 'web'
420 web2.ui_key = 'allow_archive'
422 web2.ui_key = 'allow_archive'
421 web2.ui_value = 'gz zip bz2'
423 web2.ui_value = 'gz zip bz2'
422
424
423 web3 = RhodeCodeUi()
425 web3 = RhodeCodeUi()
424 web3.ui_section = 'web'
426 web3.ui_section = 'web'
425 web3.ui_key = 'allow_push'
427 web3.ui_key = 'allow_push'
426 web3.ui_value = '*'
428 web3.ui_value = '*'
427
429
428 web4 = RhodeCodeUi()
430 web4 = RhodeCodeUi()
429 web4.ui_section = 'web'
431 web4.ui_section = 'web'
430 web4.ui_key = 'baseurl'
432 web4.ui_key = 'baseurl'
431 web4.ui_value = '/'
433 web4.ui_value = '/'
432
434
433 paths = RhodeCodeUi()
435 paths = RhodeCodeUi()
434 paths.ui_section = 'paths'
436 paths.ui_section = 'paths'
435 paths.ui_key = '/'
437 paths.ui_key = '/'
436 paths.ui_value = path
438 paths.ui_value = path
437
439
438 hgsettings1 = RhodeCodeSetting('realm', 'RhodeCode authentication')
440 hgsettings1 = RhodeCodeSetting('realm', 'RhodeCode authentication')
439 hgsettings2 = RhodeCodeSetting('title', 'RhodeCode')
441 hgsettings2 = RhodeCodeSetting('title', 'RhodeCode')
440 hgsettings3 = RhodeCodeSetting('ga_code', '')
442 hgsettings3 = RhodeCodeSetting('ga_code', '')
441
443
442 self.sa.add(web1)
444 self.sa.add(web1)
443 self.sa.add(web2)
445 self.sa.add(web2)
444 self.sa.add(web3)
446 self.sa.add(web3)
445 self.sa.add(web4)
447 self.sa.add(web4)
446 self.sa.add(paths)
448 self.sa.add(paths)
447 self.sa.add(hgsettings1)
449 self.sa.add(hgsettings1)
448 self.sa.add(hgsettings2)
450 self.sa.add(hgsettings2)
449 self.sa.add(hgsettings3)
451 self.sa.add(hgsettings3)
450
452
451 self.create_ldap_options()
453 self.create_ldap_options()
452
454
453 log.info('created ui config')
455 log.info('created ui config')
454
456
455 def create_user(self, username, password, email='', admin=False):
457 def create_user(self, username, password, email='', admin=False):
456 log.info('creating user %s' % username)
458 log.info('creating user %s' % username)
457 UserModel().create_or_update(username, password, email,
459 UserModel().create_or_update(username, password, email,
458 name='RhodeCode', lastname='Admin',
460 name='RhodeCode', lastname='Admin',
459 active=True, admin=admin)
461 active=True, admin=admin)
460
462
461 def create_default_user(self):
463 def create_default_user(self):
462 log.info('creating default user')
464 log.info('creating default user')
463 # create default user for handling default permissions.
465 # create default user for handling default permissions.
464 UserModel().create_or_update(username='default',
466 UserModel().create_or_update(username='default',
465 password=str(uuid.uuid1())[:8],
467 password=str(uuid.uuid1())[:8],
466 email='anonymous@rhodecode.org',
468 email='anonymous@rhodecode.org',
467 name='Anonymous', lastname='User')
469 name='Anonymous', lastname='User')
468
470
469 def create_permissions(self):
471 def create_permissions(self):
470 # module.(access|create|change|delete)_[name]
472 # module.(access|create|change|delete)_[name]
471 # module.(none|read|write|admin)
473 # module.(none|read|write|admin)
472 perms = [
474 perms = [
473 ('repository.none', 'Repository no access'),
475 ('repository.none', 'Repository no access'),
474 ('repository.read', 'Repository read access'),
476 ('repository.read', 'Repository read access'),
475 ('repository.write', 'Repository write access'),
477 ('repository.write', 'Repository write access'),
476 ('repository.admin', 'Repository admin access'),
478 ('repository.admin', 'Repository admin access'),
477
479
478 ('group.none', 'Repositories Group no access'),
480 ('group.none', 'Repositories Group no access'),
479 ('group.read', 'Repositories Group read access'),
481 ('group.read', 'Repositories Group read access'),
480 ('group.write', 'Repositories Group write access'),
482 ('group.write', 'Repositories Group write access'),
481 ('group.admin', 'Repositories Group admin access'),
483 ('group.admin', 'Repositories Group admin access'),
482
484
483 ('hg.admin', 'Hg Administrator'),
485 ('hg.admin', 'Hg Administrator'),
484 ('hg.create.repository', 'Repository create'),
486 ('hg.create.repository', 'Repository create'),
485 ('hg.create.none', 'Repository creation disabled'),
487 ('hg.create.none', 'Repository creation disabled'),
486 ('hg.register.none', 'Register disabled'),
488 ('hg.register.none', 'Register disabled'),
487 ('hg.register.manual_activate', 'Register new user with RhodeCode '
489 ('hg.register.manual_activate', 'Register new user with RhodeCode '
488 'without manual activation'),
490 'without manual activation'),
489
491
490 ('hg.register.auto_activate', 'Register new user with RhodeCode '
492 ('hg.register.auto_activate', 'Register new user with RhodeCode '
491 'without auto activation'),
493 'without auto activation'),
492 ]
494 ]
493
495
494 for p in perms:
496 for p in perms:
495 if not Permission.get_by_key(p[0]):
497 if not Permission.get_by_key(p[0]):
496 new_perm = Permission()
498 new_perm = Permission()
497 new_perm.permission_name = p[0]
499 new_perm.permission_name = p[0]
498 new_perm.permission_longname = p[1]
500 new_perm.permission_longname = p[1]
499 self.sa.add(new_perm)
501 self.sa.add(new_perm)
500
502
501 def populate_default_permissions(self):
503 def populate_default_permissions(self):
502 log.info('creating default user permissions')
504 log.info('creating default user permissions')
503
505
504 default_user = self.sa.query(User)\
506 default_user = self.sa.query(User)\
505 .filter(User.username == 'default').scalar()
507 .filter(User.username == 'default').scalar()
506
508
507 reg_perm = UserToPerm()
509 reg_perm = UserToPerm()
508 reg_perm.user = default_user
510 reg_perm.user = default_user
509 reg_perm.permission = self.sa.query(Permission)\
511 reg_perm.permission = self.sa.query(Permission)\
510 .filter(Permission.permission_name == 'hg.register.manual_activate')\
512 .filter(Permission.permission_name == 'hg.register.manual_activate')\
511 .scalar()
513 .scalar()
512
514
513 create_repo_perm = UserToPerm()
515 create_repo_perm = UserToPerm()
514 create_repo_perm.user = default_user
516 create_repo_perm.user = default_user
515 create_repo_perm.permission = self.sa.query(Permission)\
517 create_repo_perm.permission = self.sa.query(Permission)\
516 .filter(Permission.permission_name == 'hg.create.repository')\
518 .filter(Permission.permission_name == 'hg.create.repository')\
517 .scalar()
519 .scalar()
518
520
519 default_repo_perm = UserToPerm()
521 default_repo_perm = UserToPerm()
520 default_repo_perm.user = default_user
522 default_repo_perm.user = default_user
521 default_repo_perm.permission = self.sa.query(Permission)\
523 default_repo_perm.permission = self.sa.query(Permission)\
522 .filter(Permission.permission_name == 'repository.read')\
524 .filter(Permission.permission_name == 'repository.read')\
523 .scalar()
525 .scalar()
524
526
525 self.sa.add(reg_perm)
527 self.sa.add(reg_perm)
526 self.sa.add(create_repo_perm)
528 self.sa.add(create_repo_perm)
527 self.sa.add(default_repo_perm)
529 self.sa.add(default_repo_perm)
@@ -1,1097 +1,1097
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.db
3 rhodecode.model.db_1_2_0
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Database Models for RhodeCode
6 Database Models for RhodeCode <=1.2.X
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 from datetime import date
30 from datetime import date
31
31
32 from sqlalchemy import *
32 from sqlalchemy import *
33 from sqlalchemy.ext.hybrid import hybrid_property
33 from sqlalchemy.ext.hybrid import hybrid_property
34 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
34 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
35 from beaker.cache import cache_region, region_invalidate
35 from beaker.cache import cache_region, region_invalidate
36
36
37 from rhodecode.lib.vcs import get_backend
37 from rhodecode.lib.vcs import get_backend
38 from rhodecode.lib.vcs.utils.helpers import get_scm
38 from rhodecode.lib.vcs.utils.helpers import get_scm
39 from rhodecode.lib.vcs.exceptions import VCSError
39 from rhodecode.lib.vcs.exceptions import VCSError
40 from rhodecode.lib.vcs.utils.lazy import LazyProperty
40 from rhodecode.lib.vcs.utils.lazy import LazyProperty
41
41
42 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
42 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
43 generate_api_key, safe_unicode
43 generate_api_key, safe_unicode
44 from rhodecode.lib.exceptions import UsersGroupsAssignedException
44 from rhodecode.lib.exceptions import UsersGroupsAssignedException
45 from rhodecode.lib.compat import json
45 from rhodecode.lib.compat import json
46
46
47 from rhodecode.model.meta import Base, Session
47 from rhodecode.model.meta import Base, Session
48 from rhodecode.lib.caching_query import FromCache
48 from rhodecode.lib.caching_query import FromCache
49
49
50
50
51 log = logging.getLogger(__name__)
51 log = logging.getLogger(__name__)
52
52
53 #==============================================================================
53 #==============================================================================
54 # BASE CLASSES
54 # BASE CLASSES
55 #==============================================================================
55 #==============================================================================
56
56
57 class ModelSerializer(json.JSONEncoder):
57 class ModelSerializer(json.JSONEncoder):
58 """
58 """
59 Simple Serializer for JSON,
59 Simple Serializer for JSON,
60
60
61 usage::
61 usage::
62
62
63 to make object customized for serialization implement a __json__
63 to make object customized for serialization implement a __json__
64 method that will return a dict for serialization into json
64 method that will return a dict for serialization into json
65
65
66 example::
66 example::
67
67
68 class Task(object):
68 class Task(object):
69
69
70 def __init__(self, name, value):
70 def __init__(self, name, value):
71 self.name = name
71 self.name = name
72 self.value = value
72 self.value = value
73
73
74 def __json__(self):
74 def __json__(self):
75 return dict(name=self.name,
75 return dict(name=self.name,
76 value=self.value)
76 value=self.value)
77
77
78 """
78 """
79
79
80 def default(self, obj):
80 def default(self, obj):
81
81
82 if hasattr(obj, '__json__'):
82 if hasattr(obj, '__json__'):
83 return obj.__json__()
83 return obj.__json__()
84 else:
84 else:
85 return json.JSONEncoder.default(self, obj)
85 return json.JSONEncoder.default(self, obj)
86
86
87 class BaseModel(object):
87 class BaseModel(object):
88 """Base Model for all classess
88 """Base Model for all classess
89
89
90 """
90 """
91
91
92 @classmethod
92 @classmethod
93 def _get_keys(cls):
93 def _get_keys(cls):
94 """return column names for this model """
94 """return column names for this model """
95 return class_mapper(cls).c.keys()
95 return class_mapper(cls).c.keys()
96
96
97 def get_dict(self):
97 def get_dict(self):
98 """return dict with keys and values corresponding
98 """return dict with keys and values corresponding
99 to this model data """
99 to this model data """
100
100
101 d = {}
101 d = {}
102 for k in self._get_keys():
102 for k in self._get_keys():
103 d[k] = getattr(self, k)
103 d[k] = getattr(self, k)
104 return d
104 return d
105
105
106 def get_appstruct(self):
106 def get_appstruct(self):
107 """return list with keys and values tupples corresponding
107 """return list with keys and values tupples corresponding
108 to this model data """
108 to this model data """
109
109
110 l = []
110 l = []
111 for k in self._get_keys():
111 for k in self._get_keys():
112 l.append((k, getattr(self, k),))
112 l.append((k, getattr(self, k),))
113 return l
113 return l
114
114
115 def populate_obj(self, populate_dict):
115 def populate_obj(self, populate_dict):
116 """populate model with data from given populate_dict"""
116 """populate model with data from given populate_dict"""
117
117
118 for k in self._get_keys():
118 for k in self._get_keys():
119 if k in populate_dict:
119 if k in populate_dict:
120 setattr(self, k, populate_dict[k])
120 setattr(self, k, populate_dict[k])
121
121
122 @classmethod
122 @classmethod
123 def query(cls):
123 def query(cls):
124 return Session.query(cls)
124 return Session.query(cls)
125
125
126 @classmethod
126 @classmethod
127 def get(cls, id_):
127 def get(cls, id_):
128 if id_:
128 if id_:
129 return cls.query().get(id_)
129 return cls.query().get(id_)
130
130
131 @classmethod
131 @classmethod
132 def getAll(cls):
132 def getAll(cls):
133 return cls.query().all()
133 return cls.query().all()
134
134
135 @classmethod
135 @classmethod
136 def delete(cls, id_):
136 def delete(cls, id_):
137 obj = cls.query().get(id_)
137 obj = cls.query().get(id_)
138 Session.delete(obj)
138 Session.delete(obj)
139 Session.commit()
139 Session.commit()
140
140
141
141
142 class RhodeCodeSetting(Base, BaseModel):
142 class RhodeCodeSetting(Base, BaseModel):
143 __tablename__ = 'rhodecode_settings'
143 __tablename__ = 'rhodecode_settings'
144 __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
144 __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
145 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
145 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
146 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
146 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
147 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
147 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
148
148
149 def __init__(self, k='', v=''):
149 def __init__(self, k='', v=''):
150 self.app_settings_name = k
150 self.app_settings_name = k
151 self.app_settings_value = v
151 self.app_settings_value = v
152
152
153
153
154 @validates('_app_settings_value')
154 @validates('_app_settings_value')
155 def validate_settings_value(self, key, val):
155 def validate_settings_value(self, key, val):
156 assert type(val) == unicode
156 assert type(val) == unicode
157 return val
157 return val
158
158
159 @hybrid_property
159 @hybrid_property
160 def app_settings_value(self):
160 def app_settings_value(self):
161 v = self._app_settings_value
161 v = self._app_settings_value
162 if v == 'ldap_active':
162 if v == 'ldap_active':
163 v = str2bool(v)
163 v = str2bool(v)
164 return v
164 return v
165
165
166 @app_settings_value.setter
166 @app_settings_value.setter
167 def app_settings_value(self, val):
167 def app_settings_value(self, val):
168 """
168 """
169 Setter that will always make sure we use unicode in app_settings_value
169 Setter that will always make sure we use unicode in app_settings_value
170
170
171 :param val:
171 :param val:
172 """
172 """
173 self._app_settings_value = safe_unicode(val)
173 self._app_settings_value = safe_unicode(val)
174
174
175 def __repr__(self):
175 def __repr__(self):
176 return "<%s('%s:%s')>" % (self.__class__.__name__,
176 return "<%s('%s:%s')>" % (self.__class__.__name__,
177 self.app_settings_name, self.app_settings_value)
177 self.app_settings_name, self.app_settings_value)
178
178
179
179
180 @classmethod
180 @classmethod
181 def get_by_name(cls, ldap_key):
181 def get_by_name(cls, ldap_key):
182 return cls.query()\
182 return cls.query()\
183 .filter(cls.app_settings_name == ldap_key).scalar()
183 .filter(cls.app_settings_name == ldap_key).scalar()
184
184
185 @classmethod
185 @classmethod
186 def get_app_settings(cls, cache=False):
186 def get_app_settings(cls, cache=False):
187
187
188 ret = cls.query()
188 ret = cls.query()
189
189
190 if cache:
190 if cache:
191 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
191 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
192
192
193 if not ret:
193 if not ret:
194 raise Exception('Could not get application settings !')
194 raise Exception('Could not get application settings !')
195 settings = {}
195 settings = {}
196 for each in ret:
196 for each in ret:
197 settings['rhodecode_' + each.app_settings_name] = \
197 settings['rhodecode_' + each.app_settings_name] = \
198 each.app_settings_value
198 each.app_settings_value
199
199
200 return settings
200 return settings
201
201
202 @classmethod
202 @classmethod
203 def get_ldap_settings(cls, cache=False):
203 def get_ldap_settings(cls, cache=False):
204 ret = cls.query()\
204 ret = cls.query()\
205 .filter(cls.app_settings_name.startswith('ldap_')).all()
205 .filter(cls.app_settings_name.startswith('ldap_')).all()
206 fd = {}
206 fd = {}
207 for row in ret:
207 for row in ret:
208 fd.update({row.app_settings_name:row.app_settings_value})
208 fd.update({row.app_settings_name:row.app_settings_value})
209
209
210 return fd
210 return fd
211
211
212
212
213 class RhodeCodeUi(Base, BaseModel):
213 class RhodeCodeUi(Base, BaseModel):
214 __tablename__ = 'rhodecode_ui'
214 __tablename__ = 'rhodecode_ui'
215 __table_args__ = (UniqueConstraint('ui_key'), {'extend_existing':True})
215 __table_args__ = (UniqueConstraint('ui_key'), {'extend_existing':True})
216
216
217 HOOK_UPDATE = 'changegroup.update'
217 HOOK_UPDATE = 'changegroup.update'
218 HOOK_REPO_SIZE = 'changegroup.repo_size'
218 HOOK_REPO_SIZE = 'changegroup.repo_size'
219 HOOK_PUSH = 'pretxnchangegroup.push_logger'
219 HOOK_PUSH = 'pretxnchangegroup.push_logger'
220 HOOK_PULL = 'preoutgoing.pull_logger'
220 HOOK_PULL = 'preoutgoing.pull_logger'
221
221
222 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
222 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
223 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
223 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
224 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
224 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
225 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
225 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
226 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
226 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
227
227
228
228
229 @classmethod
229 @classmethod
230 def get_by_key(cls, key):
230 def get_by_key(cls, key):
231 return cls.query().filter(cls.ui_key == key)
231 return cls.query().filter(cls.ui_key == key)
232
232
233
233
234 @classmethod
234 @classmethod
235 def get_builtin_hooks(cls):
235 def get_builtin_hooks(cls):
236 q = cls.query()
236 q = cls.query()
237 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
237 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
238 cls.HOOK_REPO_SIZE,
238 cls.HOOK_REPO_SIZE,
239 cls.HOOK_PUSH, cls.HOOK_PULL]))
239 cls.HOOK_PUSH, cls.HOOK_PULL]))
240 return q.all()
240 return q.all()
241
241
242 @classmethod
242 @classmethod
243 def get_custom_hooks(cls):
243 def get_custom_hooks(cls):
244 q = cls.query()
244 q = cls.query()
245 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
245 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
246 cls.HOOK_REPO_SIZE,
246 cls.HOOK_REPO_SIZE,
247 cls.HOOK_PUSH, cls.HOOK_PULL]))
247 cls.HOOK_PUSH, cls.HOOK_PULL]))
248 q = q.filter(cls.ui_section == 'hooks')
248 q = q.filter(cls.ui_section == 'hooks')
249 return q.all()
249 return q.all()
250
250
251 @classmethod
251 @classmethod
252 def create_or_update_hook(cls, key, val):
252 def create_or_update_hook(cls, key, val):
253 new_ui = cls.get_by_key(key).scalar() or cls()
253 new_ui = cls.get_by_key(key).scalar() or cls()
254 new_ui.ui_section = 'hooks'
254 new_ui.ui_section = 'hooks'
255 new_ui.ui_active = True
255 new_ui.ui_active = True
256 new_ui.ui_key = key
256 new_ui.ui_key = key
257 new_ui.ui_value = val
257 new_ui.ui_value = val
258
258
259 Session.add(new_ui)
259 Session.add(new_ui)
260 Session.commit()
260 Session.commit()
261
261
262
262
263 class User(Base, BaseModel):
263 class User(Base, BaseModel):
264 __tablename__ = 'users'
264 __tablename__ = 'users'
265 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True})
265 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True})
266 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
266 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
267 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
267 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
268 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
268 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
269 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
269 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
270 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
270 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
271 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
271 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
272 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
272 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
273 email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
273 email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
274 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
274 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
275 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
275 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
276 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
276 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
277
277
278 user_log = relationship('UserLog', cascade='all')
278 user_log = relationship('UserLog', cascade='all')
279 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
279 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
280
280
281 repositories = relationship('Repository')
281 repositories = relationship('Repository')
282 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
282 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
283 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
283 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
284
284
285 group_member = relationship('UsersGroupMember', cascade='all')
285 group_member = relationship('UsersGroupMember', cascade='all')
286
286
287 @property
287 @property
288 def full_contact(self):
288 def full_contact(self):
289 return '%s %s <%s>' % (self.name, self.lastname, self.email)
289 return '%s %s <%s>' % (self.name, self.lastname, self.email)
290
290
291 @property
291 @property
292 def short_contact(self):
292 def short_contact(self):
293 return '%s %s' % (self.name, self.lastname)
293 return '%s %s' % (self.name, self.lastname)
294
294
295 @property
295 @property
296 def is_admin(self):
296 def is_admin(self):
297 return self.admin
297 return self.admin
298
298
299 def __repr__(self):
299 def __repr__(self):
300 try:
300 try:
301 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
301 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
302 self.user_id, self.username)
302 self.user_id, self.username)
303 except:
303 except:
304 return self.__class__.__name__
304 return self.__class__.__name__
305
305
306 @classmethod
306 @classmethod
307 def get_by_username(cls, username, case_insensitive=False):
307 def get_by_username(cls, username, case_insensitive=False):
308 if case_insensitive:
308 if case_insensitive:
309 return Session.query(cls).filter(cls.username.ilike(username)).scalar()
309 return Session.query(cls).filter(cls.username.ilike(username)).scalar()
310 else:
310 else:
311 return Session.query(cls).filter(cls.username == username).scalar()
311 return Session.query(cls).filter(cls.username == username).scalar()
312
312
313 @classmethod
313 @classmethod
314 def get_by_api_key(cls, api_key):
314 def get_by_api_key(cls, api_key):
315 return cls.query().filter(cls.api_key == api_key).one()
315 return cls.query().filter(cls.api_key == api_key).one()
316
316
317 def update_lastlogin(self):
317 def update_lastlogin(self):
318 """Update user lastlogin"""
318 """Update user lastlogin"""
319
319
320 self.last_login = datetime.datetime.now()
320 self.last_login = datetime.datetime.now()
321 Session.add(self)
321 Session.add(self)
322 Session.commit()
322 Session.commit()
323 log.debug('updated user %s lastlogin' % self.username)
323 log.debug('updated user %s lastlogin' % self.username)
324
324
325 @classmethod
325 @classmethod
326 def create(cls, form_data):
326 def create(cls, form_data):
327 from rhodecode.lib.auth import get_crypt_password
327 from rhodecode.lib.auth import get_crypt_password
328
328
329 try:
329 try:
330 new_user = cls()
330 new_user = cls()
331 for k, v in form_data.items():
331 for k, v in form_data.items():
332 if k == 'password':
332 if k == 'password':
333 v = get_crypt_password(v)
333 v = get_crypt_password(v)
334 setattr(new_user, k, v)
334 setattr(new_user, k, v)
335
335
336 new_user.api_key = generate_api_key(form_data['username'])
336 new_user.api_key = generate_api_key(form_data['username'])
337 Session.add(new_user)
337 Session.add(new_user)
338 Session.commit()
338 Session.commit()
339 return new_user
339 return new_user
340 except:
340 except:
341 log.error(traceback.format_exc())
341 log.error(traceback.format_exc())
342 Session.rollback()
342 Session.rollback()
343 raise
343 raise
344
344
345 class UserLog(Base, BaseModel):
345 class UserLog(Base, BaseModel):
346 __tablename__ = 'user_logs'
346 __tablename__ = 'user_logs'
347 __table_args__ = {'extend_existing':True}
347 __table_args__ = {'extend_existing':True}
348 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
348 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
349 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
349 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
350 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
350 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
351 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
351 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
352 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
352 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
353 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
353 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
354 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
354 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
355
355
356 @property
356 @property
357 def action_as_day(self):
357 def action_as_day(self):
358 return date(*self.action_date.timetuple()[:3])
358 return date(*self.action_date.timetuple()[:3])
359
359
360 user = relationship('User')
360 user = relationship('User')
361 repository = relationship('Repository')
361 repository = relationship('Repository')
362
362
363
363
364 class UsersGroup(Base, BaseModel):
364 class UsersGroup(Base, BaseModel):
365 __tablename__ = 'users_groups'
365 __tablename__ = 'users_groups'
366 __table_args__ = {'extend_existing':True}
366 __table_args__ = {'extend_existing':True}
367
367
368 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
368 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
369 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
369 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
370 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
370 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
371
371
372 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
372 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
373
373
374 def __repr__(self):
374 def __repr__(self):
375 return '<userGroup(%s)>' % (self.users_group_name)
375 return '<userGroup(%s)>' % (self.users_group_name)
376
376
377 @classmethod
377 @classmethod
378 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
378 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
379 if case_insensitive:
379 if case_insensitive:
380 gr = cls.query()\
380 gr = cls.query()\
381 .filter(cls.users_group_name.ilike(group_name))
381 .filter(cls.users_group_name.ilike(group_name))
382 else:
382 else:
383 gr = cls.query()\
383 gr = cls.query()\
384 .filter(cls.users_group_name == group_name)
384 .filter(cls.users_group_name == group_name)
385 if cache:
385 if cache:
386 gr = gr.options(FromCache("sql_cache_short",
386 gr = gr.options(FromCache("sql_cache_short",
387 "get_user_%s" % group_name))
387 "get_user_%s" % group_name))
388 return gr.scalar()
388 return gr.scalar()
389
389
390
390
391 @classmethod
391 @classmethod
392 def get(cls, users_group_id, cache=False):
392 def get(cls, users_group_id, cache=False):
393 users_group = cls.query()
393 users_group = cls.query()
394 if cache:
394 if cache:
395 users_group = users_group.options(FromCache("sql_cache_short",
395 users_group = users_group.options(FromCache("sql_cache_short",
396 "get_users_group_%s" % users_group_id))
396 "get_users_group_%s" % users_group_id))
397 return users_group.get(users_group_id)
397 return users_group.get(users_group_id)
398
398
399 @classmethod
399 @classmethod
400 def create(cls, form_data):
400 def create(cls, form_data):
401 try:
401 try:
402 new_users_group = cls()
402 new_users_group = cls()
403 for k, v in form_data.items():
403 for k, v in form_data.items():
404 setattr(new_users_group, k, v)
404 setattr(new_users_group, k, v)
405
405
406 Session.add(new_users_group)
406 Session.add(new_users_group)
407 Session.commit()
407 Session.commit()
408 return new_users_group
408 return new_users_group
409 except:
409 except:
410 log.error(traceback.format_exc())
410 log.error(traceback.format_exc())
411 Session.rollback()
411 Session.rollback()
412 raise
412 raise
413
413
414 @classmethod
414 @classmethod
415 def update(cls, users_group_id, form_data):
415 def update(cls, users_group_id, form_data):
416
416
417 try:
417 try:
418 users_group = cls.get(users_group_id, cache=False)
418 users_group = cls.get(users_group_id, cache=False)
419
419
420 for k, v in form_data.items():
420 for k, v in form_data.items():
421 if k == 'users_group_members':
421 if k == 'users_group_members':
422 users_group.members = []
422 users_group.members = []
423 Session.flush()
423 Session.flush()
424 members_list = []
424 members_list = []
425 if v:
425 if v:
426 v = [v] if isinstance(v, basestring) else v
426 v = [v] if isinstance(v, basestring) else v
427 for u_id in set(v):
427 for u_id in set(v):
428 member = UsersGroupMember(users_group_id, u_id)
428 member = UsersGroupMember(users_group_id, u_id)
429 members_list.append(member)
429 members_list.append(member)
430 setattr(users_group, 'members', members_list)
430 setattr(users_group, 'members', members_list)
431 setattr(users_group, k, v)
431 setattr(users_group, k, v)
432
432
433 Session.add(users_group)
433 Session.add(users_group)
434 Session.commit()
434 Session.commit()
435 except:
435 except:
436 log.error(traceback.format_exc())
436 log.error(traceback.format_exc())
437 Session.rollback()
437 Session.rollback()
438 raise
438 raise
439
439
440 @classmethod
440 @classmethod
441 def delete(cls, users_group_id):
441 def delete(cls, users_group_id):
442 try:
442 try:
443
443
444 # check if this group is not assigned to repo
444 # check if this group is not assigned to repo
445 assigned_groups = UsersGroupRepoToPerm.query()\
445 assigned_groups = UsersGroupRepoToPerm.query()\
446 .filter(UsersGroupRepoToPerm.users_group_id ==
446 .filter(UsersGroupRepoToPerm.users_group_id ==
447 users_group_id).all()
447 users_group_id).all()
448
448
449 if assigned_groups:
449 if assigned_groups:
450 raise UsersGroupsAssignedException('RepoGroup assigned to %s' %
450 raise UsersGroupsAssignedException('RepoGroup assigned to %s' %
451 assigned_groups)
451 assigned_groups)
452
452
453 users_group = cls.get(users_group_id, cache=False)
453 users_group = cls.get(users_group_id, cache=False)
454 Session.delete(users_group)
454 Session.delete(users_group)
455 Session.commit()
455 Session.commit()
456 except:
456 except:
457 log.error(traceback.format_exc())
457 log.error(traceback.format_exc())
458 Session.rollback()
458 Session.rollback()
459 raise
459 raise
460
460
461 class UsersGroupMember(Base, BaseModel):
461 class UsersGroupMember(Base, BaseModel):
462 __tablename__ = 'users_groups_members'
462 __tablename__ = 'users_groups_members'
463 __table_args__ = {'extend_existing':True}
463 __table_args__ = {'extend_existing':True}
464
464
465 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
465 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
466 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
466 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
467 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
467 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
468
468
469 user = relationship('User', lazy='joined')
469 user = relationship('User', lazy='joined')
470 users_group = relationship('UsersGroup')
470 users_group = relationship('UsersGroup')
471
471
472 def __init__(self, gr_id='', u_id=''):
472 def __init__(self, gr_id='', u_id=''):
473 self.users_group_id = gr_id
473 self.users_group_id = gr_id
474 self.user_id = u_id
474 self.user_id = u_id
475
475
476 @staticmethod
476 @staticmethod
477 def add_user_to_group(group, user):
477 def add_user_to_group(group, user):
478 ugm = UsersGroupMember()
478 ugm = UsersGroupMember()
479 ugm.users_group = group
479 ugm.users_group = group
480 ugm.user = user
480 ugm.user = user
481 Session.add(ugm)
481 Session.add(ugm)
482 Session.commit()
482 Session.commit()
483 return ugm
483 return ugm
484
484
485 class Repository(Base, BaseModel):
485 class Repository(Base, BaseModel):
486 __tablename__ = 'repositories'
486 __tablename__ = 'repositories'
487 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
487 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
488
488
489 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
489 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
490 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
490 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
491 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
491 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
492 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
492 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
493 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
493 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
494 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
494 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
495 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
495 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
496 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
496 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
497 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
497 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
498 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
498 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
499
499
500 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
500 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
501 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
501 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
502
502
503
503
504 user = relationship('User')
504 user = relationship('User')
505 fork = relationship('Repository', remote_side=repo_id)
505 fork = relationship('Repository', remote_side=repo_id)
506 group = relationship('RepoGroup')
506 group = relationship('RepoGroup')
507 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
507 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
508 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
508 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
509 stats = relationship('Statistics', cascade='all', uselist=False)
509 stats = relationship('Statistics', cascade='all', uselist=False)
510
510
511 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
511 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
512
512
513 logs = relationship('UserLog', cascade='all')
513 logs = relationship('UserLog', cascade='all')
514
514
515 def __repr__(self):
515 def __repr__(self):
516 return "<%s('%s:%s')>" % (self.__class__.__name__,
516 return "<%s('%s:%s')>" % (self.__class__.__name__,
517 self.repo_id, self.repo_name)
517 self.repo_id, self.repo_name)
518
518
519 @classmethod
519 @classmethod
520 def url_sep(cls):
520 def url_sep(cls):
521 return '/'
521 return '/'
522
522
523 @classmethod
523 @classmethod
524 def get_by_repo_name(cls, repo_name):
524 def get_by_repo_name(cls, repo_name):
525 q = Session.query(cls).filter(cls.repo_name == repo_name)
525 q = Session.query(cls).filter(cls.repo_name == repo_name)
526 q = q.options(joinedload(Repository.fork))\
526 q = q.options(joinedload(Repository.fork))\
527 .options(joinedload(Repository.user))\
527 .options(joinedload(Repository.user))\
528 .options(joinedload(Repository.group))
528 .options(joinedload(Repository.group))
529 return q.one()
529 return q.one()
530
530
531 @classmethod
531 @classmethod
532 def get_repo_forks(cls, repo_id):
532 def get_repo_forks(cls, repo_id):
533 return cls.query().filter(Repository.fork_id == repo_id)
533 return cls.query().filter(Repository.fork_id == repo_id)
534
534
535 @classmethod
535 @classmethod
536 def base_path(cls):
536 def base_path(cls):
537 """
537 """
538 Returns base path when all repos are stored
538 Returns base path when all repos are stored
539
539
540 :param cls:
540 :param cls:
541 """
541 """
542 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
542 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
543 cls.url_sep())
543 cls.url_sep())
544 q.options(FromCache("sql_cache_short", "repository_repo_path"))
544 q.options(FromCache("sql_cache_short", "repository_repo_path"))
545 return q.one().ui_value
545 return q.one().ui_value
546
546
547 @property
547 @property
548 def just_name(self):
548 def just_name(self):
549 return self.repo_name.split(Repository.url_sep())[-1]
549 return self.repo_name.split(Repository.url_sep())[-1]
550
550
551 @property
551 @property
552 def groups_with_parents(self):
552 def groups_with_parents(self):
553 groups = []
553 groups = []
554 if self.group is None:
554 if self.group is None:
555 return groups
555 return groups
556
556
557 cur_gr = self.group
557 cur_gr = self.group
558 groups.insert(0, cur_gr)
558 groups.insert(0, cur_gr)
559 while 1:
559 while 1:
560 gr = getattr(cur_gr, 'parent_group', None)
560 gr = getattr(cur_gr, 'parent_group', None)
561 cur_gr = cur_gr.parent_group
561 cur_gr = cur_gr.parent_group
562 if gr is None:
562 if gr is None:
563 break
563 break
564 groups.insert(0, gr)
564 groups.insert(0, gr)
565
565
566 return groups
566 return groups
567
567
568 @property
568 @property
569 def groups_and_repo(self):
569 def groups_and_repo(self):
570 return self.groups_with_parents, self.just_name
570 return self.groups_with_parents, self.just_name
571
571
572 @LazyProperty
572 @LazyProperty
573 def repo_path(self):
573 def repo_path(self):
574 """
574 """
575 Returns base full path for that repository means where it actually
575 Returns base full path for that repository means where it actually
576 exists on a filesystem
576 exists on a filesystem
577 """
577 """
578 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
578 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
579 Repository.url_sep())
579 Repository.url_sep())
580 q.options(FromCache("sql_cache_short", "repository_repo_path"))
580 q.options(FromCache("sql_cache_short", "repository_repo_path"))
581 return q.one().ui_value
581 return q.one().ui_value
582
582
583 @property
583 @property
584 def repo_full_path(self):
584 def repo_full_path(self):
585 p = [self.repo_path]
585 p = [self.repo_path]
586 # we need to split the name by / since this is how we store the
586 # we need to split the name by / since this is how we store the
587 # names in the database, but that eventually needs to be converted
587 # names in the database, but that eventually needs to be converted
588 # into a valid system path
588 # into a valid system path
589 p += self.repo_name.split(Repository.url_sep())
589 p += self.repo_name.split(Repository.url_sep())
590 return os.path.join(*p)
590 return os.path.join(*p)
591
591
592 def get_new_name(self, repo_name):
592 def get_new_name(self, repo_name):
593 """
593 """
594 returns new full repository name based on assigned group and new new
594 returns new full repository name based on assigned group and new new
595
595
596 :param group_name:
596 :param group_name:
597 """
597 """
598 path_prefix = self.group.full_path_splitted if self.group else []
598 path_prefix = self.group.full_path_splitted if self.group else []
599 return Repository.url_sep().join(path_prefix + [repo_name])
599 return Repository.url_sep().join(path_prefix + [repo_name])
600
600
601 @property
601 @property
602 def _ui(self):
602 def _ui(self):
603 """
603 """
604 Creates an db based ui object for this repository
604 Creates an db based ui object for this repository
605 """
605 """
606 from mercurial import ui
606 from mercurial import ui
607 from mercurial import config
607 from mercurial import config
608 baseui = ui.ui()
608 baseui = ui.ui()
609
609
610 #clean the baseui object
610 #clean the baseui object
611 baseui._ocfg = config.config()
611 baseui._ocfg = config.config()
612 baseui._ucfg = config.config()
612 baseui._ucfg = config.config()
613 baseui._tcfg = config.config()
613 baseui._tcfg = config.config()
614
614
615
615
616 ret = RhodeCodeUi.query()\
616 ret = RhodeCodeUi.query()\
617 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
617 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
618
618
619 hg_ui = ret
619 hg_ui = ret
620 for ui_ in hg_ui:
620 for ui_ in hg_ui:
621 if ui_.ui_active:
621 if ui_.ui_active:
622 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
622 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
623 ui_.ui_key, ui_.ui_value)
623 ui_.ui_key, ui_.ui_value)
624 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
624 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
625
625
626 return baseui
626 return baseui
627
627
628 @classmethod
628 @classmethod
629 def is_valid(cls, repo_name):
629 def is_valid(cls, repo_name):
630 """
630 """
631 returns True if given repo name is a valid filesystem repository
631 returns True if given repo name is a valid filesystem repository
632
632
633 :param cls:
633 :param cls:
634 :param repo_name:
634 :param repo_name:
635 """
635 """
636 from rhodecode.lib.utils import is_valid_repo
636 from rhodecode.lib.utils import is_valid_repo
637
637
638 return is_valid_repo(repo_name, cls.base_path())
638 return is_valid_repo(repo_name, cls.base_path())
639
639
640
640
641 #==========================================================================
641 #==========================================================================
642 # SCM PROPERTIES
642 # SCM PROPERTIES
643 #==========================================================================
643 #==========================================================================
644
644
645 def get_changeset(self, rev):
645 def get_changeset(self, rev):
646 return get_changeset_safe(self.scm_instance, rev)
646 return get_changeset_safe(self.scm_instance, rev)
647
647
648 @property
648 @property
649 def tip(self):
649 def tip(self):
650 return self.get_changeset('tip')
650 return self.get_changeset('tip')
651
651
652 @property
652 @property
653 def author(self):
653 def author(self):
654 return self.tip.author
654 return self.tip.author
655
655
656 @property
656 @property
657 def last_change(self):
657 def last_change(self):
658 return self.scm_instance.last_change
658 return self.scm_instance.last_change
659
659
660 #==========================================================================
660 #==========================================================================
661 # SCM CACHE INSTANCE
661 # SCM CACHE INSTANCE
662 #==========================================================================
662 #==========================================================================
663
663
664 @property
664 @property
665 def invalidate(self):
665 def invalidate(self):
666 return CacheInvalidation.invalidate(self.repo_name)
666 return CacheInvalidation.invalidate(self.repo_name)
667
667
668 def set_invalidate(self):
668 def set_invalidate(self):
669 """
669 """
670 set a cache for invalidation for this instance
670 set a cache for invalidation for this instance
671 """
671 """
672 CacheInvalidation.set_invalidate(self.repo_name)
672 CacheInvalidation.set_invalidate(self.repo_name)
673
673
674 @LazyProperty
674 @LazyProperty
675 def scm_instance(self):
675 def scm_instance(self):
676 return self.__get_instance()
676 return self.__get_instance()
677
677
678 @property
678 @property
679 def scm_instance_cached(self):
679 def scm_instance_cached(self):
680 @cache_region('long_term')
680 @cache_region('long_term')
681 def _c(repo_name):
681 def _c(repo_name):
682 return self.__get_instance()
682 return self.__get_instance()
683 rn = self.repo_name
683 rn = self.repo_name
684
684
685 inv = self.invalidate
685 inv = self.invalidate
686 if inv is not None:
686 if inv is not None:
687 region_invalidate(_c, None, rn)
687 region_invalidate(_c, None, rn)
688 # update our cache
688 # update our cache
689 CacheInvalidation.set_valid(inv.cache_key)
689 CacheInvalidation.set_valid(inv.cache_key)
690 return _c(rn)
690 return _c(rn)
691
691
692 def __get_instance(self):
692 def __get_instance(self):
693
693
694 repo_full_path = self.repo_full_path
694 repo_full_path = self.repo_full_path
695
695
696 try:
696 try:
697 alias = get_scm(repo_full_path)[0]
697 alias = get_scm(repo_full_path)[0]
698 log.debug('Creating instance of %s repository' % alias)
698 log.debug('Creating instance of %s repository' % alias)
699 backend = get_backend(alias)
699 backend = get_backend(alias)
700 except VCSError:
700 except VCSError:
701 log.error(traceback.format_exc())
701 log.error(traceback.format_exc())
702 log.error('Perhaps this repository is in db and not in '
702 log.error('Perhaps this repository is in db and not in '
703 'filesystem run rescan repositories with '
703 'filesystem run rescan repositories with '
704 '"destroy old data " option from admin panel')
704 '"destroy old data " option from admin panel')
705 return
705 return
706
706
707 if alias == 'hg':
707 if alias == 'hg':
708
708
709 repo = backend(safe_str(repo_full_path), create=False,
709 repo = backend(safe_str(repo_full_path), create=False,
710 baseui=self._ui)
710 baseui=self._ui)
711 # skip hidden web repository
711 # skip hidden web repository
712 if repo._get_hidden():
712 if repo._get_hidden():
713 return
713 return
714 else:
714 else:
715 repo = backend(repo_full_path, create=False)
715 repo = backend(repo_full_path, create=False)
716
716
717 return repo
717 return repo
718
718
719
719
720 class Group(Base, BaseModel):
720 class Group(Base, BaseModel):
721 __tablename__ = 'groups'
721 __tablename__ = 'groups'
722 __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'),
722 __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'),
723 CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},)
723 CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},)
724 __mapper_args__ = {'order_by':'group_name'}
724 __mapper_args__ = {'order_by':'group_name'}
725
725
726 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
726 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
727 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
727 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
728 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
728 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
729 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
729 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
730
730
731 parent_group = relationship('Group', remote_side=group_id)
731 parent_group = relationship('Group', remote_side=group_id)
732
732
733 def __init__(self, group_name='', parent_group=None):
733 def __init__(self, group_name='', parent_group=None):
734 self.group_name = group_name
734 self.group_name = group_name
735 self.parent_group = parent_group
735 self.parent_group = parent_group
736
736
737 def __repr__(self):
737 def __repr__(self):
738 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
738 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
739 self.group_name)
739 self.group_name)
740
740
741 @classmethod
741 @classmethod
742 def groups_choices(cls):
742 def groups_choices(cls):
743 from webhelpers.html import literal as _literal
743 from webhelpers.html import literal as _literal
744 repo_groups = [('', '')]
744 repo_groups = [('', '')]
745 sep = ' &raquo; '
745 sep = ' &raquo; '
746 _name = lambda k: _literal(sep.join(k))
746 _name = lambda k: _literal(sep.join(k))
747
747
748 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
748 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
749 for x in cls.query().all()])
749 for x in cls.query().all()])
750
750
751 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
751 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
752 return repo_groups
752 return repo_groups
753
753
754 @classmethod
754 @classmethod
755 def url_sep(cls):
755 def url_sep(cls):
756 return '/'
756 return '/'
757
757
758 @classmethod
758 @classmethod
759 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
759 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
760 if case_insensitive:
760 if case_insensitive:
761 gr = cls.query()\
761 gr = cls.query()\
762 .filter(cls.group_name.ilike(group_name))
762 .filter(cls.group_name.ilike(group_name))
763 else:
763 else:
764 gr = cls.query()\
764 gr = cls.query()\
765 .filter(cls.group_name == group_name)
765 .filter(cls.group_name == group_name)
766 if cache:
766 if cache:
767 gr = gr.options(FromCache("sql_cache_short",
767 gr = gr.options(FromCache("sql_cache_short",
768 "get_group_%s" % group_name))
768 "get_group_%s" % group_name))
769 return gr.scalar()
769 return gr.scalar()
770
770
771 @property
771 @property
772 def parents(self):
772 def parents(self):
773 parents_recursion_limit = 5
773 parents_recursion_limit = 5
774 groups = []
774 groups = []
775 if self.parent_group is None:
775 if self.parent_group is None:
776 return groups
776 return groups
777 cur_gr = self.parent_group
777 cur_gr = self.parent_group
778 groups.insert(0, cur_gr)
778 groups.insert(0, cur_gr)
779 cnt = 0
779 cnt = 0
780 while 1:
780 while 1:
781 cnt += 1
781 cnt += 1
782 gr = getattr(cur_gr, 'parent_group', None)
782 gr = getattr(cur_gr, 'parent_group', None)
783 cur_gr = cur_gr.parent_group
783 cur_gr = cur_gr.parent_group
784 if gr is None:
784 if gr is None:
785 break
785 break
786 if cnt == parents_recursion_limit:
786 if cnt == parents_recursion_limit:
787 # this will prevent accidental infinit loops
787 # this will prevent accidental infinit loops
788 log.error('group nested more than %s' %
788 log.error('group nested more than %s' %
789 parents_recursion_limit)
789 parents_recursion_limit)
790 break
790 break
791
791
792 groups.insert(0, gr)
792 groups.insert(0, gr)
793 return groups
793 return groups
794
794
795 @property
795 @property
796 def children(self):
796 def children(self):
797 return Group.query().filter(Group.parent_group == self)
797 return Group.query().filter(Group.parent_group == self)
798
798
799 @property
799 @property
800 def name(self):
800 def name(self):
801 return self.group_name.split(Group.url_sep())[-1]
801 return self.group_name.split(Group.url_sep())[-1]
802
802
803 @property
803 @property
804 def full_path(self):
804 def full_path(self):
805 return self.group_name
805 return self.group_name
806
806
807 @property
807 @property
808 def full_path_splitted(self):
808 def full_path_splitted(self):
809 return self.group_name.split(Group.url_sep())
809 return self.group_name.split(Group.url_sep())
810
810
811 @property
811 @property
812 def repositories(self):
812 def repositories(self):
813 return Repository.query().filter(Repository.group == self)
813 return Repository.query().filter(Repository.group == self)
814
814
815 @property
815 @property
816 def repositories_recursive_count(self):
816 def repositories_recursive_count(self):
817 cnt = self.repositories.count()
817 cnt = self.repositories.count()
818
818
819 def children_count(group):
819 def children_count(group):
820 cnt = 0
820 cnt = 0
821 for child in group.children:
821 for child in group.children:
822 cnt += child.repositories.count()
822 cnt += child.repositories.count()
823 cnt += children_count(child)
823 cnt += children_count(child)
824 return cnt
824 return cnt
825
825
826 return cnt + children_count(self)
826 return cnt + children_count(self)
827
827
828
828
829 def get_new_name(self, group_name):
829 def get_new_name(self, group_name):
830 """
830 """
831 returns new full group name based on parent and new name
831 returns new full group name based on parent and new name
832
832
833 :param group_name:
833 :param group_name:
834 """
834 """
835 path_prefix = (self.parent_group.full_path_splitted if
835 path_prefix = (self.parent_group.full_path_splitted if
836 self.parent_group else [])
836 self.parent_group else [])
837 return Group.url_sep().join(path_prefix + [group_name])
837 return Group.url_sep().join(path_prefix + [group_name])
838
838
839
839
840 class Permission(Base, BaseModel):
840 class Permission(Base, BaseModel):
841 __tablename__ = 'permissions'
841 __tablename__ = 'permissions'
842 __table_args__ = {'extend_existing':True}
842 __table_args__ = {'extend_existing':True}
843 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
843 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
844 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
844 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
845 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
845 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
846
846
847 def __repr__(self):
847 def __repr__(self):
848 return "<%s('%s:%s')>" % (self.__class__.__name__,
848 return "<%s('%s:%s')>" % (self.__class__.__name__,
849 self.permission_id, self.permission_name)
849 self.permission_id, self.permission_name)
850
850
851 @classmethod
851 @classmethod
852 def get_by_key(cls, key):
852 def get_by_key(cls, key):
853 return cls.query().filter(cls.permission_name == key).scalar()
853 return cls.query().filter(cls.permission_name == key).scalar()
854
854
855 class UserRepoToPerm(Base, BaseModel):
855 class UserRepoToPerm(Base, BaseModel):
856 __tablename__ = 'repo_to_perm'
856 __tablename__ = 'repo_to_perm'
857 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True})
857 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True})
858 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
858 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
859 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
859 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
860 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
860 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
861 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
861 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
862
862
863 user = relationship('User')
863 user = relationship('User')
864 permission = relationship('Permission')
864 permission = relationship('Permission')
865 repository = relationship('Repository')
865 repository = relationship('Repository')
866
866
867 class UserToPerm(Base, BaseModel):
867 class UserToPerm(Base, BaseModel):
868 __tablename__ = 'user_to_perm'
868 __tablename__ = 'user_to_perm'
869 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
869 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
870 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
870 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
871 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
871 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
872 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
872 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
873
873
874 user = relationship('User')
874 user = relationship('User')
875 permission = relationship('Permission')
875 permission = relationship('Permission')
876
876
877 @classmethod
877 @classmethod
878 def has_perm(cls, user_id, perm):
878 def has_perm(cls, user_id, perm):
879 if not isinstance(perm, Permission):
879 if not isinstance(perm, Permission):
880 raise Exception('perm needs to be an instance of Permission class')
880 raise Exception('perm needs to be an instance of Permission class')
881
881
882 return cls.query().filter(cls.user_id == user_id)\
882 return cls.query().filter(cls.user_id == user_id)\
883 .filter(cls.permission == perm).scalar() is not None
883 .filter(cls.permission == perm).scalar() is not None
884
884
885 @classmethod
885 @classmethod
886 def grant_perm(cls, user_id, perm):
886 def grant_perm(cls, user_id, perm):
887 if not isinstance(perm, Permission):
887 if not isinstance(perm, Permission):
888 raise Exception('perm needs to be an instance of Permission class')
888 raise Exception('perm needs to be an instance of Permission class')
889
889
890 new = cls()
890 new = cls()
891 new.user_id = user_id
891 new.user_id = user_id
892 new.permission = perm
892 new.permission = perm
893 try:
893 try:
894 Session.add(new)
894 Session.add(new)
895 Session.commit()
895 Session.commit()
896 except:
896 except:
897 Session.rollback()
897 Session.rollback()
898
898
899
899
900 @classmethod
900 @classmethod
901 def revoke_perm(cls, user_id, perm):
901 def revoke_perm(cls, user_id, perm):
902 if not isinstance(perm, Permission):
902 if not isinstance(perm, Permission):
903 raise Exception('perm needs to be an instance of Permission class')
903 raise Exception('perm needs to be an instance of Permission class')
904
904
905 try:
905 try:
906 cls.query().filter(cls.user_id == user_id)\
906 cls.query().filter(cls.user_id == user_id)\
907 .filter(cls.permission == perm).delete()
907 .filter(cls.permission == perm).delete()
908 Session.commit()
908 Session.commit()
909 except:
909 except:
910 Session.rollback()
910 Session.rollback()
911
911
912 class UsersGroupRepoToPerm(Base, BaseModel):
912 class UsersGroupRepoToPerm(Base, BaseModel):
913 __tablename__ = 'users_group_repo_to_perm'
913 __tablename__ = 'users_group_repo_to_perm'
914 __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True})
914 __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True})
915 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
915 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
916 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
916 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
917 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
917 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
918 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
918 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
919
919
920 users_group = relationship('UsersGroup')
920 users_group = relationship('UsersGroup')
921 permission = relationship('Permission')
921 permission = relationship('Permission')
922 repository = relationship('Repository')
922 repository = relationship('Repository')
923
923
924 def __repr__(self):
924 def __repr__(self):
925 return '<userGroup:%s => %s >' % (self.users_group, self.repository)
925 return '<userGroup:%s => %s >' % (self.users_group, self.repository)
926
926
927 class UsersGroupToPerm(Base, BaseModel):
927 class UsersGroupToPerm(Base, BaseModel):
928 __tablename__ = 'users_group_to_perm'
928 __tablename__ = 'users_group_to_perm'
929 __table_args__ = {'extend_existing':True}
929 __table_args__ = {'extend_existing':True}
930 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
930 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
931 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
931 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
932 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
932 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
933
933
934 users_group = relationship('UsersGroup')
934 users_group = relationship('UsersGroup')
935 permission = relationship('Permission')
935 permission = relationship('Permission')
936
936
937
937
938 @classmethod
938 @classmethod
939 def has_perm(cls, users_group_id, perm):
939 def has_perm(cls, users_group_id, perm):
940 if not isinstance(perm, Permission):
940 if not isinstance(perm, Permission):
941 raise Exception('perm needs to be an instance of Permission class')
941 raise Exception('perm needs to be an instance of Permission class')
942
942
943 return cls.query().filter(cls.users_group_id ==
943 return cls.query().filter(cls.users_group_id ==
944 users_group_id)\
944 users_group_id)\
945 .filter(cls.permission == perm)\
945 .filter(cls.permission == perm)\
946 .scalar() is not None
946 .scalar() is not None
947
947
948 @classmethod
948 @classmethod
949 def grant_perm(cls, users_group_id, perm):
949 def grant_perm(cls, users_group_id, perm):
950 if not isinstance(perm, Permission):
950 if not isinstance(perm, Permission):
951 raise Exception('perm needs to be an instance of Permission class')
951 raise Exception('perm needs to be an instance of Permission class')
952
952
953 new = cls()
953 new = cls()
954 new.users_group_id = users_group_id
954 new.users_group_id = users_group_id
955 new.permission = perm
955 new.permission = perm
956 try:
956 try:
957 Session.add(new)
957 Session.add(new)
958 Session.commit()
958 Session.commit()
959 except:
959 except:
960 Session.rollback()
960 Session.rollback()
961
961
962
962
963 @classmethod
963 @classmethod
964 def revoke_perm(cls, users_group_id, perm):
964 def revoke_perm(cls, users_group_id, perm):
965 if not isinstance(perm, Permission):
965 if not isinstance(perm, Permission):
966 raise Exception('perm needs to be an instance of Permission class')
966 raise Exception('perm needs to be an instance of Permission class')
967
967
968 try:
968 try:
969 cls.query().filter(cls.users_group_id == users_group_id)\
969 cls.query().filter(cls.users_group_id == users_group_id)\
970 .filter(cls.permission == perm).delete()
970 .filter(cls.permission == perm).delete()
971 Session.commit()
971 Session.commit()
972 except:
972 except:
973 Session.rollback()
973 Session.rollback()
974
974
975
975
976 class UserRepoGroupToPerm(Base, BaseModel):
976 class UserRepoGroupToPerm(Base, BaseModel):
977 __tablename__ = 'group_to_perm'
977 __tablename__ = 'group_to_perm'
978 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
978 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
979
979
980 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
980 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
981 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
981 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
982 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
982 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
983 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
983 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
984
984
985 user = relationship('User')
985 user = relationship('User')
986 permission = relationship('Permission')
986 permission = relationship('Permission')
987 group = relationship('RepoGroup')
987 group = relationship('RepoGroup')
988
988
989 class Statistics(Base, BaseModel):
989 class Statistics(Base, BaseModel):
990 __tablename__ = 'statistics'
990 __tablename__ = 'statistics'
991 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True})
991 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True})
992 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
992 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
993 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
993 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
994 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
994 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
995 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
995 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
996 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
996 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
997 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
997 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
998
998
999 repository = relationship('Repository', single_parent=True)
999 repository = relationship('Repository', single_parent=True)
1000
1000
1001 class UserFollowing(Base, BaseModel):
1001 class UserFollowing(Base, BaseModel):
1002 __tablename__ = 'user_followings'
1002 __tablename__ = 'user_followings'
1003 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
1003 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
1004 UniqueConstraint('user_id', 'follows_user_id')
1004 UniqueConstraint('user_id', 'follows_user_id')
1005 , {'extend_existing':True})
1005 , {'extend_existing':True})
1006
1006
1007 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1007 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1008 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1008 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1009 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1009 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1010 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1010 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1011 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1011 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1012
1012
1013 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1013 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1014
1014
1015 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1015 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1016 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1016 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1017
1017
1018
1018
1019 @classmethod
1019 @classmethod
1020 def get_repo_followers(cls, repo_id):
1020 def get_repo_followers(cls, repo_id):
1021 return cls.query().filter(cls.follows_repo_id == repo_id)
1021 return cls.query().filter(cls.follows_repo_id == repo_id)
1022
1022
1023 class CacheInvalidation(Base, BaseModel):
1023 class CacheInvalidation(Base, BaseModel):
1024 __tablename__ = 'cache_invalidation'
1024 __tablename__ = 'cache_invalidation'
1025 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True})
1025 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True})
1026 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1026 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1027 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1027 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1028 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1028 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1029 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1029 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1030
1030
1031
1031
1032 def __init__(self, cache_key, cache_args=''):
1032 def __init__(self, cache_key, cache_args=''):
1033 self.cache_key = cache_key
1033 self.cache_key = cache_key
1034 self.cache_args = cache_args
1034 self.cache_args = cache_args
1035 self.cache_active = False
1035 self.cache_active = False
1036
1036
1037 def __repr__(self):
1037 def __repr__(self):
1038 return "<%s('%s:%s')>" % (self.__class__.__name__,
1038 return "<%s('%s:%s')>" % (self.__class__.__name__,
1039 self.cache_id, self.cache_key)
1039 self.cache_id, self.cache_key)
1040
1040
1041 @classmethod
1041 @classmethod
1042 def invalidate(cls, key):
1042 def invalidate(cls, key):
1043 """
1043 """
1044 Returns Invalidation object if this given key should be invalidated
1044 Returns Invalidation object if this given key should be invalidated
1045 None otherwise. `cache_active = False` means that this cache
1045 None otherwise. `cache_active = False` means that this cache
1046 state is not valid and needs to be invalidated
1046 state is not valid and needs to be invalidated
1047
1047
1048 :param key:
1048 :param key:
1049 """
1049 """
1050 return cls.query()\
1050 return cls.query()\
1051 .filter(CacheInvalidation.cache_key == key)\
1051 .filter(CacheInvalidation.cache_key == key)\
1052 .filter(CacheInvalidation.cache_active == False)\
1052 .filter(CacheInvalidation.cache_active == False)\
1053 .scalar()
1053 .scalar()
1054
1054
1055 @classmethod
1055 @classmethod
1056 def set_invalidate(cls, key):
1056 def set_invalidate(cls, key):
1057 """
1057 """
1058 Mark this Cache key for invalidation
1058 Mark this Cache key for invalidation
1059
1059
1060 :param key:
1060 :param key:
1061 """
1061 """
1062
1062
1063 log.debug('marking %s for invalidation' % key)
1063 log.debug('marking %s for invalidation' % key)
1064 inv_obj = Session.query(cls)\
1064 inv_obj = Session.query(cls)\
1065 .filter(cls.cache_key == key).scalar()
1065 .filter(cls.cache_key == key).scalar()
1066 if inv_obj:
1066 if inv_obj:
1067 inv_obj.cache_active = False
1067 inv_obj.cache_active = False
1068 else:
1068 else:
1069 log.debug('cache key not found in invalidation db -> creating one')
1069 log.debug('cache key not found in invalidation db -> creating one')
1070 inv_obj = CacheInvalidation(key)
1070 inv_obj = CacheInvalidation(key)
1071
1071
1072 try:
1072 try:
1073 Session.add(inv_obj)
1073 Session.add(inv_obj)
1074 Session.commit()
1074 Session.commit()
1075 except Exception:
1075 except Exception:
1076 log.error(traceback.format_exc())
1076 log.error(traceback.format_exc())
1077 Session.rollback()
1077 Session.rollback()
1078
1078
1079 @classmethod
1079 @classmethod
1080 def set_valid(cls, key):
1080 def set_valid(cls, key):
1081 """
1081 """
1082 Mark this cache key as active and currently cached
1082 Mark this cache key as active and currently cached
1083
1083
1084 :param key:
1084 :param key:
1085 """
1085 """
1086 inv_obj = Session.query(CacheInvalidation)\
1086 inv_obj = Session.query(CacheInvalidation)\
1087 .filter(CacheInvalidation.cache_key == key).scalar()
1087 .filter(CacheInvalidation.cache_key == key).scalar()
1088 inv_obj.cache_active = True
1088 inv_obj.cache_active = True
1089 Session.add(inv_obj)
1089 Session.add(inv_obj)
1090 Session.commit()
1090 Session.commit()
1091
1091
1092 class DbMigrateVersion(Base, BaseModel):
1092 class DbMigrateVersion(Base, BaseModel):
1093 __tablename__ = 'db_migrate_version'
1093 __tablename__ = 'db_migrate_version'
1094 __table_args__ = {'extend_existing':True}
1094 __table_args__ = {'extend_existing':True}
1095 repository_id = Column('repository_id', String(250), primary_key=True)
1095 repository_id = Column('repository_id', String(250), primary_key=True)
1096 repository_path = Column('repository_path', Text)
1096 repository_path = Column('repository_path', Text)
1097 version = Column('version', Integer)
1097 version = Column('version', Integer)
This diff has been collapsed as it changes many lines, (1274 lines changed) Show them Hide them
@@ -1,28 +1,1292
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.db
3 rhodecode.model.db_1_3_0
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Database Models for RhodeCode
6 Database Models for RhodeCode <=1.3.X
7
7
8 :created_on: Apr 08, 2010
8 :created_on: Apr 08, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 #TODO: when branch 1.3 is finished replacem with db.py content
26 import os
27 import logging
28 import datetime
29 import traceback
30 from collections import defaultdict
31
32 from sqlalchemy import *
33 from sqlalchemy.ext.hybrid import hybrid_property
34 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
35 from beaker.cache import cache_region, region_invalidate
36
37 from rhodecode.lib.vcs import get_backend
38 from rhodecode.lib.vcs.utils.helpers import get_scm
39 from rhodecode.lib.vcs.exceptions import VCSError
40 from rhodecode.lib.vcs.utils.lazy import LazyProperty
41
42 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
43 safe_unicode
44 from rhodecode.lib.compat import json
45 from rhodecode.lib.caching_query import FromCache
46
47 from rhodecode.model.meta import Base, Session
48 import hashlib
49
50
51 log = logging.getLogger(__name__)
52
53 #==============================================================================
54 # BASE CLASSES
55 #==============================================================================
56
57 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
58
59
60 class ModelSerializer(json.JSONEncoder):
61 """
62 Simple Serializer for JSON,
63
64 usage::
65
66 to make object customized for serialization implement a __json__
67 method that will return a dict for serialization into json
68
69 example::
70
71 class Task(object):
72
73 def __init__(self, name, value):
74 self.name = name
75 self.value = value
76
77 def __json__(self):
78 return dict(name=self.name,
79 value=self.value)
80
81 """
82
83 def default(self, obj):
84
85 if hasattr(obj, '__json__'):
86 return obj.__json__()
87 else:
88 return json.JSONEncoder.default(self, obj)
89
90
91 class BaseModel(object):
92 """
93 Base Model for all classess
94 """
95
96 @classmethod
97 def _get_keys(cls):
98 """return column names for this model """
99 return class_mapper(cls).c.keys()
100
101 def get_dict(self):
102 """
103 return dict with keys and values corresponding
104 to this model data """
105
106 d = {}
107 for k in self._get_keys():
108 d[k] = getattr(self, k)
109
110 # also use __json__() if present to get additional fields
111 for k, val in getattr(self, '__json__', lambda: {})().iteritems():
112 d[k] = val
113 return d
114
115 def get_appstruct(self):
116 """return list with keys and values tupples corresponding
117 to this model data """
118
119 l = []
120 for k in self._get_keys():
121 l.append((k, getattr(self, k),))
122 return l
123
124 def populate_obj(self, populate_dict):
125 """populate model with data from given populate_dict"""
126
127 for k in self._get_keys():
128 if k in populate_dict:
129 setattr(self, k, populate_dict[k])
130
131 @classmethod
132 def query(cls):
133 return Session.query(cls)
134
135 @classmethod
136 def get(cls, id_):
137 if id_:
138 return cls.query().get(id_)
139
140 @classmethod
141 def getAll(cls):
142 return cls.query().all()
143
144 @classmethod
145 def delete(cls, id_):
146 obj = cls.query().get(id_)
147 Session.delete(obj)
148
149 def __repr__(self):
150 if hasattr(self, '__unicode__'):
151 # python repr needs to return str
152 return safe_str(self.__unicode__())
153 return '<DB:%s>' % (self.__class__.__name__)
154
155 class RhodeCodeSetting(Base, BaseModel):
156 __tablename__ = 'rhodecode_settings'
157 __table_args__ = (
158 UniqueConstraint('app_settings_name'),
159 {'extend_existing': True, 'mysql_engine':'InnoDB',
160 'mysql_charset': 'utf8'}
161 )
162 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
163 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
164 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
165
166 def __init__(self, k='', v=''):
167 self.app_settings_name = k
168 self.app_settings_value = v
169
170 @validates('_app_settings_value')
171 def validate_settings_value(self, key, val):
172 assert type(val) == unicode
173 return val
174
175 @hybrid_property
176 def app_settings_value(self):
177 v = self._app_settings_value
178 if self.app_settings_name == 'ldap_active':
179 v = str2bool(v)
180 return v
181
182 @app_settings_value.setter
183 def app_settings_value(self, val):
184 """
185 Setter that will always make sure we use unicode in app_settings_value
186
187 :param val:
188 """
189 self._app_settings_value = safe_unicode(val)
190
191 def __unicode__(self):
192 return u"<%s('%s:%s')>" % (
193 self.__class__.__name__,
194 self.app_settings_name, self.app_settings_value
195 )
196
197 @classmethod
198 def get_by_name(cls, ldap_key):
199 return cls.query()\
200 .filter(cls.app_settings_name == ldap_key).scalar()
201
202 @classmethod
203 def get_app_settings(cls, cache=False):
204
205 ret = cls.query()
206
207 if cache:
208 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
209
210 if not ret:
211 raise Exception('Could not get application settings !')
212 settings = {}
213 for each in ret:
214 settings['rhodecode_' + each.app_settings_name] = \
215 each.app_settings_value
216
217 return settings
218
219 @classmethod
220 def get_ldap_settings(cls, cache=False):
221 ret = cls.query()\
222 .filter(cls.app_settings_name.startswith('ldap_')).all()
223 fd = {}
224 for row in ret:
225 fd.update({row.app_settings_name:row.app_settings_value})
226
227 return fd
228
229
230 class RhodeCodeUi(Base, BaseModel):
231 __tablename__ = 'rhodecode_ui'
232 __table_args__ = (
233 UniqueConstraint('ui_key'),
234 {'extend_existing': True, 'mysql_engine':'InnoDB',
235 'mysql_charset': 'utf8'}
236 )
237
238 HOOK_UPDATE = 'changegroup.update'
239 HOOK_REPO_SIZE = 'changegroup.repo_size'
240 HOOK_PUSH = 'pretxnchangegroup.push_logger'
241 HOOK_PULL = 'preoutgoing.pull_logger'
242
243 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
244 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
245 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
246 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
247 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
248
249 @classmethod
250 def get_by_key(cls, key):
251 return cls.query().filter(cls.ui_key == key)
252
253 @classmethod
254 def get_builtin_hooks(cls):
255 q = cls.query()
256 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
257 cls.HOOK_REPO_SIZE,
258 cls.HOOK_PUSH, cls.HOOK_PULL]))
259 return q.all()
260
261 @classmethod
262 def get_custom_hooks(cls):
263 q = cls.query()
264 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
265 cls.HOOK_REPO_SIZE,
266 cls.HOOK_PUSH, cls.HOOK_PULL]))
267 q = q.filter(cls.ui_section == 'hooks')
268 return q.all()
269
270 @classmethod
271 def create_or_update_hook(cls, key, val):
272 new_ui = cls.get_by_key(key).scalar() or cls()
273 new_ui.ui_section = 'hooks'
274 new_ui.ui_active = True
275 new_ui.ui_key = key
276 new_ui.ui_value = val
277
278 Session.add(new_ui)
279
280
281 class User(Base, BaseModel):
282 __tablename__ = 'users'
283 __table_args__ = (
284 UniqueConstraint('username'), UniqueConstraint('email'),
285 {'extend_existing': True, 'mysql_engine':'InnoDB',
286 'mysql_charset': 'utf8'}
287 )
288 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
289 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
290 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
291 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
292 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
293 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
294 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
295 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
296 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
297 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
298 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
299
300 user_log = relationship('UserLog', cascade='all')
301 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
302
303 repositories = relationship('Repository')
304 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
305 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
306 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
307
308 group_member = relationship('UsersGroupMember', cascade='all')
309
310 notifications = relationship('UserNotification', cascade='all')
311 # notifications assigned to this user
312 user_created_notifications = relationship('Notification', cascade='all')
313 # comments created by this user
314 user_comments = relationship('ChangesetComment', cascade='all')
315
316 @hybrid_property
317 def email(self):
318 return self._email
319
320 @email.setter
321 def email(self, val):
322 self._email = val.lower() if val else None
323
324 @property
325 def full_name(self):
326 return '%s %s' % (self.name, self.lastname)
327
328 @property
329 def full_name_or_username(self):
330 return ('%s %s' % (self.name, self.lastname)
331 if (self.name and self.lastname) else self.username)
332
333 @property
334 def full_contact(self):
335 return '%s %s <%s>' % (self.name, self.lastname, self.email)
336
337 @property
338 def short_contact(self):
339 return '%s %s' % (self.name, self.lastname)
340
341 @property
342 def is_admin(self):
343 return self.admin
344
345 def __unicode__(self):
346 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
347 self.user_id, self.username)
348
349 @classmethod
350 def get_by_username(cls, username, case_insensitive=False, cache=False):
351 if case_insensitive:
352 q = cls.query().filter(cls.username.ilike(username))
353 else:
354 q = cls.query().filter(cls.username == username)
355
356 if cache:
357 q = q.options(FromCache(
358 "sql_cache_short",
359 "get_user_%s" % _hash_key(username)
360 )
361 )
362 return q.scalar()
363
364 @classmethod
365 def get_by_api_key(cls, api_key, cache=False):
366 q = cls.query().filter(cls.api_key == api_key)
367
368 if cache:
369 q = q.options(FromCache("sql_cache_short",
370 "get_api_key_%s" % api_key))
371 return q.scalar()
372
373 @classmethod
374 def get_by_email(cls, email, case_insensitive=False, cache=False):
375 if case_insensitive:
376 q = cls.query().filter(cls.email.ilike(email))
377 else:
378 q = cls.query().filter(cls.email == email)
379
380 if cache:
381 q = q.options(FromCache("sql_cache_short",
382 "get_api_key_%s" % email))
383 return q.scalar()
384
385 def update_lastlogin(self):
386 """Update user lastlogin"""
387 self.last_login = datetime.datetime.now()
388 Session.add(self)
389 log.debug('updated user %s lastlogin' % self.username)
390
391 def __json__(self):
392 return dict(
393 user_id=self.user_id,
394 first_name=self.name,
395 last_name=self.lastname,
396 email=self.email,
397 full_name=self.full_name,
398 full_name_or_username=self.full_name_or_username,
399 short_contact=self.short_contact,
400 full_contact=self.full_contact
401 )
402
403
404 class UserLog(Base, BaseModel):
405 __tablename__ = 'user_logs'
406 __table_args__ = (
407 {'extend_existing': True, 'mysql_engine':'InnoDB',
408 'mysql_charset': 'utf8'},
409 )
410 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
411 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
412 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
413 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
414 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
415 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
416 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
417
418 @property
419 def action_as_day(self):
420 return datetime.date(*self.action_date.timetuple()[:3])
421
422 user = relationship('User')
423 repository = relationship('Repository', cascade='')
424
425
426 class UsersGroup(Base, BaseModel):
427 __tablename__ = 'users_groups'
428 __table_args__ = (
429 {'extend_existing': True, 'mysql_engine':'InnoDB',
430 'mysql_charset': 'utf8'},
431 )
432
433 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
434 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
435 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
436
437 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
438 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
439 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
440
441 def __unicode__(self):
442 return u'<userGroup(%s)>' % (self.users_group_name)
443
444 @classmethod
445 def get_by_group_name(cls, group_name, cache=False,
446 case_insensitive=False):
447 if case_insensitive:
448 q = cls.query().filter(cls.users_group_name.ilike(group_name))
449 else:
450 q = cls.query().filter(cls.users_group_name == group_name)
451 if cache:
452 q = q.options(FromCache(
453 "sql_cache_short",
454 "get_user_%s" % _hash_key(group_name)
455 )
456 )
457 return q.scalar()
458
459 @classmethod
460 def get(cls, users_group_id, cache=False):
461 users_group = cls.query()
462 if cache:
463 users_group = users_group.options(FromCache("sql_cache_short",
464 "get_users_group_%s" % users_group_id))
465 return users_group.get(users_group_id)
466
467
468 class UsersGroupMember(Base, BaseModel):
469 __tablename__ = 'users_groups_members'
470 __table_args__ = (
471 {'extend_existing': True, 'mysql_engine':'InnoDB',
472 'mysql_charset': 'utf8'},
473 )
474
475 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
476 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
477 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
478
479 user = relationship('User', lazy='joined')
480 users_group = relationship('UsersGroup')
481
482 def __init__(self, gr_id='', u_id=''):
483 self.users_group_id = gr_id
484 self.user_id = u_id
485
486
487 class Repository(Base, BaseModel):
488 __tablename__ = 'repositories'
489 __table_args__ = (
490 UniqueConstraint('repo_name'),
491 {'extend_existing': True, 'mysql_engine':'InnoDB',
492 'mysql_charset': 'utf8'},
493 )
494
495 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
496 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
497 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
498 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
499 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
500 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
501 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
502 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
503 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
504 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
505
506 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
507 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
508
509 user = relationship('User')
510 fork = relationship('Repository', remote_side=repo_id)
511 group = relationship('RepoGroup')
512 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
513 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
514 stats = relationship('Statistics', cascade='all', uselist=False)
515
516 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
517
518 logs = relationship('UserLog')
519
520 def __unicode__(self):
521 return u"<%s('%s:%s')>" % (self.__class__.__name__,self.repo_id,
522 self.repo_name)
523
524 @classmethod
525 def url_sep(cls):
526 return '/'
527
528 @classmethod
529 def get_by_repo_name(cls, repo_name):
530 q = Session.query(cls).filter(cls.repo_name == repo_name)
531 q = q.options(joinedload(Repository.fork))\
532 .options(joinedload(Repository.user))\
533 .options(joinedload(Repository.group))
534 return q.scalar()
535
536 @classmethod
537 def get_repo_forks(cls, repo_id):
538 return cls.query().filter(Repository.fork_id == repo_id)
539
540 @classmethod
541 def base_path(cls):
542 """
543 Returns base path when all repos are stored
544
545 :param cls:
546 """
547 q = Session.query(RhodeCodeUi)\
548 .filter(RhodeCodeUi.ui_key == cls.url_sep())
549 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
550 return q.one().ui_value
551
552 @property
553 def just_name(self):
554 return self.repo_name.split(Repository.url_sep())[-1]
555
556 @property
557 def groups_with_parents(self):
558 groups = []
559 if self.group is None:
560 return groups
561
562 cur_gr = self.group
563 groups.insert(0, cur_gr)
564 while 1:
565 gr = getattr(cur_gr, 'parent_group', None)
566 cur_gr = cur_gr.parent_group
567 if gr is None:
568 break
569 groups.insert(0, gr)
570
571 return groups
572
573 @property
574 def groups_and_repo(self):
575 return self.groups_with_parents, self.just_name
576
577 @LazyProperty
578 def repo_path(self):
579 """
580 Returns base full path for that repository means where it actually
581 exists on a filesystem
582 """
583 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
584 Repository.url_sep())
585 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
586 return q.one().ui_value
587
588 @property
589 def repo_full_path(self):
590 p = [self.repo_path]
591 # we need to split the name by / since this is how we store the
592 # names in the database, but that eventually needs to be converted
593 # into a valid system path
594 p += self.repo_name.split(Repository.url_sep())
595 return os.path.join(*p)
596
597 def get_new_name(self, repo_name):
598 """
599 returns new full repository name based on assigned group and new new
600
601 :param group_name:
602 """
603 path_prefix = self.group.full_path_splitted if self.group else []
604 return Repository.url_sep().join(path_prefix + [repo_name])
605
606 @property
607 def _ui(self):
608 """
609 Creates an db based ui object for this repository
610 """
611 from mercurial import ui
612 from mercurial import config
613 baseui = ui.ui()
614
615 #clean the baseui object
616 baseui._ocfg = config.config()
617 baseui._ucfg = config.config()
618 baseui._tcfg = config.config()
619
620 ret = RhodeCodeUi.query()\
621 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
622
623 hg_ui = ret
624 for ui_ in hg_ui:
625 if ui_.ui_active:
626 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
627 ui_.ui_key, ui_.ui_value)
628 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
629
630 return baseui
631
632 @classmethod
633 def is_valid(cls, repo_name):
634 """
635 returns True if given repo name is a valid filesystem repository
636
637 :param cls:
638 :param repo_name:
639 """
640 from rhodecode.lib.utils import is_valid_repo
641
642 return is_valid_repo(repo_name, cls.base_path())
643
644 #==========================================================================
645 # SCM PROPERTIES
646 #==========================================================================
647
648 def get_changeset(self, rev):
649 return get_changeset_safe(self.scm_instance, rev)
650
651 @property
652 def tip(self):
653 return self.get_changeset('tip')
654
655 @property
656 def author(self):
657 return self.tip.author
27
658
28 from rhodecode.model.db import *
659 @property
660 def last_change(self):
661 return self.scm_instance.last_change
662
663 def comments(self, revisions=None):
664 """
665 Returns comments for this repository grouped by revisions
666
667 :param revisions: filter query by revisions only
668 """
669 cmts = ChangesetComment.query()\
670 .filter(ChangesetComment.repo == self)
671 if revisions:
672 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
673 grouped = defaultdict(list)
674 for cmt in cmts.all():
675 grouped[cmt.revision].append(cmt)
676 return grouped
677
678 #==========================================================================
679 # SCM CACHE INSTANCE
680 #==========================================================================
681
682 @property
683 def invalidate(self):
684 return CacheInvalidation.invalidate(self.repo_name)
685
686 def set_invalidate(self):
687 """
688 set a cache for invalidation for this instance
689 """
690 CacheInvalidation.set_invalidate(self.repo_name)
691
692 @LazyProperty
693 def scm_instance(self):
694 return self.__get_instance()
695
696 @property
697 def scm_instance_cached(self):
698 @cache_region('long_term')
699 def _c(repo_name):
700 return self.__get_instance()
701 rn = self.repo_name
702 log.debug('Getting cached instance of repo')
703 inv = self.invalidate
704 if inv is not None:
705 region_invalidate(_c, None, rn)
706 # update our cache
707 CacheInvalidation.set_valid(inv.cache_key)
708 return _c(rn)
709
710 def __get_instance(self):
711 repo_full_path = self.repo_full_path
712 try:
713 alias = get_scm(repo_full_path)[0]
714 log.debug('Creating instance of %s repository' % alias)
715 backend = get_backend(alias)
716 except VCSError:
717 log.error(traceback.format_exc())
718 log.error('Perhaps this repository is in db and not in '
719 'filesystem run rescan repositories with '
720 '"destroy old data " option from admin panel')
721 return
722
723 if alias == 'hg':
724
725 repo = backend(safe_str(repo_full_path), create=False,
726 baseui=self._ui)
727 # skip hidden web repository
728 if repo._get_hidden():
729 return
730 else:
731 repo = backend(repo_full_path, create=False)
732
733 return repo
734
735
736 class RepoGroup(Base, BaseModel):
737 __tablename__ = 'groups'
738 __table_args__ = (
739 UniqueConstraint('group_name', 'group_parent_id'),
740 CheckConstraint('group_id != group_parent_id'),
741 {'extend_existing': True, 'mysql_engine':'InnoDB',
742 'mysql_charset': 'utf8'},
743 )
744 __mapper_args__ = {'order_by': 'group_name'}
745
746 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
747 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
748 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
749 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
750
751 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
752 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
753
754 parent_group = relationship('RepoGroup', remote_side=group_id)
755
756 def __init__(self, group_name='', parent_group=None):
757 self.group_name = group_name
758 self.parent_group = parent_group
759
760 def __unicode__(self):
761 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
762 self.group_name)
763
764 @classmethod
765 def groups_choices(cls):
766 from webhelpers.html import literal as _literal
767 repo_groups = [('', '')]
768 sep = ' &raquo; '
769 _name = lambda k: _literal(sep.join(k))
770
771 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
772 for x in cls.query().all()])
773
774 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
775 return repo_groups
776
777 @classmethod
778 def url_sep(cls):
779 return '/'
780
781 @classmethod
782 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
783 if case_insensitive:
784 gr = cls.query()\
785 .filter(cls.group_name.ilike(group_name))
786 else:
787 gr = cls.query()\
788 .filter(cls.group_name == group_name)
789 if cache:
790 gr = gr.options(FromCache(
791 "sql_cache_short",
792 "get_group_%s" % _hash_key(group_name)
793 )
794 )
795 return gr.scalar()
796
797 @property
798 def parents(self):
799 parents_recursion_limit = 5
800 groups = []
801 if self.parent_group is None:
802 return groups
803 cur_gr = self.parent_group
804 groups.insert(0, cur_gr)
805 cnt = 0
806 while 1:
807 cnt += 1
808 gr = getattr(cur_gr, 'parent_group', None)
809 cur_gr = cur_gr.parent_group
810 if gr is None:
811 break
812 if cnt == parents_recursion_limit:
813 # this will prevent accidental infinit loops
814 log.error('group nested more than %s' %
815 parents_recursion_limit)
816 break
817
818 groups.insert(0, gr)
819 return groups
820
821 @property
822 def children(self):
823 return RepoGroup.query().filter(RepoGroup.parent_group == self)
824
825 @property
826 def name(self):
827 return self.group_name.split(RepoGroup.url_sep())[-1]
828
829 @property
830 def full_path(self):
831 return self.group_name
832
833 @property
834 def full_path_splitted(self):
835 return self.group_name.split(RepoGroup.url_sep())
836
837 @property
838 def repositories(self):
839 return Repository.query()\
840 .filter(Repository.group == self)\
841 .order_by(Repository.repo_name)
842
843 @property
844 def repositories_recursive_count(self):
845 cnt = self.repositories.count()
846
847 def children_count(group):
848 cnt = 0
849 for child in group.children:
850 cnt += child.repositories.count()
851 cnt += children_count(child)
852 return cnt
853
854 return cnt + children_count(self)
855
856 def get_new_name(self, group_name):
857 """
858 returns new full group name based on parent and new name
859
860 :param group_name:
861 """
862 path_prefix = (self.parent_group.full_path_splitted if
863 self.parent_group else [])
864 return RepoGroup.url_sep().join(path_prefix + [group_name])
865
866
867 class Permission(Base, BaseModel):
868 __tablename__ = 'permissions'
869 __table_args__ = (
870 {'extend_existing': True, 'mysql_engine':'InnoDB',
871 'mysql_charset': 'utf8'},
872 )
873 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
874 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
875 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
876
877 def __unicode__(self):
878 return u"<%s('%s:%s')>" % (
879 self.__class__.__name__, self.permission_id, self.permission_name
880 )
881
882 @classmethod
883 def get_by_key(cls, key):
884 return cls.query().filter(cls.permission_name == key).scalar()
885
886 @classmethod
887 def get_default_perms(cls, default_user_id):
888 q = Session.query(UserRepoToPerm, Repository, cls)\
889 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
890 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
891 .filter(UserRepoToPerm.user_id == default_user_id)
892
893 return q.all()
894
895 @classmethod
896 def get_default_group_perms(cls, default_user_id):
897 q = Session.query(UserRepoGroupToPerm, RepoGroup, cls)\
898 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
899 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
900 .filter(UserRepoGroupToPerm.user_id == default_user_id)
901
902 return q.all()
903
904
905 class UserRepoToPerm(Base, BaseModel):
906 __tablename__ = 'repo_to_perm'
907 __table_args__ = (
908 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
909 {'extend_existing': True, 'mysql_engine':'InnoDB',
910 'mysql_charset': 'utf8'}
911 )
912 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
913 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
914 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
915 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
916
917 user = relationship('User')
918 repository = relationship('Repository')
919 permission = relationship('Permission')
920
921 @classmethod
922 def create(cls, user, repository, permission):
923 n = cls()
924 n.user = user
925 n.repository = repository
926 n.permission = permission
927 Session.add(n)
928 return n
929
930 def __unicode__(self):
931 return u'<user:%s => %s >' % (self.user, self.repository)
932
933
934 class UserToPerm(Base, BaseModel):
935 __tablename__ = 'user_to_perm'
936 __table_args__ = (
937 UniqueConstraint('user_id', 'permission_id'),
938 {'extend_existing': True, 'mysql_engine':'InnoDB',
939 'mysql_charset': 'utf8'}
940 )
941 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
942 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
943 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
944
945 user = relationship('User')
946 permission = relationship('Permission', lazy='joined')
947
948
949 class UsersGroupRepoToPerm(Base, BaseModel):
950 __tablename__ = 'users_group_repo_to_perm'
951 __table_args__ = (
952 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
953 {'extend_existing': True, 'mysql_engine':'InnoDB',
954 'mysql_charset': 'utf8'}
955 )
956 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
957 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
958 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
959 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
960
961 users_group = relationship('UsersGroup')
962 permission = relationship('Permission')
963 repository = relationship('Repository')
964
965 @classmethod
966 def create(cls, users_group, repository, permission):
967 n = cls()
968 n.users_group = users_group
969 n.repository = repository
970 n.permission = permission
971 Session.add(n)
972 return n
973
974 def __unicode__(self):
975 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
976
977
978 class UsersGroupToPerm(Base, BaseModel):
979 __tablename__ = 'users_group_to_perm'
980 __table_args__ = (
981 UniqueConstraint('users_group_id', 'permission_id',),
982 {'extend_existing': True, 'mysql_engine':'InnoDB',
983 'mysql_charset': 'utf8'}
984 )
985 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
986 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
987 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
988
989 users_group = relationship('UsersGroup')
990 permission = relationship('Permission')
991
992
993 class UserRepoGroupToPerm(Base, BaseModel):
994 __tablename__ = 'user_repo_group_to_perm'
995 __table_args__ = (
996 UniqueConstraint('user_id', 'group_id', 'permission_id'),
997 {'extend_existing': True, 'mysql_engine':'InnoDB',
998 'mysql_charset': 'utf8'}
999 )
1000
1001 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1002 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1003 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1004 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1005
1006 user = relationship('User')
1007 group = relationship('RepoGroup')
1008 permission = relationship('Permission')
1009
1010
1011 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1012 __tablename__ = 'users_group_repo_group_to_perm'
1013 __table_args__ = (
1014 UniqueConstraint('users_group_id', 'group_id'),
1015 {'extend_existing': True, 'mysql_engine':'InnoDB',
1016 'mysql_charset': 'utf8'}
1017 )
1018
1019 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)
1020 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1021 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1022 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1023
1024 users_group = relationship('UsersGroup')
1025 permission = relationship('Permission')
1026 group = relationship('RepoGroup')
1027
1028
1029 class Statistics(Base, BaseModel):
1030 __tablename__ = 'statistics'
1031 __table_args__ = (
1032 UniqueConstraint('repository_id'),
1033 {'extend_existing': True, 'mysql_engine':'InnoDB',
1034 'mysql_charset': 'utf8'}
1035 )
1036 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1037 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1038 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1039 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1040 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1041 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1042
1043 repository = relationship('Repository', single_parent=True)
1044
1045
1046 class UserFollowing(Base, BaseModel):
1047 __tablename__ = 'user_followings'
1048 __table_args__ = (
1049 UniqueConstraint('user_id', 'follows_repository_id'),
1050 UniqueConstraint('user_id', 'follows_user_id'),
1051 {'extend_existing': True, 'mysql_engine':'InnoDB',
1052 'mysql_charset': 'utf8'}
1053 )
1054
1055 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1056 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1057 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1058 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1059 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1060
1061 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1062
1063 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1064 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1065
1066 @classmethod
1067 def get_repo_followers(cls, repo_id):
1068 return cls.query().filter(cls.follows_repo_id == repo_id)
1069
1070
1071 class CacheInvalidation(Base, BaseModel):
1072 __tablename__ = 'cache_invalidation'
1073 __table_args__ = (
1074 UniqueConstraint('cache_key'),
1075 {'extend_existing': True, 'mysql_engine':'InnoDB',
1076 'mysql_charset': 'utf8'},
1077 )
1078 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1079 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1080 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1081 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1082
1083 def __init__(self, cache_key, cache_args=''):
1084 self.cache_key = cache_key
1085 self.cache_args = cache_args
1086 self.cache_active = False
1087
1088 def __unicode__(self):
1089 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1090 self.cache_id, self.cache_key)
1091 @classmethod
1092 def clear_cache(cls):
1093 cls.query().delete()
1094
1095 @classmethod
1096 def _get_key(cls, key):
1097 """
1098 Wrapper for generating a key, together with a prefix
1099
1100 :param key:
1101 """
1102 import rhodecode
1103 prefix = ''
1104 iid = rhodecode.CONFIG.get('instance_id')
1105 if iid:
1106 prefix = iid
1107 return "%s%s" % (prefix, key), prefix, key.rstrip('_README')
1108
1109 @classmethod
1110 def get_by_key(cls, key):
1111 return cls.query().filter(cls.cache_key == key).scalar()
1112
1113 @classmethod
1114 def _get_or_create_key(cls, key, prefix, org_key):
1115 inv_obj = Session.query(cls).filter(cls.cache_key == key).scalar()
1116 if not inv_obj:
1117 try:
1118 inv_obj = CacheInvalidation(key, org_key)
1119 Session.add(inv_obj)
1120 Session.commit()
1121 except Exception:
1122 log.error(traceback.format_exc())
1123 Session.rollback()
1124 return inv_obj
1125
1126 @classmethod
1127 def invalidate(cls, key):
1128 """
1129 Returns Invalidation object if this given key should be invalidated
1130 None otherwise. `cache_active = False` means that this cache
1131 state is not valid and needs to be invalidated
1132
1133 :param key:
1134 """
1135
1136 key, _prefix, _org_key = cls._get_key(key)
1137 inv = cls._get_or_create_key(key, _prefix, _org_key)
1138
1139 if inv and inv.cache_active is False:
1140 return inv
1141
1142 @classmethod
1143 def set_invalidate(cls, key):
1144 """
1145 Mark this Cache key for invalidation
1146
1147 :param key:
1148 """
1149
1150 key, _prefix, _org_key = cls._get_key(key)
1151 inv_objs = Session.query(cls).filter(cls.cache_args == _org_key).all()
1152 log.debug('marking %s key[s] %s for invalidation' % (len(inv_objs),
1153 _org_key))
1154 try:
1155 for inv_obj in inv_objs:
1156 if inv_obj:
1157 inv_obj.cache_active = False
1158
1159 Session.add(inv_obj)
1160 Session.commit()
1161 except Exception:
1162 log.error(traceback.format_exc())
1163 Session.rollback()
1164
1165 @classmethod
1166 def set_valid(cls, key):
1167 """
1168 Mark this cache key as active and currently cached
1169
1170 :param key:
1171 """
1172 inv_obj = cls.get_by_key(key)
1173 inv_obj.cache_active = True
1174 Session.add(inv_obj)
1175 Session.commit()
1176
1177
1178 class ChangesetComment(Base, BaseModel):
1179 __tablename__ = 'changeset_comments'
1180 __table_args__ = (
1181 {'extend_existing': True, 'mysql_engine':'InnoDB',
1182 'mysql_charset': 'utf8'},
1183 )
1184 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1185 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1186 revision = Column('revision', String(40), nullable=False)
1187 line_no = Column('line_no', Unicode(10), nullable=True)
1188 f_path = Column('f_path', Unicode(1000), nullable=True)
1189 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1190 text = Column('text', Unicode(25000), nullable=False)
1191 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1192
1193 author = relationship('User', lazy='joined')
1194 repo = relationship('Repository')
1195
1196 @classmethod
1197 def get_users(cls, revision):
1198 """
1199 Returns user associated with this changesetComment. ie those
1200 who actually commented
1201
1202 :param cls:
1203 :param revision:
1204 """
1205 return Session.query(User)\
1206 .filter(cls.revision == revision)\
1207 .join(ChangesetComment.author).all()
1208
1209
1210 class Notification(Base, BaseModel):
1211 __tablename__ = 'notifications'
1212 __table_args__ = (
1213 {'extend_existing': True, 'mysql_engine':'InnoDB',
1214 'mysql_charset': 'utf8'},
1215 )
1216
1217 TYPE_CHANGESET_COMMENT = u'cs_comment'
1218 TYPE_MESSAGE = u'message'
1219 TYPE_MENTION = u'mention'
1220 TYPE_REGISTRATION = u'registration'
1221
1222 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1223 subject = Column('subject', Unicode(512), nullable=True)
1224 body = Column('body', Unicode(50000), nullable=True)
1225 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1226 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1227 type_ = Column('type', Unicode(256))
1228
1229 created_by_user = relationship('User')
1230 notifications_to_users = relationship('UserNotification', lazy='joined',
1231 cascade="all, delete, delete-orphan")
1232
1233 @property
1234 def recipients(self):
1235 return [x.user for x in UserNotification.query()\
1236 .filter(UserNotification.notification == self).all()]
1237
1238 @classmethod
1239 def create(cls, created_by, subject, body, recipients, type_=None):
1240 if type_ is None:
1241 type_ = Notification.TYPE_MESSAGE
1242
1243 notification = cls()
1244 notification.created_by_user = created_by
1245 notification.subject = subject
1246 notification.body = body
1247 notification.type_ = type_
1248 notification.created_on = datetime.datetime.now()
1249
1250 for u in recipients:
1251 assoc = UserNotification()
1252 assoc.notification = notification
1253 u.notifications.append(assoc)
1254 Session.add(notification)
1255 return notification
1256
1257 @property
1258 def description(self):
1259 from rhodecode.model.notification import NotificationModel
1260 return NotificationModel().make_description(self)
1261
1262
1263 class UserNotification(Base, BaseModel):
1264 __tablename__ = 'user_to_notification'
1265 __table_args__ = (
1266 UniqueConstraint('user_id', 'notification_id'),
1267 {'extend_existing': True, 'mysql_engine':'InnoDB',
1268 'mysql_charset': 'utf8'}
1269 )
1270 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1271 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1272 read = Column('read', Boolean, default=False)
1273 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1274
1275 user = relationship('User', lazy="joined")
1276 notification = relationship('Notification', lazy="joined",
1277 order_by=lambda: Notification.created_on.desc(),)
1278
1279 def mark_as_read(self):
1280 self.read = True
1281 Session.add(self)
1282
1283
1284 class DbMigrateVersion(Base, BaseModel):
1285 __tablename__ = 'db_migrate_version'
1286 __table_args__ = (
1287 {'extend_existing': True, 'mysql_engine':'InnoDB',
1288 'mysql_charset': 'utf8'},
1289 )
1290 repository_id = Column('repository_id', String(250), primary_key=True)
1291 repository_path = Column('repository_path', Text)
1292 version = Column('version', Integer)
General Comments 0
You need to be logged in to leave comments. Login now