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