##// END OF EJS Templates
fixed the push_ssl issues after mercurial 2.3 upgrade....
marcink -
r2689:73cfc54c beta
parent child Browse files
Show More
@@ -1,659 +1,663 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.utils
3 rhodecode.lib.utils
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Utilities library for RhodeCode
6 Utilities library for RhodeCode
7
7
8 :created_on: Apr 18, 2010
8 :created_on: Apr 18, 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 re
27 import re
28 import logging
28 import logging
29 import datetime
29 import datetime
30 import traceback
30 import traceback
31 import paste
31 import paste
32 import beaker
32 import beaker
33 import tarfile
33 import tarfile
34 import shutil
34 import shutil
35 from os.path import abspath
35 from os.path import abspath
36 from os.path import dirname as dn, join as jn
36 from os.path import dirname as dn, join as jn
37
37
38 from paste.script.command import Command, BadCommand
38 from paste.script.command import Command, BadCommand
39
39
40 from mercurial import ui, config
40 from mercurial import ui, config
41
41
42 from webhelpers.text import collapse, remove_formatting, strip_tags
42 from webhelpers.text import collapse, remove_formatting, strip_tags
43
43
44 from rhodecode.lib.vcs import get_backend
44 from rhodecode.lib.vcs import get_backend
45 from rhodecode.lib.vcs.backends.base import BaseChangeset
45 from rhodecode.lib.vcs.backends.base import BaseChangeset
46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
47 from rhodecode.lib.vcs.utils.helpers import get_scm
47 from rhodecode.lib.vcs.utils.helpers import get_scm
48 from rhodecode.lib.vcs.exceptions import VCSError
48 from rhodecode.lib.vcs.exceptions import VCSError
49
49
50 from rhodecode.lib.caching_query import FromCache
50 from rhodecode.lib.caching_query import FromCache
51
51
52 from rhodecode.model import meta
52 from rhodecode.model import meta
53 from rhodecode.model.db import Repository, User, RhodeCodeUi, \
53 from rhodecode.model.db import Repository, User, RhodeCodeUi, \
54 UserLog, RepoGroup, RhodeCodeSetting, CacheInvalidation
54 UserLog, RepoGroup, RhodeCodeSetting, CacheInvalidation
55 from rhodecode.model.meta import Session
55 from rhodecode.model.meta import Session
56 from rhodecode.model.repos_group import ReposGroupModel
56 from rhodecode.model.repos_group import ReposGroupModel
57 from rhodecode.lib.utils2 import safe_str, safe_unicode
57 from rhodecode.lib.utils2 import safe_str, safe_unicode
58 from rhodecode.lib.vcs.utils.fakemod import create_module
58 from rhodecode.lib.vcs.utils.fakemod import create_module
59
59
60 log = logging.getLogger(__name__)
60 log = logging.getLogger(__name__)
61
61
62 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
62 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
63
63
64
64
65 def recursive_replace(str_, replace=' '):
65 def recursive_replace(str_, replace=' '):
66 """
66 """
67 Recursive replace of given sign to just one instance
67 Recursive replace of given sign to just one instance
68
68
69 :param str_: given string
69 :param str_: given string
70 :param replace: char to find and replace multiple instances
70 :param replace: char to find and replace multiple instances
71
71
72 Examples::
72 Examples::
73 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
73 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
74 'Mighty-Mighty-Bo-sstones'
74 'Mighty-Mighty-Bo-sstones'
75 """
75 """
76
76
77 if str_.find(replace * 2) == -1:
77 if str_.find(replace * 2) == -1:
78 return str_
78 return str_
79 else:
79 else:
80 str_ = str_.replace(replace * 2, replace)
80 str_ = str_.replace(replace * 2, replace)
81 return recursive_replace(str_, replace)
81 return recursive_replace(str_, replace)
82
82
83
83
84 def repo_name_slug(value):
84 def repo_name_slug(value):
85 """
85 """
86 Return slug of name of repository
86 Return slug of name of repository
87 This function is called on each creation/modification
87 This function is called on each creation/modification
88 of repository to prevent bad names in repo
88 of repository to prevent bad names in repo
89 """
89 """
90
90
91 slug = remove_formatting(value)
91 slug = remove_formatting(value)
92 slug = strip_tags(slug)
92 slug = strip_tags(slug)
93
93
94 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
94 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
95 slug = slug.replace(c, '-')
95 slug = slug.replace(c, '-')
96 slug = recursive_replace(slug, '-')
96 slug = recursive_replace(slug, '-')
97 slug = collapse(slug, '-')
97 slug = collapse(slug, '-')
98 return slug
98 return slug
99
99
100
100
101 def get_repo_slug(request):
101 def get_repo_slug(request):
102 _repo = request.environ['pylons.routes_dict'].get('repo_name')
102 _repo = request.environ['pylons.routes_dict'].get('repo_name')
103 if _repo:
103 if _repo:
104 _repo = _repo.rstrip('/')
104 _repo = _repo.rstrip('/')
105 return _repo
105 return _repo
106
106
107
107
108 def get_repos_group_slug(request):
108 def get_repos_group_slug(request):
109 _group = request.environ['pylons.routes_dict'].get('group_name')
109 _group = request.environ['pylons.routes_dict'].get('group_name')
110 if _group:
110 if _group:
111 _group = _group.rstrip('/')
111 _group = _group.rstrip('/')
112 return _group
112 return _group
113
113
114
114
115 def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
115 def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
116 """
116 """
117 Action logger for various actions made by users
117 Action logger for various actions made by users
118
118
119 :param user: user that made this action, can be a unique username string or
119 :param user: user that made this action, can be a unique username string or
120 object containing user_id attribute
120 object containing user_id attribute
121 :param action: action to log, should be on of predefined unique actions for
121 :param action: action to log, should be on of predefined unique actions for
122 easy translations
122 easy translations
123 :param repo: string name of repository or object containing repo_id,
123 :param repo: string name of repository or object containing repo_id,
124 that action was made on
124 that action was made on
125 :param ipaddr: optional ip address from what the action was made
125 :param ipaddr: optional ip address from what the action was made
126 :param sa: optional sqlalchemy session
126 :param sa: optional sqlalchemy session
127
127
128 """
128 """
129
129
130 if not sa:
130 if not sa:
131 sa = meta.Session()
131 sa = meta.Session()
132
132
133 try:
133 try:
134 if hasattr(user, 'user_id'):
134 if hasattr(user, 'user_id'):
135 user_obj = user
135 user_obj = user
136 elif isinstance(user, basestring):
136 elif isinstance(user, basestring):
137 user_obj = User.get_by_username(user)
137 user_obj = User.get_by_username(user)
138 else:
138 else:
139 raise Exception('You have to provide user object or username')
139 raise Exception('You have to provide user object or username')
140
140
141 if hasattr(repo, 'repo_id'):
141 if hasattr(repo, 'repo_id'):
142 repo_obj = Repository.get(repo.repo_id)
142 repo_obj = Repository.get(repo.repo_id)
143 repo_name = repo_obj.repo_name
143 repo_name = repo_obj.repo_name
144 elif isinstance(repo, basestring):
144 elif isinstance(repo, basestring):
145 repo_name = repo.lstrip('/')
145 repo_name = repo.lstrip('/')
146 repo_obj = Repository.get_by_repo_name(repo_name)
146 repo_obj = Repository.get_by_repo_name(repo_name)
147 else:
147 else:
148 repo_obj = None
148 repo_obj = None
149 repo_name = ''
149 repo_name = ''
150
150
151 user_log = UserLog()
151 user_log = UserLog()
152 user_log.user_id = user_obj.user_id
152 user_log.user_id = user_obj.user_id
153 user_log.action = safe_unicode(action)
153 user_log.action = safe_unicode(action)
154
154
155 user_log.repository = repo_obj
155 user_log.repository = repo_obj
156 user_log.repository_name = repo_name
156 user_log.repository_name = repo_name
157
157
158 user_log.action_date = datetime.datetime.now()
158 user_log.action_date = datetime.datetime.now()
159 user_log.user_ip = ipaddr
159 user_log.user_ip = ipaddr
160 sa.add(user_log)
160 sa.add(user_log)
161
161
162 log.info(
162 log.info(
163 'Adding user %s, action %s on %s' % (user_obj, action,
163 'Adding user %s, action %s on %s' % (user_obj, action,
164 safe_unicode(repo))
164 safe_unicode(repo))
165 )
165 )
166 if commit:
166 if commit:
167 sa.commit()
167 sa.commit()
168 except:
168 except:
169 log.error(traceback.format_exc())
169 log.error(traceback.format_exc())
170 raise
170 raise
171
171
172
172
173 def get_repos(path, recursive=False):
173 def get_repos(path, recursive=False):
174 """
174 """
175 Scans given path for repos and return (name,(type,path)) tuple
175 Scans given path for repos and return (name,(type,path)) tuple
176
176
177 :param path: path to scan for repositories
177 :param path: path to scan for repositories
178 :param recursive: recursive search and return names with subdirs in front
178 :param recursive: recursive search and return names with subdirs in front
179 """
179 """
180
180
181 # remove ending slash for better results
181 # remove ending slash for better results
182 path = path.rstrip(os.sep)
182 path = path.rstrip(os.sep)
183
183
184 def _get_repos(p):
184 def _get_repos(p):
185 if not os.access(p, os.W_OK):
185 if not os.access(p, os.W_OK):
186 return
186 return
187 for dirpath in os.listdir(p):
187 for dirpath in os.listdir(p):
188 if os.path.isfile(os.path.join(p, dirpath)):
188 if os.path.isfile(os.path.join(p, dirpath)):
189 continue
189 continue
190 cur_path = os.path.join(p, dirpath)
190 cur_path = os.path.join(p, dirpath)
191 try:
191 try:
192 scm_info = get_scm(cur_path)
192 scm_info = get_scm(cur_path)
193 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
193 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
194 except VCSError:
194 except VCSError:
195 if not recursive:
195 if not recursive:
196 continue
196 continue
197 #check if this dir containts other repos for recursive scan
197 #check if this dir containts other repos for recursive scan
198 rec_path = os.path.join(p, dirpath)
198 rec_path = os.path.join(p, dirpath)
199 if os.path.isdir(rec_path):
199 if os.path.isdir(rec_path):
200 for inner_scm in _get_repos(rec_path):
200 for inner_scm in _get_repos(rec_path):
201 yield inner_scm
201 yield inner_scm
202
202
203 return _get_repos(path)
203 return _get_repos(path)
204
204
205
205
206 def is_valid_repo(repo_name, base_path):
206 def is_valid_repo(repo_name, base_path):
207 """
207 """
208 Returns True if given path is a valid repository False otherwise
208 Returns True if given path is a valid repository False otherwise
209
209
210 :param repo_name:
210 :param repo_name:
211 :param base_path:
211 :param base_path:
212
212
213 :return True: if given path is a valid repository
213 :return True: if given path is a valid repository
214 """
214 """
215 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
215 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
216
216
217 try:
217 try:
218 get_scm(full_path)
218 get_scm(full_path)
219 return True
219 return True
220 except VCSError:
220 except VCSError:
221 return False
221 return False
222
222
223
223
224 def is_valid_repos_group(repos_group_name, base_path):
224 def is_valid_repos_group(repos_group_name, base_path):
225 """
225 """
226 Returns True if given path is a repos group False otherwise
226 Returns True if given path is a repos group False otherwise
227
227
228 :param repo_name:
228 :param repo_name:
229 :param base_path:
229 :param base_path:
230 """
230 """
231 full_path = os.path.join(safe_str(base_path), safe_str(repos_group_name))
231 full_path = os.path.join(safe_str(base_path), safe_str(repos_group_name))
232
232
233 # check if it's not a repo
233 # check if it's not a repo
234 if is_valid_repo(repos_group_name, base_path):
234 if is_valid_repo(repos_group_name, base_path):
235 return False
235 return False
236
236
237 try:
237 try:
238 # we need to check bare git repos at higher level
238 # we need to check bare git repos at higher level
239 # since we might match branches/hooks/info/objects or possible
239 # since we might match branches/hooks/info/objects or possible
240 # other things inside bare git repo
240 # other things inside bare git repo
241 get_scm(os.path.dirname(full_path))
241 get_scm(os.path.dirname(full_path))
242 return False
242 return False
243 except VCSError:
243 except VCSError:
244 pass
244 pass
245
245
246 # check if it's a valid path
246 # check if it's a valid path
247 if os.path.isdir(full_path):
247 if os.path.isdir(full_path):
248 return True
248 return True
249
249
250 return False
250 return False
251
251
252
252
253 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
253 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
254 while True:
254 while True:
255 ok = raw_input(prompt)
255 ok = raw_input(prompt)
256 if ok in ('y', 'ye', 'yes'):
256 if ok in ('y', 'ye', 'yes'):
257 return True
257 return True
258 if ok in ('n', 'no', 'nop', 'nope'):
258 if ok in ('n', 'no', 'nop', 'nope'):
259 return False
259 return False
260 retries = retries - 1
260 retries = retries - 1
261 if retries < 0:
261 if retries < 0:
262 raise IOError
262 raise IOError
263 print complaint
263 print complaint
264
264
265 #propagated from mercurial documentation
265 #propagated from mercurial documentation
266 ui_sections = ['alias', 'auth',
266 ui_sections = ['alias', 'auth',
267 'decode/encode', 'defaults',
267 'decode/encode', 'defaults',
268 'diff', 'email',
268 'diff', 'email',
269 'extensions', 'format',
269 'extensions', 'format',
270 'merge-patterns', 'merge-tools',
270 'merge-patterns', 'merge-tools',
271 'hooks', 'http_proxy',
271 'hooks', 'http_proxy',
272 'smtp', 'patch',
272 'smtp', 'patch',
273 'paths', 'profiling',
273 'paths', 'profiling',
274 'server', 'trusted',
274 'server', 'trusted',
275 'ui', 'web', ]
275 'ui', 'web', ]
276
276
277
277
278 def make_ui(read_from='file', path=None, checkpaths=True):
278 def make_ui(read_from='file', path=None, checkpaths=True):
279 """
279 """
280 A function that will read python rc files or database
280 A function that will read python rc files or database
281 and make an mercurial ui object from read options
281 and make an mercurial ui object from read options
282
282
283 :param path: path to mercurial config file
283 :param path: path to mercurial config file
284 :param checkpaths: check the path
284 :param checkpaths: check the path
285 :param read_from: read from 'file' or 'db'
285 :param read_from: read from 'file' or 'db'
286 """
286 """
287
287
288 baseui = ui.ui()
288 baseui = ui.ui()
289
289
290 # clean the baseui object
290 # clean the baseui object
291 baseui._ocfg = config.config()
291 baseui._ocfg = config.config()
292 baseui._ucfg = config.config()
292 baseui._ucfg = config.config()
293 baseui._tcfg = config.config()
293 baseui._tcfg = config.config()
294
294
295 if read_from == 'file':
295 if read_from == 'file':
296 if not os.path.isfile(path):
296 if not os.path.isfile(path):
297 log.debug('hgrc file is not present at %s skipping...' % path)
297 log.debug('hgrc file is not present at %s skipping...' % path)
298 return False
298 return False
299 log.debug('reading hgrc from %s' % path)
299 log.debug('reading hgrc from %s' % path)
300 cfg = config.config()
300 cfg = config.config()
301 cfg.read(path)
301 cfg.read(path)
302 for section in ui_sections:
302 for section in ui_sections:
303 for k, v in cfg.items(section):
303 for k, v in cfg.items(section):
304 log.debug('settings ui from file[%s]%s:%s' % (section, k, v))
304 log.debug('settings ui from file[%s]%s:%s' % (section, k, v))
305 baseui.setconfig(section, k, v)
305 baseui.setconfig(section, k, v)
306
306
307 elif read_from == 'db':
307 elif read_from == 'db':
308 sa = meta.Session()
308 sa = meta.Session()
309 ret = sa.query(RhodeCodeUi)\
309 ret = sa.query(RhodeCodeUi)\
310 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
310 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
311 .all()
311 .all()
312
312
313 hg_ui = ret
313 hg_ui = ret
314 for ui_ in hg_ui:
314 for ui_ in hg_ui:
315 if ui_.ui_active and ui_.ui_key != 'push_ssl':
315 if ui_.ui_active:
316 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
316 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
317 ui_.ui_key, ui_.ui_value)
317 ui_.ui_key, ui_.ui_value)
318 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
318 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
319 if ui_.ui_key == 'push_ssl':
320 # force set push_ssl requirement to False, rhodecode
321 # handles that
322 baseui.setconfig(ui_.ui_section, ui_.ui_key, False)
319
323
320 meta.Session.remove()
324 meta.Session.remove()
321 return baseui
325 return baseui
322
326
323
327
324 def set_rhodecode_config(config):
328 def set_rhodecode_config(config):
325 """
329 """
326 Updates pylons config with new settings from database
330 Updates pylons config with new settings from database
327
331
328 :param config:
332 :param config:
329 """
333 """
330 hgsettings = RhodeCodeSetting.get_app_settings()
334 hgsettings = RhodeCodeSetting.get_app_settings()
331
335
332 for k, v in hgsettings.items():
336 for k, v in hgsettings.items():
333 config[k] = v
337 config[k] = v
334
338
335
339
336 def invalidate_cache(cache_key, *args):
340 def invalidate_cache(cache_key, *args):
337 """
341 """
338 Puts cache invalidation task into db for
342 Puts cache invalidation task into db for
339 further global cache invalidation
343 further global cache invalidation
340 """
344 """
341
345
342 from rhodecode.model.scm import ScmModel
346 from rhodecode.model.scm import ScmModel
343
347
344 if cache_key.startswith('get_repo_cached_'):
348 if cache_key.startswith('get_repo_cached_'):
345 name = cache_key.split('get_repo_cached_')[-1]
349 name = cache_key.split('get_repo_cached_')[-1]
346 ScmModel().mark_for_invalidation(name)
350 ScmModel().mark_for_invalidation(name)
347
351
348
352
349 def map_groups(path):
353 def map_groups(path):
350 """
354 """
351 Given a full path to a repository, create all nested groups that this
355 Given a full path to a repository, create all nested groups that this
352 repo is inside. This function creates parent-child relationships between
356 repo is inside. This function creates parent-child relationships between
353 groups and creates default perms for all new groups.
357 groups and creates default perms for all new groups.
354
358
355 :param paths: full path to repository
359 :param paths: full path to repository
356 """
360 """
357 sa = meta.Session()
361 sa = meta.Session()
358 groups = path.split(Repository.url_sep())
362 groups = path.split(Repository.url_sep())
359 parent = None
363 parent = None
360 group = None
364 group = None
361
365
362 # last element is repo in nested groups structure
366 # last element is repo in nested groups structure
363 groups = groups[:-1]
367 groups = groups[:-1]
364 rgm = ReposGroupModel(sa)
368 rgm = ReposGroupModel(sa)
365 for lvl, group_name in enumerate(groups):
369 for lvl, group_name in enumerate(groups):
366 group_name = '/'.join(groups[:lvl] + [group_name])
370 group_name = '/'.join(groups[:lvl] + [group_name])
367 group = RepoGroup.get_by_group_name(group_name)
371 group = RepoGroup.get_by_group_name(group_name)
368 desc = '%s group' % group_name
372 desc = '%s group' % group_name
369
373
370 # skip folders that are now removed repos
374 # skip folders that are now removed repos
371 if REMOVED_REPO_PAT.match(group_name):
375 if REMOVED_REPO_PAT.match(group_name):
372 break
376 break
373
377
374 if group is None:
378 if group is None:
375 log.debug('creating group level: %s group_name: %s' % (lvl,
379 log.debug('creating group level: %s group_name: %s' % (lvl,
376 group_name))
380 group_name))
377 group = RepoGroup(group_name, parent)
381 group = RepoGroup(group_name, parent)
378 group.group_description = desc
382 group.group_description = desc
379 sa.add(group)
383 sa.add(group)
380 rgm._create_default_perms(group)
384 rgm._create_default_perms(group)
381 sa.flush()
385 sa.flush()
382 parent = group
386 parent = group
383 return group
387 return group
384
388
385
389
386 def repo2db_mapper(initial_repo_list, remove_obsolete=False,
390 def repo2db_mapper(initial_repo_list, remove_obsolete=False,
387 install_git_hook=False):
391 install_git_hook=False):
388 """
392 """
389 maps all repos given in initial_repo_list, non existing repositories
393 maps all repos given in initial_repo_list, non existing repositories
390 are created, if remove_obsolete is True it also check for db entries
394 are created, if remove_obsolete is True it also check for db entries
391 that are not in initial_repo_list and removes them.
395 that are not in initial_repo_list and removes them.
392
396
393 :param initial_repo_list: list of repositories found by scanning methods
397 :param initial_repo_list: list of repositories found by scanning methods
394 :param remove_obsolete: check for obsolete entries in database
398 :param remove_obsolete: check for obsolete entries in database
395 :param install_git_hook: if this is True, also check and install githook
399 :param install_git_hook: if this is True, also check and install githook
396 for a repo if missing
400 for a repo if missing
397 """
401 """
398 from rhodecode.model.repo import RepoModel
402 from rhodecode.model.repo import RepoModel
399 from rhodecode.model.scm import ScmModel
403 from rhodecode.model.scm import ScmModel
400 sa = meta.Session()
404 sa = meta.Session()
401 rm = RepoModel()
405 rm = RepoModel()
402 user = sa.query(User).filter(User.admin == True).first()
406 user = sa.query(User).filter(User.admin == True).first()
403 if user is None:
407 if user is None:
404 raise Exception('Missing administrative account !')
408 raise Exception('Missing administrative account !')
405 added = []
409 added = []
406
410
407 for name, repo in initial_repo_list.items():
411 for name, repo in initial_repo_list.items():
408 group = map_groups(name)
412 group = map_groups(name)
409 db_repo = rm.get_by_repo_name(name)
413 db_repo = rm.get_by_repo_name(name)
410 # found repo that is on filesystem not in RhodeCode database
414 # found repo that is on filesystem not in RhodeCode database
411 if not db_repo:
415 if not db_repo:
412 log.info('repository %s not found creating now' % name)
416 log.info('repository %s not found creating now' % name)
413 added.append(name)
417 added.append(name)
414 desc = (repo.description
418 desc = (repo.description
415 if repo.description != 'unknown'
419 if repo.description != 'unknown'
416 else '%s repository' % name)
420 else '%s repository' % name)
417 new_repo = rm.create_repo(
421 new_repo = rm.create_repo(
418 repo_name=name,
422 repo_name=name,
419 repo_type=repo.alias,
423 repo_type=repo.alias,
420 description=desc,
424 description=desc,
421 repos_group=getattr(group, 'group_id', None),
425 repos_group=getattr(group, 'group_id', None),
422 owner=user,
426 owner=user,
423 just_db=True
427 just_db=True
424 )
428 )
425 # we added that repo just now, and make sure it has githook
429 # we added that repo just now, and make sure it has githook
426 # installed
430 # installed
427 if new_repo.repo_type == 'git':
431 if new_repo.repo_type == 'git':
428 ScmModel().install_git_hook(new_repo.scm_instance)
432 ScmModel().install_git_hook(new_repo.scm_instance)
429 elif install_git_hook:
433 elif install_git_hook:
430 if db_repo.repo_type == 'git':
434 if db_repo.repo_type == 'git':
431 ScmModel().install_git_hook(db_repo.scm_instance)
435 ScmModel().install_git_hook(db_repo.scm_instance)
432 sa.commit()
436 sa.commit()
433 removed = []
437 removed = []
434 if remove_obsolete:
438 if remove_obsolete:
435 # remove from database those repositories that are not in the filesystem
439 # remove from database those repositories that are not in the filesystem
436 for repo in sa.query(Repository).all():
440 for repo in sa.query(Repository).all():
437 if repo.repo_name not in initial_repo_list.keys():
441 if repo.repo_name not in initial_repo_list.keys():
438 log.debug("Removing non existing repository found in db `%s`" %
442 log.debug("Removing non existing repository found in db `%s`" %
439 repo.repo_name)
443 repo.repo_name)
440 try:
444 try:
441 sa.delete(repo)
445 sa.delete(repo)
442 sa.commit()
446 sa.commit()
443 removed.append(repo.repo_name)
447 removed.append(repo.repo_name)
444 except:
448 except:
445 #don't hold further removals on error
449 #don't hold further removals on error
446 log.error(traceback.format_exc())
450 log.error(traceback.format_exc())
447 sa.rollback()
451 sa.rollback()
448
452
449 # clear cache keys
453 # clear cache keys
450 log.debug("Clearing cache keys now...")
454 log.debug("Clearing cache keys now...")
451 CacheInvalidation.clear_cache()
455 CacheInvalidation.clear_cache()
452 sa.commit()
456 sa.commit()
453 return added, removed
457 return added, removed
454
458
455
459
456 # set cache regions for beaker so celery can utilise it
460 # set cache regions for beaker so celery can utilise it
457 def add_cache(settings):
461 def add_cache(settings):
458 cache_settings = {'regions': None}
462 cache_settings = {'regions': None}
459 for key in settings.keys():
463 for key in settings.keys():
460 for prefix in ['beaker.cache.', 'cache.']:
464 for prefix in ['beaker.cache.', 'cache.']:
461 if key.startswith(prefix):
465 if key.startswith(prefix):
462 name = key.split(prefix)[1].strip()
466 name = key.split(prefix)[1].strip()
463 cache_settings[name] = settings[key].strip()
467 cache_settings[name] = settings[key].strip()
464 if cache_settings['regions']:
468 if cache_settings['regions']:
465 for region in cache_settings['regions'].split(','):
469 for region in cache_settings['regions'].split(','):
466 region = region.strip()
470 region = region.strip()
467 region_settings = {}
471 region_settings = {}
468 for key, value in cache_settings.items():
472 for key, value in cache_settings.items():
469 if key.startswith(region):
473 if key.startswith(region):
470 region_settings[key.split('.')[1]] = value
474 region_settings[key.split('.')[1]] = value
471 region_settings['expire'] = int(region_settings.get('expire',
475 region_settings['expire'] = int(region_settings.get('expire',
472 60))
476 60))
473 region_settings.setdefault('lock_dir',
477 region_settings.setdefault('lock_dir',
474 cache_settings.get('lock_dir'))
478 cache_settings.get('lock_dir'))
475 region_settings.setdefault('data_dir',
479 region_settings.setdefault('data_dir',
476 cache_settings.get('data_dir'))
480 cache_settings.get('data_dir'))
477
481
478 if 'type' not in region_settings:
482 if 'type' not in region_settings:
479 region_settings['type'] = cache_settings.get('type',
483 region_settings['type'] = cache_settings.get('type',
480 'memory')
484 'memory')
481 beaker.cache.cache_regions[region] = region_settings
485 beaker.cache.cache_regions[region] = region_settings
482
486
483
487
484 def load_rcextensions(root_path):
488 def load_rcextensions(root_path):
485 import rhodecode
489 import rhodecode
486 from rhodecode.config import conf
490 from rhodecode.config import conf
487
491
488 path = os.path.join(root_path, 'rcextensions', '__init__.py')
492 path = os.path.join(root_path, 'rcextensions', '__init__.py')
489 if os.path.isfile(path):
493 if os.path.isfile(path):
490 rcext = create_module('rc', path)
494 rcext = create_module('rc', path)
491 EXT = rhodecode.EXTENSIONS = rcext
495 EXT = rhodecode.EXTENSIONS = rcext
492 log.debug('Found rcextensions now loading %s...' % rcext)
496 log.debug('Found rcextensions now loading %s...' % rcext)
493
497
494 # Additional mappings that are not present in the pygments lexers
498 # Additional mappings that are not present in the pygments lexers
495 conf.LANGUAGES_EXTENSIONS_MAP.update(getattr(EXT, 'EXTRA_MAPPINGS', {}))
499 conf.LANGUAGES_EXTENSIONS_MAP.update(getattr(EXT, 'EXTRA_MAPPINGS', {}))
496
500
497 #OVERRIDE OUR EXTENSIONS FROM RC-EXTENSIONS (if present)
501 #OVERRIDE OUR EXTENSIONS FROM RC-EXTENSIONS (if present)
498
502
499 if getattr(EXT, 'INDEX_EXTENSIONS', []) != []:
503 if getattr(EXT, 'INDEX_EXTENSIONS', []) != []:
500 log.debug('settings custom INDEX_EXTENSIONS')
504 log.debug('settings custom INDEX_EXTENSIONS')
501 conf.INDEX_EXTENSIONS = getattr(EXT, 'INDEX_EXTENSIONS', [])
505 conf.INDEX_EXTENSIONS = getattr(EXT, 'INDEX_EXTENSIONS', [])
502
506
503 #ADDITIONAL MAPPINGS
507 #ADDITIONAL MAPPINGS
504 log.debug('adding extra into INDEX_EXTENSIONS')
508 log.debug('adding extra into INDEX_EXTENSIONS')
505 conf.INDEX_EXTENSIONS.extend(getattr(EXT, 'EXTRA_INDEX_EXTENSIONS', []))
509 conf.INDEX_EXTENSIONS.extend(getattr(EXT, 'EXTRA_INDEX_EXTENSIONS', []))
506
510
507
511
508 #==============================================================================
512 #==============================================================================
509 # TEST FUNCTIONS AND CREATORS
513 # TEST FUNCTIONS AND CREATORS
510 #==============================================================================
514 #==============================================================================
511 def create_test_index(repo_location, config, full_index):
515 def create_test_index(repo_location, config, full_index):
512 """
516 """
513 Makes default test index
517 Makes default test index
514
518
515 :param config: test config
519 :param config: test config
516 :param full_index:
520 :param full_index:
517 """
521 """
518
522
519 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
523 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
520 from rhodecode.lib.pidlock import DaemonLock, LockHeld
524 from rhodecode.lib.pidlock import DaemonLock, LockHeld
521
525
522 repo_location = repo_location
526 repo_location = repo_location
523
527
524 index_location = os.path.join(config['app_conf']['index_dir'])
528 index_location = os.path.join(config['app_conf']['index_dir'])
525 if not os.path.exists(index_location):
529 if not os.path.exists(index_location):
526 os.makedirs(index_location)
530 os.makedirs(index_location)
527
531
528 try:
532 try:
529 l = DaemonLock(file_=jn(dn(index_location), 'make_index.lock'))
533 l = DaemonLock(file_=jn(dn(index_location), 'make_index.lock'))
530 WhooshIndexingDaemon(index_location=index_location,
534 WhooshIndexingDaemon(index_location=index_location,
531 repo_location=repo_location)\
535 repo_location=repo_location)\
532 .run(full_index=full_index)
536 .run(full_index=full_index)
533 l.release()
537 l.release()
534 except LockHeld:
538 except LockHeld:
535 pass
539 pass
536
540
537
541
538 def create_test_env(repos_test_path, config):
542 def create_test_env(repos_test_path, config):
539 """
543 """
540 Makes a fresh database and
544 Makes a fresh database and
541 install test repository into tmp dir
545 install test repository into tmp dir
542 """
546 """
543 from rhodecode.lib.db_manage import DbManage
547 from rhodecode.lib.db_manage import DbManage
544 from rhodecode.tests import HG_REPO, GIT_REPO, TESTS_TMP_PATH
548 from rhodecode.tests import HG_REPO, GIT_REPO, TESTS_TMP_PATH
545
549
546 # PART ONE create db
550 # PART ONE create db
547 dbconf = config['sqlalchemy.db1.url']
551 dbconf = config['sqlalchemy.db1.url']
548 log.debug('making test db %s' % dbconf)
552 log.debug('making test db %s' % dbconf)
549
553
550 # create test dir if it doesn't exist
554 # create test dir if it doesn't exist
551 if not os.path.isdir(repos_test_path):
555 if not os.path.isdir(repos_test_path):
552 log.debug('Creating testdir %s' % repos_test_path)
556 log.debug('Creating testdir %s' % repos_test_path)
553 os.makedirs(repos_test_path)
557 os.makedirs(repos_test_path)
554
558
555 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
559 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
556 tests=True)
560 tests=True)
557 dbmanage.create_tables(override=True)
561 dbmanage.create_tables(override=True)
558 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
562 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
559 dbmanage.create_default_user()
563 dbmanage.create_default_user()
560 dbmanage.admin_prompt()
564 dbmanage.admin_prompt()
561 dbmanage.create_permissions()
565 dbmanage.create_permissions()
562 dbmanage.populate_default_permissions()
566 dbmanage.populate_default_permissions()
563 Session().commit()
567 Session().commit()
564 # PART TWO make test repo
568 # PART TWO make test repo
565 log.debug('making test vcs repositories')
569 log.debug('making test vcs repositories')
566
570
567 idx_path = config['app_conf']['index_dir']
571 idx_path = config['app_conf']['index_dir']
568 data_path = config['app_conf']['cache_dir']
572 data_path = config['app_conf']['cache_dir']
569
573
570 #clean index and data
574 #clean index and data
571 if idx_path and os.path.exists(idx_path):
575 if idx_path and os.path.exists(idx_path):
572 log.debug('remove %s' % idx_path)
576 log.debug('remove %s' % idx_path)
573 shutil.rmtree(idx_path)
577 shutil.rmtree(idx_path)
574
578
575 if data_path and os.path.exists(data_path):
579 if data_path and os.path.exists(data_path):
576 log.debug('remove %s' % data_path)
580 log.debug('remove %s' % data_path)
577 shutil.rmtree(data_path)
581 shutil.rmtree(data_path)
578
582
579 #CREATE DEFAULT TEST REPOS
583 #CREATE DEFAULT TEST REPOS
580 cur_dir = dn(dn(abspath(__file__)))
584 cur_dir = dn(dn(abspath(__file__)))
581 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
585 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
582 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
586 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
583 tar.close()
587 tar.close()
584
588
585 cur_dir = dn(dn(abspath(__file__)))
589 cur_dir = dn(dn(abspath(__file__)))
586 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_git.tar.gz"))
590 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_git.tar.gz"))
587 tar.extractall(jn(TESTS_TMP_PATH, GIT_REPO))
591 tar.extractall(jn(TESTS_TMP_PATH, GIT_REPO))
588 tar.close()
592 tar.close()
589
593
590 #LOAD VCS test stuff
594 #LOAD VCS test stuff
591 from rhodecode.tests.vcs import setup_package
595 from rhodecode.tests.vcs import setup_package
592 setup_package()
596 setup_package()
593
597
594
598
595 #==============================================================================
599 #==============================================================================
596 # PASTER COMMANDS
600 # PASTER COMMANDS
597 #==============================================================================
601 #==============================================================================
598 class BasePasterCommand(Command):
602 class BasePasterCommand(Command):
599 """
603 """
600 Abstract Base Class for paster commands.
604 Abstract Base Class for paster commands.
601
605
602 The celery commands are somewhat aggressive about loading
606 The celery commands are somewhat aggressive about loading
603 celery.conf, and since our module sets the `CELERY_LOADER`
607 celery.conf, and since our module sets the `CELERY_LOADER`
604 environment variable to our loader, we have to bootstrap a bit and
608 environment variable to our loader, we have to bootstrap a bit and
605 make sure we've had a chance to load the pylons config off of the
609 make sure we've had a chance to load the pylons config off of the
606 command line, otherwise everything fails.
610 command line, otherwise everything fails.
607 """
611 """
608 min_args = 1
612 min_args = 1
609 min_args_error = "Please provide a paster config file as an argument."
613 min_args_error = "Please provide a paster config file as an argument."
610 takes_config_file = 1
614 takes_config_file = 1
611 requires_config_file = True
615 requires_config_file = True
612
616
613 def notify_msg(self, msg, log=False):
617 def notify_msg(self, msg, log=False):
614 """Make a notification to user, additionally if logger is passed
618 """Make a notification to user, additionally if logger is passed
615 it logs this action using given logger
619 it logs this action using given logger
616
620
617 :param msg: message that will be printed to user
621 :param msg: message that will be printed to user
618 :param log: logging instance, to use to additionally log this message
622 :param log: logging instance, to use to additionally log this message
619
623
620 """
624 """
621 if log and isinstance(log, logging):
625 if log and isinstance(log, logging):
622 log(msg)
626 log(msg)
623
627
624 def run(self, args):
628 def run(self, args):
625 """
629 """
626 Overrides Command.run
630 Overrides Command.run
627
631
628 Checks for a config file argument and loads it.
632 Checks for a config file argument and loads it.
629 """
633 """
630 if len(args) < self.min_args:
634 if len(args) < self.min_args:
631 raise BadCommand(
635 raise BadCommand(
632 self.min_args_error % {'min_args': self.min_args,
636 self.min_args_error % {'min_args': self.min_args,
633 'actual_args': len(args)})
637 'actual_args': len(args)})
634
638
635 # Decrement because we're going to lob off the first argument.
639 # Decrement because we're going to lob off the first argument.
636 # @@ This is hacky
640 # @@ This is hacky
637 self.min_args -= 1
641 self.min_args -= 1
638 self.bootstrap_config(args[0])
642 self.bootstrap_config(args[0])
639 self.update_parser()
643 self.update_parser()
640 return super(BasePasterCommand, self).run(args[1:])
644 return super(BasePasterCommand, self).run(args[1:])
641
645
642 def update_parser(self):
646 def update_parser(self):
643 """
647 """
644 Abstract method. Allows for the class's parser to be updated
648 Abstract method. Allows for the class's parser to be updated
645 before the superclass's `run` method is called. Necessary to
649 before the superclass's `run` method is called. Necessary to
646 allow options/arguments to be passed through to the underlying
650 allow options/arguments to be passed through to the underlying
647 celery command.
651 celery command.
648 """
652 """
649 raise NotImplementedError("Abstract Method.")
653 raise NotImplementedError("Abstract Method.")
650
654
651 def bootstrap_config(self, conf):
655 def bootstrap_config(self, conf):
652 """
656 """
653 Loads the pylons configuration.
657 Loads the pylons configuration.
654 """
658 """
655 from pylons import config as pylonsconfig
659 from pylons import config as pylonsconfig
656
660
657 self.path_to_ini_file = os.path.realpath(conf)
661 self.path_to_ini_file = os.path.realpath(conf)
658 conf = paste.deploy.appconfig('config:' + self.path_to_ini_file)
662 conf = paste.deploy.appconfig('config:' + self.path_to_ini_file)
659 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
663 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
@@ -1,1679 +1,1683 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 from collections import defaultdict
31 from collections import defaultdict
32
32
33 from sqlalchemy import *
33 from sqlalchemy import *
34 from sqlalchemy.ext.hybrid import hybrid_property
34 from sqlalchemy.ext.hybrid import hybrid_property
35 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
35 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
36 from sqlalchemy.exc import DatabaseError
36 from sqlalchemy.exc import DatabaseError
37 from beaker.cache import cache_region, region_invalidate
37 from beaker.cache import cache_region, region_invalidate
38 from webob.exc import HTTPNotFound
38 from webob.exc import HTTPNotFound
39
39
40 from pylons.i18n.translation import lazy_ugettext as _
40 from pylons.i18n.translation import lazy_ugettext as _
41
41
42 from rhodecode.lib.vcs import get_backend
42 from rhodecode.lib.vcs import get_backend
43 from rhodecode.lib.vcs.utils.helpers import get_scm
43 from rhodecode.lib.vcs.utils.helpers import get_scm
44 from rhodecode.lib.vcs.exceptions import VCSError
44 from rhodecode.lib.vcs.exceptions import VCSError
45 from rhodecode.lib.vcs.utils.lazy import LazyProperty
45 from rhodecode.lib.vcs.utils.lazy import LazyProperty
46
46
47 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
47 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
48 safe_unicode
48 safe_unicode
49 from rhodecode.lib.compat import json
49 from rhodecode.lib.compat import json
50 from rhodecode.lib.caching_query import FromCache
50 from rhodecode.lib.caching_query import FromCache
51
51
52 from rhodecode.model.meta import Base, Session
52 from rhodecode.model.meta import Base, Session
53
53
54 URL_SEP = '/'
54 URL_SEP = '/'
55 log = logging.getLogger(__name__)
55 log = logging.getLogger(__name__)
56
56
57 #==============================================================================
57 #==============================================================================
58 # BASE CLASSES
58 # BASE CLASSES
59 #==============================================================================
59 #==============================================================================
60
60
61 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
61 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
62
62
63
63
64 class BaseModel(object):
64 class BaseModel(object):
65 """
65 """
66 Base Model for all classess
66 Base Model for all classess
67 """
67 """
68
68
69 @classmethod
69 @classmethod
70 def _get_keys(cls):
70 def _get_keys(cls):
71 """return column names for this model """
71 """return column names for this model """
72 return class_mapper(cls).c.keys()
72 return class_mapper(cls).c.keys()
73
73
74 def get_dict(self):
74 def get_dict(self):
75 """
75 """
76 return dict with keys and values corresponding
76 return dict with keys and values corresponding
77 to this model data """
77 to this model data """
78
78
79 d = {}
79 d = {}
80 for k in self._get_keys():
80 for k in self._get_keys():
81 d[k] = getattr(self, k)
81 d[k] = getattr(self, k)
82
82
83 # also use __json__() if present to get additional fields
83 # also use __json__() if present to get additional fields
84 _json_attr = getattr(self, '__json__', None)
84 _json_attr = getattr(self, '__json__', None)
85 if _json_attr:
85 if _json_attr:
86 # update with attributes from __json__
86 # update with attributes from __json__
87 if callable(_json_attr):
87 if callable(_json_attr):
88 _json_attr = _json_attr()
88 _json_attr = _json_attr()
89 for k, val in _json_attr.iteritems():
89 for k, val in _json_attr.iteritems():
90 d[k] = val
90 d[k] = val
91 return d
91 return d
92
92
93 def get_appstruct(self):
93 def get_appstruct(self):
94 """return list with keys and values tupples corresponding
94 """return list with keys and values tupples corresponding
95 to this model data """
95 to this model data """
96
96
97 l = []
97 l = []
98 for k in self._get_keys():
98 for k in self._get_keys():
99 l.append((k, getattr(self, k),))
99 l.append((k, getattr(self, k),))
100 return l
100 return l
101
101
102 def populate_obj(self, populate_dict):
102 def populate_obj(self, populate_dict):
103 """populate model with data from given populate_dict"""
103 """populate model with data from given populate_dict"""
104
104
105 for k in self._get_keys():
105 for k in self._get_keys():
106 if k in populate_dict:
106 if k in populate_dict:
107 setattr(self, k, populate_dict[k])
107 setattr(self, k, populate_dict[k])
108
108
109 @classmethod
109 @classmethod
110 def query(cls):
110 def query(cls):
111 return Session().query(cls)
111 return Session().query(cls)
112
112
113 @classmethod
113 @classmethod
114 def get(cls, id_):
114 def get(cls, id_):
115 if id_:
115 if id_:
116 return cls.query().get(id_)
116 return cls.query().get(id_)
117
117
118 @classmethod
118 @classmethod
119 def get_or_404(cls, id_):
119 def get_or_404(cls, id_):
120 if id_:
120 if id_:
121 res = cls.query().get(id_)
121 res = cls.query().get(id_)
122 if not res:
122 if not res:
123 raise HTTPNotFound
123 raise HTTPNotFound
124 return res
124 return res
125
125
126 @classmethod
126 @classmethod
127 def getAll(cls):
127 def getAll(cls):
128 return cls.query().all()
128 return cls.query().all()
129
129
130 @classmethod
130 @classmethod
131 def delete(cls, id_):
131 def delete(cls, id_):
132 obj = cls.query().get(id_)
132 obj = cls.query().get(id_)
133 Session().delete(obj)
133 Session().delete(obj)
134
134
135 def __repr__(self):
135 def __repr__(self):
136 if hasattr(self, '__unicode__'):
136 if hasattr(self, '__unicode__'):
137 # python repr needs to return str
137 # python repr needs to return str
138 return safe_str(self.__unicode__())
138 return safe_str(self.__unicode__())
139 return '<DB:%s>' % (self.__class__.__name__)
139 return '<DB:%s>' % (self.__class__.__name__)
140
140
141
141
142 class RhodeCodeSetting(Base, BaseModel):
142 class RhodeCodeSetting(Base, BaseModel):
143 __tablename__ = 'rhodecode_settings'
143 __tablename__ = 'rhodecode_settings'
144 __table_args__ = (
144 __table_args__ = (
145 UniqueConstraint('app_settings_name'),
145 UniqueConstraint('app_settings_name'),
146 {'extend_existing': True, 'mysql_engine': 'InnoDB',
146 {'extend_existing': True, 'mysql_engine': 'InnoDB',
147 'mysql_charset': 'utf8'}
147 'mysql_charset': 'utf8'}
148 )
148 )
149 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
149 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
150 app_settings_name = Column("app_settings_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
150 app_settings_name = Column("app_settings_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
151 _app_settings_value = Column("app_settings_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
151 _app_settings_value = Column("app_settings_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
152
152
153 def __init__(self, k='', v=''):
153 def __init__(self, k='', v=''):
154 self.app_settings_name = k
154 self.app_settings_name = k
155 self.app_settings_value = v
155 self.app_settings_value = v
156
156
157 @validates('_app_settings_value')
157 @validates('_app_settings_value')
158 def validate_settings_value(self, key, val):
158 def validate_settings_value(self, key, val):
159 assert type(val) == unicode
159 assert type(val) == unicode
160 return val
160 return val
161
161
162 @hybrid_property
162 @hybrid_property
163 def app_settings_value(self):
163 def app_settings_value(self):
164 v = self._app_settings_value
164 v = self._app_settings_value
165 if self.app_settings_name == 'ldap_active':
165 if self.app_settings_name == 'ldap_active':
166 v = str2bool(v)
166 v = str2bool(v)
167 return v
167 return v
168
168
169 @app_settings_value.setter
169 @app_settings_value.setter
170 def app_settings_value(self, val):
170 def app_settings_value(self, val):
171 """
171 """
172 Setter that will always make sure we use unicode in app_settings_value
172 Setter that will always make sure we use unicode in app_settings_value
173
173
174 :param val:
174 :param val:
175 """
175 """
176 self._app_settings_value = safe_unicode(val)
176 self._app_settings_value = safe_unicode(val)
177
177
178 def __unicode__(self):
178 def __unicode__(self):
179 return u"<%s('%s:%s')>" % (
179 return u"<%s('%s:%s')>" % (
180 self.__class__.__name__,
180 self.__class__.__name__,
181 self.app_settings_name, self.app_settings_value
181 self.app_settings_name, self.app_settings_value
182 )
182 )
183
183
184 @classmethod
184 @classmethod
185 def get_by_name(cls, key):
185 def get_by_name(cls, key):
186 return cls.query()\
186 return cls.query()\
187 .filter(cls.app_settings_name == key).scalar()
187 .filter(cls.app_settings_name == key).scalar()
188
188
189 @classmethod
189 @classmethod
190 def get_by_name_or_create(cls, key):
190 def get_by_name_or_create(cls, key):
191 res = cls.get_by_name(key)
191 res = cls.get_by_name(key)
192 if not res:
192 if not res:
193 res = cls(key)
193 res = cls(key)
194 return res
194 return res
195
195
196 @classmethod
196 @classmethod
197 def get_app_settings(cls, cache=False):
197 def get_app_settings(cls, cache=False):
198
198
199 ret = cls.query()
199 ret = cls.query()
200
200
201 if cache:
201 if cache:
202 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
202 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
203
203
204 if not ret:
204 if not ret:
205 raise Exception('Could not get application settings !')
205 raise Exception('Could not get application settings !')
206 settings = {}
206 settings = {}
207 for each in ret:
207 for each in ret:
208 settings['rhodecode_' + each.app_settings_name] = \
208 settings['rhodecode_' + each.app_settings_name] = \
209 each.app_settings_value
209 each.app_settings_value
210
210
211 return settings
211 return settings
212
212
213 @classmethod
213 @classmethod
214 def get_ldap_settings(cls, cache=False):
214 def get_ldap_settings(cls, cache=False):
215 ret = cls.query()\
215 ret = cls.query()\
216 .filter(cls.app_settings_name.startswith('ldap_')).all()
216 .filter(cls.app_settings_name.startswith('ldap_')).all()
217 fd = {}
217 fd = {}
218 for row in ret:
218 for row in ret:
219 fd.update({row.app_settings_name: row.app_settings_value})
219 fd.update({row.app_settings_name: row.app_settings_value})
220
220
221 return fd
221 return fd
222
222
223
223
224 class RhodeCodeUi(Base, BaseModel):
224 class RhodeCodeUi(Base, BaseModel):
225 __tablename__ = 'rhodecode_ui'
225 __tablename__ = 'rhodecode_ui'
226 __table_args__ = (
226 __table_args__ = (
227 UniqueConstraint('ui_key'),
227 UniqueConstraint('ui_key'),
228 {'extend_existing': True, 'mysql_engine': 'InnoDB',
228 {'extend_existing': True, 'mysql_engine': 'InnoDB',
229 'mysql_charset': 'utf8'}
229 'mysql_charset': 'utf8'}
230 )
230 )
231
231
232 HOOK_UPDATE = 'changegroup.update'
232 HOOK_UPDATE = 'changegroup.update'
233 HOOK_REPO_SIZE = 'changegroup.repo_size'
233 HOOK_REPO_SIZE = 'changegroup.repo_size'
234 HOOK_PUSH = 'changegroup.push_logger'
234 HOOK_PUSH = 'changegroup.push_logger'
235 HOOK_PULL = 'preoutgoing.pull_logger'
235 HOOK_PULL = 'preoutgoing.pull_logger'
236
236
237 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
237 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
238 ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
238 ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
239 ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
239 ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
240 ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
240 ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
241 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
241 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
242
242
243 @classmethod
243 @classmethod
244 def get_by_key(cls, key):
244 def get_by_key(cls, key):
245 return cls.query().filter(cls.ui_key == key)
245 return cls.query().filter(cls.ui_key == key)
246
246
247 @classmethod
247 @classmethod
248 def get_builtin_hooks(cls):
248 def get_builtin_hooks(cls):
249 q = cls.query()
249 q = cls.query()
250 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
250 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
251 cls.HOOK_REPO_SIZE,
251 cls.HOOK_REPO_SIZE,
252 cls.HOOK_PUSH, cls.HOOK_PULL]))
252 cls.HOOK_PUSH, cls.HOOK_PULL]))
253 return q.all()
253 return q.all()
254
254
255 @classmethod
255 @classmethod
256 def get_custom_hooks(cls):
256 def get_custom_hooks(cls):
257 q = cls.query()
257 q = cls.query()
258 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
258 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
259 cls.HOOK_REPO_SIZE,
259 cls.HOOK_REPO_SIZE,
260 cls.HOOK_PUSH, cls.HOOK_PULL]))
260 cls.HOOK_PUSH, cls.HOOK_PULL]))
261 q = q.filter(cls.ui_section == 'hooks')
261 q = q.filter(cls.ui_section == 'hooks')
262 return q.all()
262 return q.all()
263
263
264 @classmethod
264 @classmethod
265 def get_repos_location(cls):
265 def get_repos_location(cls):
266 return cls.get_by_key('/').one().ui_value
266 return cls.get_by_key('/').one().ui_value
267
267
268 @classmethod
268 @classmethod
269 def create_or_update_hook(cls, key, val):
269 def create_or_update_hook(cls, key, val):
270 new_ui = cls.get_by_key(key).scalar() or cls()
270 new_ui = cls.get_by_key(key).scalar() or cls()
271 new_ui.ui_section = 'hooks'
271 new_ui.ui_section = 'hooks'
272 new_ui.ui_active = True
272 new_ui.ui_active = True
273 new_ui.ui_key = key
273 new_ui.ui_key = key
274 new_ui.ui_value = val
274 new_ui.ui_value = val
275
275
276 Session().add(new_ui)
276 Session().add(new_ui)
277
277
278
278
279 class User(Base, BaseModel):
279 class User(Base, BaseModel):
280 __tablename__ = 'users'
280 __tablename__ = 'users'
281 __table_args__ = (
281 __table_args__ = (
282 UniqueConstraint('username'), UniqueConstraint('email'),
282 UniqueConstraint('username'), UniqueConstraint('email'),
283 {'extend_existing': True, 'mysql_engine': 'InnoDB',
283 {'extend_existing': True, 'mysql_engine': 'InnoDB',
284 'mysql_charset': 'utf8'}
284 'mysql_charset': 'utf8'}
285 )
285 )
286 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
286 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
287 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
287 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
288 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
288 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
289 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
289 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
290 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
290 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
291 name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
291 name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
292 lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
292 lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
293 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
293 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
294 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
294 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
295 ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
295 ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
296 api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
296 api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
297
297
298 user_log = relationship('UserLog', cascade='all')
298 user_log = relationship('UserLog', cascade='all')
299 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
299 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
300
300
301 repositories = relationship('Repository')
301 repositories = relationship('Repository')
302 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
302 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
303 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
303 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
304 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
304 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
305
305
306 group_member = relationship('UsersGroupMember', cascade='all')
306 group_member = relationship('UsersGroupMember', cascade='all')
307
307
308 notifications = relationship('UserNotification', cascade='all')
308 notifications = relationship('UserNotification', cascade='all')
309 # notifications assigned to this user
309 # notifications assigned to this user
310 user_created_notifications = relationship('Notification', cascade='all')
310 user_created_notifications = relationship('Notification', cascade='all')
311 # comments created by this user
311 # comments created by this user
312 user_comments = relationship('ChangesetComment', cascade='all')
312 user_comments = relationship('ChangesetComment', cascade='all')
313 #extra emails for this user
313 #extra emails for this user
314 user_emails = relationship('UserEmailMap', cascade='all')
314 user_emails = relationship('UserEmailMap', cascade='all')
315
315
316 @hybrid_property
316 @hybrid_property
317 def email(self):
317 def email(self):
318 return self._email
318 return self._email
319
319
320 @email.setter
320 @email.setter
321 def email(self, val):
321 def email(self, val):
322 self._email = val.lower() if val else None
322 self._email = val.lower() if val else None
323
323
324 @property
324 @property
325 def emails(self):
325 def emails(self):
326 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
326 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
327 return [self.email] + [x.email for x in other]
327 return [self.email] + [x.email for x in other]
328
328
329 @property
329 @property
330 def full_name(self):
330 def full_name(self):
331 return '%s %s' % (self.name, self.lastname)
331 return '%s %s' % (self.name, self.lastname)
332
332
333 @property
333 @property
334 def full_name_or_username(self):
334 def full_name_or_username(self):
335 return ('%s %s' % (self.name, self.lastname)
335 return ('%s %s' % (self.name, self.lastname)
336 if (self.name and self.lastname) else self.username)
336 if (self.name and self.lastname) else self.username)
337
337
338 @property
338 @property
339 def full_contact(self):
339 def full_contact(self):
340 return '%s %s <%s>' % (self.name, self.lastname, self.email)
340 return '%s %s <%s>' % (self.name, self.lastname, self.email)
341
341
342 @property
342 @property
343 def short_contact(self):
343 def short_contact(self):
344 return '%s %s' % (self.name, self.lastname)
344 return '%s %s' % (self.name, self.lastname)
345
345
346 @property
346 @property
347 def is_admin(self):
347 def is_admin(self):
348 return self.admin
348 return self.admin
349
349
350 def __unicode__(self):
350 def __unicode__(self):
351 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
351 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
352 self.user_id, self.username)
352 self.user_id, self.username)
353
353
354 @classmethod
354 @classmethod
355 def get_by_username(cls, username, case_insensitive=False, cache=False):
355 def get_by_username(cls, username, case_insensitive=False, cache=False):
356 if case_insensitive:
356 if case_insensitive:
357 q = cls.query().filter(cls.username.ilike(username))
357 q = cls.query().filter(cls.username.ilike(username))
358 else:
358 else:
359 q = cls.query().filter(cls.username == username)
359 q = cls.query().filter(cls.username == username)
360
360
361 if cache:
361 if cache:
362 q = q.options(FromCache(
362 q = q.options(FromCache(
363 "sql_cache_short",
363 "sql_cache_short",
364 "get_user_%s" % _hash_key(username)
364 "get_user_%s" % _hash_key(username)
365 )
365 )
366 )
366 )
367 return q.scalar()
367 return q.scalar()
368
368
369 @classmethod
369 @classmethod
370 def get_by_api_key(cls, api_key, cache=False):
370 def get_by_api_key(cls, api_key, cache=False):
371 q = cls.query().filter(cls.api_key == api_key)
371 q = cls.query().filter(cls.api_key == api_key)
372
372
373 if cache:
373 if cache:
374 q = q.options(FromCache("sql_cache_short",
374 q = q.options(FromCache("sql_cache_short",
375 "get_api_key_%s" % api_key))
375 "get_api_key_%s" % api_key))
376 return q.scalar()
376 return q.scalar()
377
377
378 @classmethod
378 @classmethod
379 def get_by_email(cls, email, case_insensitive=False, cache=False):
379 def get_by_email(cls, email, case_insensitive=False, cache=False):
380 if case_insensitive:
380 if case_insensitive:
381 q = cls.query().filter(cls.email.ilike(email))
381 q = cls.query().filter(cls.email.ilike(email))
382 else:
382 else:
383 q = cls.query().filter(cls.email == email)
383 q = cls.query().filter(cls.email == email)
384
384
385 if cache:
385 if cache:
386 q = q.options(FromCache("sql_cache_short",
386 q = q.options(FromCache("sql_cache_short",
387 "get_email_key_%s" % email))
387 "get_email_key_%s" % email))
388
388
389 ret = q.scalar()
389 ret = q.scalar()
390 if ret is None:
390 if ret is None:
391 q = UserEmailMap.query()
391 q = UserEmailMap.query()
392 # try fetching in alternate email map
392 # try fetching in alternate email map
393 if case_insensitive:
393 if case_insensitive:
394 q = q.filter(UserEmailMap.email.ilike(email))
394 q = q.filter(UserEmailMap.email.ilike(email))
395 else:
395 else:
396 q = q.filter(UserEmailMap.email == email)
396 q = q.filter(UserEmailMap.email == email)
397 q = q.options(joinedload(UserEmailMap.user))
397 q = q.options(joinedload(UserEmailMap.user))
398 if cache:
398 if cache:
399 q = q.options(FromCache("sql_cache_short",
399 q = q.options(FromCache("sql_cache_short",
400 "get_email_map_key_%s" % email))
400 "get_email_map_key_%s" % email))
401 ret = getattr(q.scalar(), 'user', None)
401 ret = getattr(q.scalar(), 'user', None)
402
402
403 return ret
403 return ret
404
404
405 def update_lastlogin(self):
405 def update_lastlogin(self):
406 """Update user lastlogin"""
406 """Update user lastlogin"""
407 self.last_login = datetime.datetime.now()
407 self.last_login = datetime.datetime.now()
408 Session().add(self)
408 Session().add(self)
409 log.debug('updated user %s lastlogin' % self.username)
409 log.debug('updated user %s lastlogin' % self.username)
410
410
411 def get_api_data(self):
411 def get_api_data(self):
412 """
412 """
413 Common function for generating user related data for API
413 Common function for generating user related data for API
414 """
414 """
415 user = self
415 user = self
416 data = dict(
416 data = dict(
417 user_id=user.user_id,
417 user_id=user.user_id,
418 username=user.username,
418 username=user.username,
419 firstname=user.name,
419 firstname=user.name,
420 lastname=user.lastname,
420 lastname=user.lastname,
421 email=user.email,
421 email=user.email,
422 emails=user.emails,
422 emails=user.emails,
423 api_key=user.api_key,
423 api_key=user.api_key,
424 active=user.active,
424 active=user.active,
425 admin=user.admin,
425 admin=user.admin,
426 ldap_dn=user.ldap_dn,
426 ldap_dn=user.ldap_dn,
427 last_login=user.last_login,
427 last_login=user.last_login,
428 )
428 )
429 return data
429 return data
430
430
431 def __json__(self):
431 def __json__(self):
432 data = dict(
432 data = dict(
433 full_name=self.full_name,
433 full_name=self.full_name,
434 full_name_or_username=self.full_name_or_username,
434 full_name_or_username=self.full_name_or_username,
435 short_contact=self.short_contact,
435 short_contact=self.short_contact,
436 full_contact=self.full_contact
436 full_contact=self.full_contact
437 )
437 )
438 data.update(self.get_api_data())
438 data.update(self.get_api_data())
439 return data
439 return data
440
440
441
441
442 class UserEmailMap(Base, BaseModel):
442 class UserEmailMap(Base, BaseModel):
443 __tablename__ = 'user_email_map'
443 __tablename__ = 'user_email_map'
444 __table_args__ = (
444 __table_args__ = (
445 Index('uem_email_idx', 'email'),
445 Index('uem_email_idx', 'email'),
446 UniqueConstraint('email'),
446 UniqueConstraint('email'),
447 {'extend_existing': True, 'mysql_engine': 'InnoDB',
447 {'extend_existing': True, 'mysql_engine': 'InnoDB',
448 'mysql_charset': 'utf8'}
448 'mysql_charset': 'utf8'}
449 )
449 )
450 __mapper_args__ = {}
450 __mapper_args__ = {}
451
451
452 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
452 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
453 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
453 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
454 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
454 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
455
455
456 user = relationship('User', lazy='joined')
456 user = relationship('User', lazy='joined')
457
457
458 @validates('_email')
458 @validates('_email')
459 def validate_email(self, key, email):
459 def validate_email(self, key, email):
460 # check if this email is not main one
460 # check if this email is not main one
461 main_email = Session().query(User).filter(User.email == email).scalar()
461 main_email = Session().query(User).filter(User.email == email).scalar()
462 if main_email is not None:
462 if main_email is not None:
463 raise AttributeError('email %s is present is user table' % email)
463 raise AttributeError('email %s is present is user table' % email)
464 return email
464 return email
465
465
466 @hybrid_property
466 @hybrid_property
467 def email(self):
467 def email(self):
468 return self._email
468 return self._email
469
469
470 @email.setter
470 @email.setter
471 def email(self, val):
471 def email(self, val):
472 self._email = val.lower() if val else None
472 self._email = val.lower() if val else None
473
473
474
474
475 class UserLog(Base, BaseModel):
475 class UserLog(Base, BaseModel):
476 __tablename__ = 'user_logs'
476 __tablename__ = 'user_logs'
477 __table_args__ = (
477 __table_args__ = (
478 {'extend_existing': True, 'mysql_engine': 'InnoDB',
478 {'extend_existing': True, 'mysql_engine': 'InnoDB',
479 'mysql_charset': 'utf8'},
479 'mysql_charset': 'utf8'},
480 )
480 )
481 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
481 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
482 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
482 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
483 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
483 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
484 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
484 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
485 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
485 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
486 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
486 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
487 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
487 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
488
488
489 @property
489 @property
490 def action_as_day(self):
490 def action_as_day(self):
491 return datetime.date(*self.action_date.timetuple()[:3])
491 return datetime.date(*self.action_date.timetuple()[:3])
492
492
493 user = relationship('User')
493 user = relationship('User')
494 repository = relationship('Repository', cascade='')
494 repository = relationship('Repository', cascade='')
495
495
496
496
497 class UsersGroup(Base, BaseModel):
497 class UsersGroup(Base, BaseModel):
498 __tablename__ = 'users_groups'
498 __tablename__ = 'users_groups'
499 __table_args__ = (
499 __table_args__ = (
500 {'extend_existing': True, 'mysql_engine': 'InnoDB',
500 {'extend_existing': True, 'mysql_engine': 'InnoDB',
501 'mysql_charset': 'utf8'},
501 'mysql_charset': 'utf8'},
502 )
502 )
503
503
504 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
504 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
505 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
505 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
506 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
506 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
507
507
508 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
508 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
509 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
509 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
510 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
510 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
511
511
512 def __unicode__(self):
512 def __unicode__(self):
513 return u'<userGroup(%s)>' % (self.users_group_name)
513 return u'<userGroup(%s)>' % (self.users_group_name)
514
514
515 @classmethod
515 @classmethod
516 def get_by_group_name(cls, group_name, cache=False,
516 def get_by_group_name(cls, group_name, cache=False,
517 case_insensitive=False):
517 case_insensitive=False):
518 if case_insensitive:
518 if case_insensitive:
519 q = cls.query().filter(cls.users_group_name.ilike(group_name))
519 q = cls.query().filter(cls.users_group_name.ilike(group_name))
520 else:
520 else:
521 q = cls.query().filter(cls.users_group_name == group_name)
521 q = cls.query().filter(cls.users_group_name == group_name)
522 if cache:
522 if cache:
523 q = q.options(FromCache(
523 q = q.options(FromCache(
524 "sql_cache_short",
524 "sql_cache_short",
525 "get_user_%s" % _hash_key(group_name)
525 "get_user_%s" % _hash_key(group_name)
526 )
526 )
527 )
527 )
528 return q.scalar()
528 return q.scalar()
529
529
530 @classmethod
530 @classmethod
531 def get(cls, users_group_id, cache=False):
531 def get(cls, users_group_id, cache=False):
532 users_group = cls.query()
532 users_group = cls.query()
533 if cache:
533 if cache:
534 users_group = users_group.options(FromCache("sql_cache_short",
534 users_group = users_group.options(FromCache("sql_cache_short",
535 "get_users_group_%s" % users_group_id))
535 "get_users_group_%s" % users_group_id))
536 return users_group.get(users_group_id)
536 return users_group.get(users_group_id)
537
537
538 def get_api_data(self):
538 def get_api_data(self):
539 users_group = self
539 users_group = self
540
540
541 data = dict(
541 data = dict(
542 users_group_id=users_group.users_group_id,
542 users_group_id=users_group.users_group_id,
543 group_name=users_group.users_group_name,
543 group_name=users_group.users_group_name,
544 active=users_group.users_group_active,
544 active=users_group.users_group_active,
545 )
545 )
546
546
547 return data
547 return data
548
548
549
549
550 class UsersGroupMember(Base, BaseModel):
550 class UsersGroupMember(Base, BaseModel):
551 __tablename__ = 'users_groups_members'
551 __tablename__ = 'users_groups_members'
552 __table_args__ = (
552 __table_args__ = (
553 {'extend_existing': True, 'mysql_engine': 'InnoDB',
553 {'extend_existing': True, 'mysql_engine': 'InnoDB',
554 'mysql_charset': 'utf8'},
554 'mysql_charset': 'utf8'},
555 )
555 )
556
556
557 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
557 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
558 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
558 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
559 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
559 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
560
560
561 user = relationship('User', lazy='joined')
561 user = relationship('User', lazy='joined')
562 users_group = relationship('UsersGroup')
562 users_group = relationship('UsersGroup')
563
563
564 def __init__(self, gr_id='', u_id=''):
564 def __init__(self, gr_id='', u_id=''):
565 self.users_group_id = gr_id
565 self.users_group_id = gr_id
566 self.user_id = u_id
566 self.user_id = u_id
567
567
568
568
569 class Repository(Base, BaseModel):
569 class Repository(Base, BaseModel):
570 __tablename__ = 'repositories'
570 __tablename__ = 'repositories'
571 __table_args__ = (
571 __table_args__ = (
572 UniqueConstraint('repo_name'),
572 UniqueConstraint('repo_name'),
573 {'extend_existing': True, 'mysql_engine': 'InnoDB',
573 {'extend_existing': True, 'mysql_engine': 'InnoDB',
574 'mysql_charset': 'utf8'},
574 'mysql_charset': 'utf8'},
575 )
575 )
576
576
577 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
577 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
578 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
578 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
579 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
579 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
580 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
580 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
581 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
581 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
582 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
582 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
583 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
583 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
584 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
584 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
585 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
585 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
586 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
586 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
587 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
587 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
588
588
589 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
589 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
590 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
590 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
591
591
592 user = relationship('User')
592 user = relationship('User')
593 fork = relationship('Repository', remote_side=repo_id)
593 fork = relationship('Repository', remote_side=repo_id)
594 group = relationship('RepoGroup')
594 group = relationship('RepoGroup')
595 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
595 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
596 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
596 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
597 stats = relationship('Statistics', cascade='all', uselist=False)
597 stats = relationship('Statistics', cascade='all', uselist=False)
598
598
599 followers = relationship('UserFollowing',
599 followers = relationship('UserFollowing',
600 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
600 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
601 cascade='all')
601 cascade='all')
602
602
603 logs = relationship('UserLog')
603 logs = relationship('UserLog')
604 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
604 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
605
605
606 pull_requests_org = relationship('PullRequest',
606 pull_requests_org = relationship('PullRequest',
607 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
607 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
608 cascade="all, delete, delete-orphan")
608 cascade="all, delete, delete-orphan")
609
609
610 pull_requests_other = relationship('PullRequest',
610 pull_requests_other = relationship('PullRequest',
611 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
611 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
612 cascade="all, delete, delete-orphan")
612 cascade="all, delete, delete-orphan")
613
613
614 def __unicode__(self):
614 def __unicode__(self):
615 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
615 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
616 self.repo_name)
616 self.repo_name)
617
617
618 @classmethod
618 @classmethod
619 def url_sep(cls):
619 def url_sep(cls):
620 return URL_SEP
620 return URL_SEP
621
621
622 @classmethod
622 @classmethod
623 def get_by_repo_name(cls, repo_name):
623 def get_by_repo_name(cls, repo_name):
624 q = Session().query(cls).filter(cls.repo_name == repo_name)
624 q = Session().query(cls).filter(cls.repo_name == repo_name)
625 q = q.options(joinedload(Repository.fork))\
625 q = q.options(joinedload(Repository.fork))\
626 .options(joinedload(Repository.user))\
626 .options(joinedload(Repository.user))\
627 .options(joinedload(Repository.group))
627 .options(joinedload(Repository.group))
628 return q.scalar()
628 return q.scalar()
629
629
630 @classmethod
630 @classmethod
631 def get_by_full_path(cls, repo_full_path):
631 def get_by_full_path(cls, repo_full_path):
632 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
632 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
633 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
633 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
634
634
635 @classmethod
635 @classmethod
636 def get_repo_forks(cls, repo_id):
636 def get_repo_forks(cls, repo_id):
637 return cls.query().filter(Repository.fork_id == repo_id)
637 return cls.query().filter(Repository.fork_id == repo_id)
638
638
639 @classmethod
639 @classmethod
640 def base_path(cls):
640 def base_path(cls):
641 """
641 """
642 Returns base path when all repos are stored
642 Returns base path when all repos are stored
643
643
644 :param cls:
644 :param cls:
645 """
645 """
646 q = Session().query(RhodeCodeUi)\
646 q = Session().query(RhodeCodeUi)\
647 .filter(RhodeCodeUi.ui_key == cls.url_sep())
647 .filter(RhodeCodeUi.ui_key == cls.url_sep())
648 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
648 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
649 return q.one().ui_value
649 return q.one().ui_value
650
650
651 @property
651 @property
652 def forks(self):
652 def forks(self):
653 """
653 """
654 Return forks of this repo
654 Return forks of this repo
655 """
655 """
656 return Repository.get_repo_forks(self.repo_id)
656 return Repository.get_repo_forks(self.repo_id)
657
657
658 @property
658 @property
659 def parent(self):
659 def parent(self):
660 """
660 """
661 Returns fork parent
661 Returns fork parent
662 """
662 """
663 return self.fork
663 return self.fork
664
664
665 @property
665 @property
666 def just_name(self):
666 def just_name(self):
667 return self.repo_name.split(Repository.url_sep())[-1]
667 return self.repo_name.split(Repository.url_sep())[-1]
668
668
669 @property
669 @property
670 def groups_with_parents(self):
670 def groups_with_parents(self):
671 groups = []
671 groups = []
672 if self.group is None:
672 if self.group is None:
673 return groups
673 return groups
674
674
675 cur_gr = self.group
675 cur_gr = self.group
676 groups.insert(0, cur_gr)
676 groups.insert(0, cur_gr)
677 while 1:
677 while 1:
678 gr = getattr(cur_gr, 'parent_group', None)
678 gr = getattr(cur_gr, 'parent_group', None)
679 cur_gr = cur_gr.parent_group
679 cur_gr = cur_gr.parent_group
680 if gr is None:
680 if gr is None:
681 break
681 break
682 groups.insert(0, gr)
682 groups.insert(0, gr)
683
683
684 return groups
684 return groups
685
685
686 @property
686 @property
687 def groups_and_repo(self):
687 def groups_and_repo(self):
688 return self.groups_with_parents, self.just_name
688 return self.groups_with_parents, self.just_name
689
689
690 @LazyProperty
690 @LazyProperty
691 def repo_path(self):
691 def repo_path(self):
692 """
692 """
693 Returns base full path for that repository means where it actually
693 Returns base full path for that repository means where it actually
694 exists on a filesystem
694 exists on a filesystem
695 """
695 """
696 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
696 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
697 Repository.url_sep())
697 Repository.url_sep())
698 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
698 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
699 return q.one().ui_value
699 return q.one().ui_value
700
700
701 @property
701 @property
702 def repo_full_path(self):
702 def repo_full_path(self):
703 p = [self.repo_path]
703 p = [self.repo_path]
704 # we need to split the name by / since this is how we store the
704 # we need to split the name by / since this is how we store the
705 # names in the database, but that eventually needs to be converted
705 # names in the database, but that eventually needs to be converted
706 # into a valid system path
706 # into a valid system path
707 p += self.repo_name.split(Repository.url_sep())
707 p += self.repo_name.split(Repository.url_sep())
708 return os.path.join(*p)
708 return os.path.join(*p)
709
709
710 def get_new_name(self, repo_name):
710 def get_new_name(self, repo_name):
711 """
711 """
712 returns new full repository name based on assigned group and new new
712 returns new full repository name based on assigned group and new new
713
713
714 :param group_name:
714 :param group_name:
715 """
715 """
716 path_prefix = self.group.full_path_splitted if self.group else []
716 path_prefix = self.group.full_path_splitted if self.group else []
717 return Repository.url_sep().join(path_prefix + [repo_name])
717 return Repository.url_sep().join(path_prefix + [repo_name])
718
718
719 @property
719 @property
720 def _ui(self):
720 def _ui(self):
721 """
721 """
722 Creates an db based ui object for this repository
722 Creates an db based ui object for this repository
723 """
723 """
724 from mercurial import ui
724 from mercurial import ui
725 from mercurial import config
725 from mercurial import config
726 baseui = ui.ui()
726 baseui = ui.ui()
727
727
728 #clean the baseui object
728 #clean the baseui object
729 baseui._ocfg = config.config()
729 baseui._ocfg = config.config()
730 baseui._ucfg = config.config()
730 baseui._ucfg = config.config()
731 baseui._tcfg = config.config()
731 baseui._tcfg = config.config()
732
732
733 ret = RhodeCodeUi.query()\
733 ret = RhodeCodeUi.query()\
734 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
734 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
735
735
736 hg_ui = ret
736 hg_ui = ret
737 for ui_ in hg_ui:
737 for ui_ in hg_ui:
738 if ui_.ui_active and ui_.ui_key != 'push_ssl':
738 if ui_.ui_active:
739 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
739 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
740 ui_.ui_key, ui_.ui_value)
740 ui_.ui_key, ui_.ui_value)
741 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
741 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
742 if ui_.ui_key == 'push_ssl':
743 # force set push_ssl requirement to False, rhodecode
744 # handles that
745 baseui.setconfig(ui_.ui_section, ui_.ui_key, False)
742
746
743 return baseui
747 return baseui
744
748
745 @classmethod
749 @classmethod
746 def inject_ui(cls, repo, extras={}):
750 def inject_ui(cls, repo, extras={}):
747 from rhodecode.lib.vcs.backends.hg import MercurialRepository
751 from rhodecode.lib.vcs.backends.hg import MercurialRepository
748 from rhodecode.lib.vcs.backends.git import GitRepository
752 from rhodecode.lib.vcs.backends.git import GitRepository
749 required = (MercurialRepository, GitRepository)
753 required = (MercurialRepository, GitRepository)
750 if not isinstance(repo, required):
754 if not isinstance(repo, required):
751 raise Exception('repo must be instance of %s' % required)
755 raise Exception('repo must be instance of %s' % required)
752
756
753 # inject ui extra param to log this action via push logger
757 # inject ui extra param to log this action via push logger
754 for k, v in extras.items():
758 for k, v in extras.items():
755 repo._repo.ui.setconfig('rhodecode_extras', k, v)
759 repo._repo.ui.setconfig('rhodecode_extras', k, v)
756
760
757 @classmethod
761 @classmethod
758 def is_valid(cls, repo_name):
762 def is_valid(cls, repo_name):
759 """
763 """
760 returns True if given repo name is a valid filesystem repository
764 returns True if given repo name is a valid filesystem repository
761
765
762 :param cls:
766 :param cls:
763 :param repo_name:
767 :param repo_name:
764 """
768 """
765 from rhodecode.lib.utils import is_valid_repo
769 from rhodecode.lib.utils import is_valid_repo
766
770
767 return is_valid_repo(repo_name, cls.base_path())
771 return is_valid_repo(repo_name, cls.base_path())
768
772
769 def get_api_data(self):
773 def get_api_data(self):
770 """
774 """
771 Common function for generating repo api data
775 Common function for generating repo api data
772
776
773 """
777 """
774 repo = self
778 repo = self
775 data = dict(
779 data = dict(
776 repo_id=repo.repo_id,
780 repo_id=repo.repo_id,
777 repo_name=repo.repo_name,
781 repo_name=repo.repo_name,
778 repo_type=repo.repo_type,
782 repo_type=repo.repo_type,
779 clone_uri=repo.clone_uri,
783 clone_uri=repo.clone_uri,
780 private=repo.private,
784 private=repo.private,
781 created_on=repo.created_on,
785 created_on=repo.created_on,
782 description=repo.description,
786 description=repo.description,
783 landing_rev=repo.landing_rev,
787 landing_rev=repo.landing_rev,
784 owner=repo.user.username,
788 owner=repo.user.username,
785 fork_of=repo.fork.repo_name if repo.fork else None
789 fork_of=repo.fork.repo_name if repo.fork else None
786 )
790 )
787
791
788 return data
792 return data
789
793
790 #==========================================================================
794 #==========================================================================
791 # SCM PROPERTIES
795 # SCM PROPERTIES
792 #==========================================================================
796 #==========================================================================
793
797
794 def get_changeset(self, rev=None):
798 def get_changeset(self, rev=None):
795 return get_changeset_safe(self.scm_instance, rev)
799 return get_changeset_safe(self.scm_instance, rev)
796
800
797 def get_landing_changeset(self):
801 def get_landing_changeset(self):
798 """
802 """
799 Returns landing changeset, or if that doesn't exist returns the tip
803 Returns landing changeset, or if that doesn't exist returns the tip
800 """
804 """
801 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
805 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
802 return cs
806 return cs
803
807
804 @property
808 @property
805 def tip(self):
809 def tip(self):
806 return self.get_changeset('tip')
810 return self.get_changeset('tip')
807
811
808 @property
812 @property
809 def author(self):
813 def author(self):
810 return self.tip.author
814 return self.tip.author
811
815
812 @property
816 @property
813 def last_change(self):
817 def last_change(self):
814 return self.scm_instance.last_change
818 return self.scm_instance.last_change
815
819
816 def get_comments(self, revisions=None):
820 def get_comments(self, revisions=None):
817 """
821 """
818 Returns comments for this repository grouped by revisions
822 Returns comments for this repository grouped by revisions
819
823
820 :param revisions: filter query by revisions only
824 :param revisions: filter query by revisions only
821 """
825 """
822 cmts = ChangesetComment.query()\
826 cmts = ChangesetComment.query()\
823 .filter(ChangesetComment.repo == self)
827 .filter(ChangesetComment.repo == self)
824 if revisions:
828 if revisions:
825 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
829 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
826 grouped = defaultdict(list)
830 grouped = defaultdict(list)
827 for cmt in cmts.all():
831 for cmt in cmts.all():
828 grouped[cmt.revision].append(cmt)
832 grouped[cmt.revision].append(cmt)
829 return grouped
833 return grouped
830
834
831 def statuses(self, revisions=None):
835 def statuses(self, revisions=None):
832 """
836 """
833 Returns statuses for this repository
837 Returns statuses for this repository
834
838
835 :param revisions: list of revisions to get statuses for
839 :param revisions: list of revisions to get statuses for
836 :type revisions: list
840 :type revisions: list
837 """
841 """
838
842
839 statuses = ChangesetStatus.query()\
843 statuses = ChangesetStatus.query()\
840 .filter(ChangesetStatus.repo == self)\
844 .filter(ChangesetStatus.repo == self)\
841 .filter(ChangesetStatus.version == 0)
845 .filter(ChangesetStatus.version == 0)
842 if revisions:
846 if revisions:
843 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
847 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
844 grouped = {}
848 grouped = {}
845
849
846 #maybe we have open new pullrequest without a status ?
850 #maybe we have open new pullrequest without a status ?
847 stat = ChangesetStatus.STATUS_UNDER_REVIEW
851 stat = ChangesetStatus.STATUS_UNDER_REVIEW
848 status_lbl = ChangesetStatus.get_status_lbl(stat)
852 status_lbl = ChangesetStatus.get_status_lbl(stat)
849 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
853 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
850 for rev in pr.revisions:
854 for rev in pr.revisions:
851 pr_id = pr.pull_request_id
855 pr_id = pr.pull_request_id
852 pr_repo = pr.other_repo.repo_name
856 pr_repo = pr.other_repo.repo_name
853 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
857 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
854
858
855 for stat in statuses.all():
859 for stat in statuses.all():
856 pr_id = pr_repo = None
860 pr_id = pr_repo = None
857 if stat.pull_request:
861 if stat.pull_request:
858 pr_id = stat.pull_request.pull_request_id
862 pr_id = stat.pull_request.pull_request_id
859 pr_repo = stat.pull_request.other_repo.repo_name
863 pr_repo = stat.pull_request.other_repo.repo_name
860 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
864 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
861 pr_id, pr_repo]
865 pr_id, pr_repo]
862 return grouped
866 return grouped
863
867
864 #==========================================================================
868 #==========================================================================
865 # SCM CACHE INSTANCE
869 # SCM CACHE INSTANCE
866 #==========================================================================
870 #==========================================================================
867
871
868 @property
872 @property
869 def invalidate(self):
873 def invalidate(self):
870 return CacheInvalidation.invalidate(self.repo_name)
874 return CacheInvalidation.invalidate(self.repo_name)
871
875
872 def set_invalidate(self):
876 def set_invalidate(self):
873 """
877 """
874 set a cache for invalidation for this instance
878 set a cache for invalidation for this instance
875 """
879 """
876 CacheInvalidation.set_invalidate(self.repo_name)
880 CacheInvalidation.set_invalidate(self.repo_name)
877
881
878 @LazyProperty
882 @LazyProperty
879 def scm_instance(self):
883 def scm_instance(self):
880 return self.__get_instance()
884 return self.__get_instance()
881
885
882 def scm_instance_cached(self, cache_map=None):
886 def scm_instance_cached(self, cache_map=None):
883 @cache_region('long_term')
887 @cache_region('long_term')
884 def _c(repo_name):
888 def _c(repo_name):
885 return self.__get_instance()
889 return self.__get_instance()
886 rn = self.repo_name
890 rn = self.repo_name
887 log.debug('Getting cached instance of repo')
891 log.debug('Getting cached instance of repo')
888
892
889 if cache_map:
893 if cache_map:
890 # get using prefilled cache_map
894 # get using prefilled cache_map
891 invalidate_repo = cache_map[self.repo_name]
895 invalidate_repo = cache_map[self.repo_name]
892 if invalidate_repo:
896 if invalidate_repo:
893 invalidate_repo = (None if invalidate_repo.cache_active
897 invalidate_repo = (None if invalidate_repo.cache_active
894 else invalidate_repo)
898 else invalidate_repo)
895 else:
899 else:
896 # get from invalidate
900 # get from invalidate
897 invalidate_repo = self.invalidate
901 invalidate_repo = self.invalidate
898
902
899 if invalidate_repo is not None:
903 if invalidate_repo is not None:
900 region_invalidate(_c, None, rn)
904 region_invalidate(_c, None, rn)
901 # update our cache
905 # update our cache
902 CacheInvalidation.set_valid(invalidate_repo.cache_key)
906 CacheInvalidation.set_valid(invalidate_repo.cache_key)
903 return _c(rn)
907 return _c(rn)
904
908
905 def __get_instance(self):
909 def __get_instance(self):
906 repo_full_path = self.repo_full_path
910 repo_full_path = self.repo_full_path
907 try:
911 try:
908 alias = get_scm(repo_full_path)[0]
912 alias = get_scm(repo_full_path)[0]
909 log.debug('Creating instance of %s repository' % alias)
913 log.debug('Creating instance of %s repository' % alias)
910 backend = get_backend(alias)
914 backend = get_backend(alias)
911 except VCSError:
915 except VCSError:
912 log.error(traceback.format_exc())
916 log.error(traceback.format_exc())
913 log.error('Perhaps this repository is in db and not in '
917 log.error('Perhaps this repository is in db and not in '
914 'filesystem run rescan repositories with '
918 'filesystem run rescan repositories with '
915 '"destroy old data " option from admin panel')
919 '"destroy old data " option from admin panel')
916 return
920 return
917
921
918 if alias == 'hg':
922 if alias == 'hg':
919
923
920 repo = backend(safe_str(repo_full_path), create=False,
924 repo = backend(safe_str(repo_full_path), create=False,
921 baseui=self._ui)
925 baseui=self._ui)
922 # skip hidden web repository
926 # skip hidden web repository
923 if repo._get_hidden():
927 if repo._get_hidden():
924 return
928 return
925 else:
929 else:
926 repo = backend(repo_full_path, create=False)
930 repo = backend(repo_full_path, create=False)
927
931
928 return repo
932 return repo
929
933
930
934
931 class RepoGroup(Base, BaseModel):
935 class RepoGroup(Base, BaseModel):
932 __tablename__ = 'groups'
936 __tablename__ = 'groups'
933 __table_args__ = (
937 __table_args__ = (
934 UniqueConstraint('group_name', 'group_parent_id'),
938 UniqueConstraint('group_name', 'group_parent_id'),
935 CheckConstraint('group_id != group_parent_id'),
939 CheckConstraint('group_id != group_parent_id'),
936 {'extend_existing': True, 'mysql_engine': 'InnoDB',
940 {'extend_existing': True, 'mysql_engine': 'InnoDB',
937 'mysql_charset': 'utf8'},
941 'mysql_charset': 'utf8'},
938 )
942 )
939 __mapper_args__ = {'order_by': 'group_name'}
943 __mapper_args__ = {'order_by': 'group_name'}
940
944
941 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
945 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
942 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
946 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
943 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
947 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
944 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
948 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
945
949
946 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
950 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
947 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
951 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
948
952
949 parent_group = relationship('RepoGroup', remote_side=group_id)
953 parent_group = relationship('RepoGroup', remote_side=group_id)
950
954
951 def __init__(self, group_name='', parent_group=None):
955 def __init__(self, group_name='', parent_group=None):
952 self.group_name = group_name
956 self.group_name = group_name
953 self.parent_group = parent_group
957 self.parent_group = parent_group
954
958
955 def __unicode__(self):
959 def __unicode__(self):
956 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
960 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
957 self.group_name)
961 self.group_name)
958
962
959 @classmethod
963 @classmethod
960 def groups_choices(cls):
964 def groups_choices(cls):
961 from webhelpers.html import literal as _literal
965 from webhelpers.html import literal as _literal
962 repo_groups = [('', '')]
966 repo_groups = [('', '')]
963 sep = ' &raquo; '
967 sep = ' &raquo; '
964 _name = lambda k: _literal(sep.join(k))
968 _name = lambda k: _literal(sep.join(k))
965
969
966 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
970 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
967 for x in cls.query().all()])
971 for x in cls.query().all()])
968
972
969 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
973 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
970 return repo_groups
974 return repo_groups
971
975
972 @classmethod
976 @classmethod
973 def url_sep(cls):
977 def url_sep(cls):
974 return URL_SEP
978 return URL_SEP
975
979
976 @classmethod
980 @classmethod
977 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
981 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
978 if case_insensitive:
982 if case_insensitive:
979 gr = cls.query()\
983 gr = cls.query()\
980 .filter(cls.group_name.ilike(group_name))
984 .filter(cls.group_name.ilike(group_name))
981 else:
985 else:
982 gr = cls.query()\
986 gr = cls.query()\
983 .filter(cls.group_name == group_name)
987 .filter(cls.group_name == group_name)
984 if cache:
988 if cache:
985 gr = gr.options(FromCache(
989 gr = gr.options(FromCache(
986 "sql_cache_short",
990 "sql_cache_short",
987 "get_group_%s" % _hash_key(group_name)
991 "get_group_%s" % _hash_key(group_name)
988 )
992 )
989 )
993 )
990 return gr.scalar()
994 return gr.scalar()
991
995
992 @property
996 @property
993 def parents(self):
997 def parents(self):
994 parents_recursion_limit = 5
998 parents_recursion_limit = 5
995 groups = []
999 groups = []
996 if self.parent_group is None:
1000 if self.parent_group is None:
997 return groups
1001 return groups
998 cur_gr = self.parent_group
1002 cur_gr = self.parent_group
999 groups.insert(0, cur_gr)
1003 groups.insert(0, cur_gr)
1000 cnt = 0
1004 cnt = 0
1001 while 1:
1005 while 1:
1002 cnt += 1
1006 cnt += 1
1003 gr = getattr(cur_gr, 'parent_group', None)
1007 gr = getattr(cur_gr, 'parent_group', None)
1004 cur_gr = cur_gr.parent_group
1008 cur_gr = cur_gr.parent_group
1005 if gr is None:
1009 if gr is None:
1006 break
1010 break
1007 if cnt == parents_recursion_limit:
1011 if cnt == parents_recursion_limit:
1008 # this will prevent accidental infinit loops
1012 # this will prevent accidental infinit loops
1009 log.error('group nested more than %s' %
1013 log.error('group nested more than %s' %
1010 parents_recursion_limit)
1014 parents_recursion_limit)
1011 break
1015 break
1012
1016
1013 groups.insert(0, gr)
1017 groups.insert(0, gr)
1014 return groups
1018 return groups
1015
1019
1016 @property
1020 @property
1017 def children(self):
1021 def children(self):
1018 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1022 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1019
1023
1020 @property
1024 @property
1021 def name(self):
1025 def name(self):
1022 return self.group_name.split(RepoGroup.url_sep())[-1]
1026 return self.group_name.split(RepoGroup.url_sep())[-1]
1023
1027
1024 @property
1028 @property
1025 def full_path(self):
1029 def full_path(self):
1026 return self.group_name
1030 return self.group_name
1027
1031
1028 @property
1032 @property
1029 def full_path_splitted(self):
1033 def full_path_splitted(self):
1030 return self.group_name.split(RepoGroup.url_sep())
1034 return self.group_name.split(RepoGroup.url_sep())
1031
1035
1032 @property
1036 @property
1033 def repositories(self):
1037 def repositories(self):
1034 return Repository.query()\
1038 return Repository.query()\
1035 .filter(Repository.group == self)\
1039 .filter(Repository.group == self)\
1036 .order_by(Repository.repo_name)
1040 .order_by(Repository.repo_name)
1037
1041
1038 @property
1042 @property
1039 def repositories_recursive_count(self):
1043 def repositories_recursive_count(self):
1040 cnt = self.repositories.count()
1044 cnt = self.repositories.count()
1041
1045
1042 def children_count(group):
1046 def children_count(group):
1043 cnt = 0
1047 cnt = 0
1044 for child in group.children:
1048 for child in group.children:
1045 cnt += child.repositories.count()
1049 cnt += child.repositories.count()
1046 cnt += children_count(child)
1050 cnt += children_count(child)
1047 return cnt
1051 return cnt
1048
1052
1049 return cnt + children_count(self)
1053 return cnt + children_count(self)
1050
1054
1051 def get_new_name(self, group_name):
1055 def get_new_name(self, group_name):
1052 """
1056 """
1053 returns new full group name based on parent and new name
1057 returns new full group name based on parent and new name
1054
1058
1055 :param group_name:
1059 :param group_name:
1056 """
1060 """
1057 path_prefix = (self.parent_group.full_path_splitted if
1061 path_prefix = (self.parent_group.full_path_splitted if
1058 self.parent_group else [])
1062 self.parent_group else [])
1059 return RepoGroup.url_sep().join(path_prefix + [group_name])
1063 return RepoGroup.url_sep().join(path_prefix + [group_name])
1060
1064
1061
1065
1062 class Permission(Base, BaseModel):
1066 class Permission(Base, BaseModel):
1063 __tablename__ = 'permissions'
1067 __tablename__ = 'permissions'
1064 __table_args__ = (
1068 __table_args__ = (
1065 Index('p_perm_name_idx', 'permission_name'),
1069 Index('p_perm_name_idx', 'permission_name'),
1066 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1070 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1067 'mysql_charset': 'utf8'},
1071 'mysql_charset': 'utf8'},
1068 )
1072 )
1069 PERMS = [
1073 PERMS = [
1070 ('repository.none', _('Repository no access')),
1074 ('repository.none', _('Repository no access')),
1071 ('repository.read', _('Repository read access')),
1075 ('repository.read', _('Repository read access')),
1072 ('repository.write', _('Repository write access')),
1076 ('repository.write', _('Repository write access')),
1073 ('repository.admin', _('Repository admin access')),
1077 ('repository.admin', _('Repository admin access')),
1074
1078
1075 ('group.none', _('Repositories Group no access')),
1079 ('group.none', _('Repositories Group no access')),
1076 ('group.read', _('Repositories Group read access')),
1080 ('group.read', _('Repositories Group read access')),
1077 ('group.write', _('Repositories Group write access')),
1081 ('group.write', _('Repositories Group write access')),
1078 ('group.admin', _('Repositories Group admin access')),
1082 ('group.admin', _('Repositories Group admin access')),
1079
1083
1080 ('hg.admin', _('RhodeCode Administrator')),
1084 ('hg.admin', _('RhodeCode Administrator')),
1081 ('hg.create.none', _('Repository creation disabled')),
1085 ('hg.create.none', _('Repository creation disabled')),
1082 ('hg.create.repository', _('Repository creation enabled')),
1086 ('hg.create.repository', _('Repository creation enabled')),
1083 ('hg.register.none', _('Register disabled')),
1087 ('hg.register.none', _('Register disabled')),
1084 ('hg.register.manual_activate', _('Register new user with RhodeCode '
1088 ('hg.register.manual_activate', _('Register new user with RhodeCode '
1085 'with manual activation')),
1089 'with manual activation')),
1086
1090
1087 ('hg.register.auto_activate', _('Register new user with RhodeCode '
1091 ('hg.register.auto_activate', _('Register new user with RhodeCode '
1088 'with auto activation')),
1092 'with auto activation')),
1089 ]
1093 ]
1090
1094
1091 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1095 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1092 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1096 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1093 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1097 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1094
1098
1095 def __unicode__(self):
1099 def __unicode__(self):
1096 return u"<%s('%s:%s')>" % (
1100 return u"<%s('%s:%s')>" % (
1097 self.__class__.__name__, self.permission_id, self.permission_name
1101 self.__class__.__name__, self.permission_id, self.permission_name
1098 )
1102 )
1099
1103
1100 @classmethod
1104 @classmethod
1101 def get_by_key(cls, key):
1105 def get_by_key(cls, key):
1102 return cls.query().filter(cls.permission_name == key).scalar()
1106 return cls.query().filter(cls.permission_name == key).scalar()
1103
1107
1104 @classmethod
1108 @classmethod
1105 def get_default_perms(cls, default_user_id):
1109 def get_default_perms(cls, default_user_id):
1106 q = Session().query(UserRepoToPerm, Repository, cls)\
1110 q = Session().query(UserRepoToPerm, Repository, cls)\
1107 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1111 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1108 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1112 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1109 .filter(UserRepoToPerm.user_id == default_user_id)
1113 .filter(UserRepoToPerm.user_id == default_user_id)
1110
1114
1111 return q.all()
1115 return q.all()
1112
1116
1113 @classmethod
1117 @classmethod
1114 def get_default_group_perms(cls, default_user_id):
1118 def get_default_group_perms(cls, default_user_id):
1115 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1119 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1116 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1120 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1117 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1121 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1118 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1122 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1119
1123
1120 return q.all()
1124 return q.all()
1121
1125
1122
1126
1123 class UserRepoToPerm(Base, BaseModel):
1127 class UserRepoToPerm(Base, BaseModel):
1124 __tablename__ = 'repo_to_perm'
1128 __tablename__ = 'repo_to_perm'
1125 __table_args__ = (
1129 __table_args__ = (
1126 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1130 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1127 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1131 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1128 'mysql_charset': 'utf8'}
1132 'mysql_charset': 'utf8'}
1129 )
1133 )
1130 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1134 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1131 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1135 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1132 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1136 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1133 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1137 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1134
1138
1135 user = relationship('User')
1139 user = relationship('User')
1136 repository = relationship('Repository')
1140 repository = relationship('Repository')
1137 permission = relationship('Permission')
1141 permission = relationship('Permission')
1138
1142
1139 @classmethod
1143 @classmethod
1140 def create(cls, user, repository, permission):
1144 def create(cls, user, repository, permission):
1141 n = cls()
1145 n = cls()
1142 n.user = user
1146 n.user = user
1143 n.repository = repository
1147 n.repository = repository
1144 n.permission = permission
1148 n.permission = permission
1145 Session().add(n)
1149 Session().add(n)
1146 return n
1150 return n
1147
1151
1148 def __unicode__(self):
1152 def __unicode__(self):
1149 return u'<user:%s => %s >' % (self.user, self.repository)
1153 return u'<user:%s => %s >' % (self.user, self.repository)
1150
1154
1151
1155
1152 class UserToPerm(Base, BaseModel):
1156 class UserToPerm(Base, BaseModel):
1153 __tablename__ = 'user_to_perm'
1157 __tablename__ = 'user_to_perm'
1154 __table_args__ = (
1158 __table_args__ = (
1155 UniqueConstraint('user_id', 'permission_id'),
1159 UniqueConstraint('user_id', 'permission_id'),
1156 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1160 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1157 'mysql_charset': 'utf8'}
1161 'mysql_charset': 'utf8'}
1158 )
1162 )
1159 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1163 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1160 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1164 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1161 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1165 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1162
1166
1163 user = relationship('User')
1167 user = relationship('User')
1164 permission = relationship('Permission', lazy='joined')
1168 permission = relationship('Permission', lazy='joined')
1165
1169
1166
1170
1167 class UsersGroupRepoToPerm(Base, BaseModel):
1171 class UsersGroupRepoToPerm(Base, BaseModel):
1168 __tablename__ = 'users_group_repo_to_perm'
1172 __tablename__ = 'users_group_repo_to_perm'
1169 __table_args__ = (
1173 __table_args__ = (
1170 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1174 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1171 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1175 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1172 'mysql_charset': 'utf8'}
1176 'mysql_charset': 'utf8'}
1173 )
1177 )
1174 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1178 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1175 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1179 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1176 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1180 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1177 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1181 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1178
1182
1179 users_group = relationship('UsersGroup')
1183 users_group = relationship('UsersGroup')
1180 permission = relationship('Permission')
1184 permission = relationship('Permission')
1181 repository = relationship('Repository')
1185 repository = relationship('Repository')
1182
1186
1183 @classmethod
1187 @classmethod
1184 def create(cls, users_group, repository, permission):
1188 def create(cls, users_group, repository, permission):
1185 n = cls()
1189 n = cls()
1186 n.users_group = users_group
1190 n.users_group = users_group
1187 n.repository = repository
1191 n.repository = repository
1188 n.permission = permission
1192 n.permission = permission
1189 Session().add(n)
1193 Session().add(n)
1190 return n
1194 return n
1191
1195
1192 def __unicode__(self):
1196 def __unicode__(self):
1193 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1197 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1194
1198
1195
1199
1196 class UsersGroupToPerm(Base, BaseModel):
1200 class UsersGroupToPerm(Base, BaseModel):
1197 __tablename__ = 'users_group_to_perm'
1201 __tablename__ = 'users_group_to_perm'
1198 __table_args__ = (
1202 __table_args__ = (
1199 UniqueConstraint('users_group_id', 'permission_id',),
1203 UniqueConstraint('users_group_id', 'permission_id',),
1200 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1204 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1201 'mysql_charset': 'utf8'}
1205 'mysql_charset': 'utf8'}
1202 )
1206 )
1203 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1207 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1204 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1208 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1205 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1209 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1206
1210
1207 users_group = relationship('UsersGroup')
1211 users_group = relationship('UsersGroup')
1208 permission = relationship('Permission')
1212 permission = relationship('Permission')
1209
1213
1210
1214
1211 class UserRepoGroupToPerm(Base, BaseModel):
1215 class UserRepoGroupToPerm(Base, BaseModel):
1212 __tablename__ = 'user_repo_group_to_perm'
1216 __tablename__ = 'user_repo_group_to_perm'
1213 __table_args__ = (
1217 __table_args__ = (
1214 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1218 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1215 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1219 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1216 'mysql_charset': 'utf8'}
1220 'mysql_charset': 'utf8'}
1217 )
1221 )
1218
1222
1219 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1223 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1220 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1224 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1221 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1225 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1222 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1226 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1223
1227
1224 user = relationship('User')
1228 user = relationship('User')
1225 group = relationship('RepoGroup')
1229 group = relationship('RepoGroup')
1226 permission = relationship('Permission')
1230 permission = relationship('Permission')
1227
1231
1228
1232
1229 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1233 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1230 __tablename__ = 'users_group_repo_group_to_perm'
1234 __tablename__ = 'users_group_repo_group_to_perm'
1231 __table_args__ = (
1235 __table_args__ = (
1232 UniqueConstraint('users_group_id', 'group_id'),
1236 UniqueConstraint('users_group_id', 'group_id'),
1233 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1237 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1234 'mysql_charset': 'utf8'}
1238 'mysql_charset': 'utf8'}
1235 )
1239 )
1236
1240
1237 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)
1241 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)
1238 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1242 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1239 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1243 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1240 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1244 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1241
1245
1242 users_group = relationship('UsersGroup')
1246 users_group = relationship('UsersGroup')
1243 permission = relationship('Permission')
1247 permission = relationship('Permission')
1244 group = relationship('RepoGroup')
1248 group = relationship('RepoGroup')
1245
1249
1246
1250
1247 class Statistics(Base, BaseModel):
1251 class Statistics(Base, BaseModel):
1248 __tablename__ = 'statistics'
1252 __tablename__ = 'statistics'
1249 __table_args__ = (
1253 __table_args__ = (
1250 UniqueConstraint('repository_id'),
1254 UniqueConstraint('repository_id'),
1251 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1255 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1252 'mysql_charset': 'utf8'}
1256 'mysql_charset': 'utf8'}
1253 )
1257 )
1254 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1258 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1255 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1259 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1256 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1260 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1257 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1261 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1258 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1262 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1259 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1263 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1260
1264
1261 repository = relationship('Repository', single_parent=True)
1265 repository = relationship('Repository', single_parent=True)
1262
1266
1263
1267
1264 class UserFollowing(Base, BaseModel):
1268 class UserFollowing(Base, BaseModel):
1265 __tablename__ = 'user_followings'
1269 __tablename__ = 'user_followings'
1266 __table_args__ = (
1270 __table_args__ = (
1267 UniqueConstraint('user_id', 'follows_repository_id'),
1271 UniqueConstraint('user_id', 'follows_repository_id'),
1268 UniqueConstraint('user_id', 'follows_user_id'),
1272 UniqueConstraint('user_id', 'follows_user_id'),
1269 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1273 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1270 'mysql_charset': 'utf8'}
1274 'mysql_charset': 'utf8'}
1271 )
1275 )
1272
1276
1273 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1277 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1274 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1278 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1275 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1279 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1276 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1280 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1277 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1281 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1278
1282
1279 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1283 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1280
1284
1281 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1285 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1282 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1286 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1283
1287
1284 @classmethod
1288 @classmethod
1285 def get_repo_followers(cls, repo_id):
1289 def get_repo_followers(cls, repo_id):
1286 return cls.query().filter(cls.follows_repo_id == repo_id)
1290 return cls.query().filter(cls.follows_repo_id == repo_id)
1287
1291
1288
1292
1289 class CacheInvalidation(Base, BaseModel):
1293 class CacheInvalidation(Base, BaseModel):
1290 __tablename__ = 'cache_invalidation'
1294 __tablename__ = 'cache_invalidation'
1291 __table_args__ = (
1295 __table_args__ = (
1292 UniqueConstraint('cache_key'),
1296 UniqueConstraint('cache_key'),
1293 Index('key_idx', 'cache_key'),
1297 Index('key_idx', 'cache_key'),
1294 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1298 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1295 'mysql_charset': 'utf8'},
1299 'mysql_charset': 'utf8'},
1296 )
1300 )
1297 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1301 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1298 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1302 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1299 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1303 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1300 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1304 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1301
1305
1302 def __init__(self, cache_key, cache_args=''):
1306 def __init__(self, cache_key, cache_args=''):
1303 self.cache_key = cache_key
1307 self.cache_key = cache_key
1304 self.cache_args = cache_args
1308 self.cache_args = cache_args
1305 self.cache_active = False
1309 self.cache_active = False
1306
1310
1307 def __unicode__(self):
1311 def __unicode__(self):
1308 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1312 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1309 self.cache_id, self.cache_key)
1313 self.cache_id, self.cache_key)
1310
1314
1311 @classmethod
1315 @classmethod
1312 def clear_cache(cls):
1316 def clear_cache(cls):
1313 cls.query().delete()
1317 cls.query().delete()
1314
1318
1315 @classmethod
1319 @classmethod
1316 def _get_key(cls, key):
1320 def _get_key(cls, key):
1317 """
1321 """
1318 Wrapper for generating a key, together with a prefix
1322 Wrapper for generating a key, together with a prefix
1319
1323
1320 :param key:
1324 :param key:
1321 """
1325 """
1322 import rhodecode
1326 import rhodecode
1323 prefix = ''
1327 prefix = ''
1324 iid = rhodecode.CONFIG.get('instance_id')
1328 iid = rhodecode.CONFIG.get('instance_id')
1325 if iid:
1329 if iid:
1326 prefix = iid
1330 prefix = iid
1327 return "%s%s" % (prefix, key), prefix, key.rstrip('_README')
1331 return "%s%s" % (prefix, key), prefix, key.rstrip('_README')
1328
1332
1329 @classmethod
1333 @classmethod
1330 def get_by_key(cls, key):
1334 def get_by_key(cls, key):
1331 return cls.query().filter(cls.cache_key == key).scalar()
1335 return cls.query().filter(cls.cache_key == key).scalar()
1332
1336
1333 @classmethod
1337 @classmethod
1334 def _get_or_create_key(cls, key, prefix, org_key):
1338 def _get_or_create_key(cls, key, prefix, org_key):
1335 inv_obj = Session().query(cls).filter(cls.cache_key == key).scalar()
1339 inv_obj = Session().query(cls).filter(cls.cache_key == key).scalar()
1336 if not inv_obj:
1340 if not inv_obj:
1337 try:
1341 try:
1338 inv_obj = CacheInvalidation(key, org_key)
1342 inv_obj = CacheInvalidation(key, org_key)
1339 Session().add(inv_obj)
1343 Session().add(inv_obj)
1340 Session().commit()
1344 Session().commit()
1341 except Exception:
1345 except Exception:
1342 log.error(traceback.format_exc())
1346 log.error(traceback.format_exc())
1343 Session().rollback()
1347 Session().rollback()
1344 return inv_obj
1348 return inv_obj
1345
1349
1346 @classmethod
1350 @classmethod
1347 def invalidate(cls, key):
1351 def invalidate(cls, key):
1348 """
1352 """
1349 Returns Invalidation object if this given key should be invalidated
1353 Returns Invalidation object if this given key should be invalidated
1350 None otherwise. `cache_active = False` means that this cache
1354 None otherwise. `cache_active = False` means that this cache
1351 state is not valid and needs to be invalidated
1355 state is not valid and needs to be invalidated
1352
1356
1353 :param key:
1357 :param key:
1354 """
1358 """
1355
1359
1356 key, _prefix, _org_key = cls._get_key(key)
1360 key, _prefix, _org_key = cls._get_key(key)
1357 inv = cls._get_or_create_key(key, _prefix, _org_key)
1361 inv = cls._get_or_create_key(key, _prefix, _org_key)
1358
1362
1359 if inv and inv.cache_active is False:
1363 if inv and inv.cache_active is False:
1360 return inv
1364 return inv
1361
1365
1362 @classmethod
1366 @classmethod
1363 def set_invalidate(cls, key):
1367 def set_invalidate(cls, key):
1364 """
1368 """
1365 Mark this Cache key for invalidation
1369 Mark this Cache key for invalidation
1366
1370
1367 :param key:
1371 :param key:
1368 """
1372 """
1369
1373
1370 key, _prefix, _org_key = cls._get_key(key)
1374 key, _prefix, _org_key = cls._get_key(key)
1371 inv_objs = Session().query(cls).filter(cls.cache_args == _org_key).all()
1375 inv_objs = Session().query(cls).filter(cls.cache_args == _org_key).all()
1372 log.debug('marking %s key[s] %s for invalidation' % (len(inv_objs),
1376 log.debug('marking %s key[s] %s for invalidation' % (len(inv_objs),
1373 _org_key))
1377 _org_key))
1374 try:
1378 try:
1375 for inv_obj in inv_objs:
1379 for inv_obj in inv_objs:
1376 if inv_obj:
1380 if inv_obj:
1377 inv_obj.cache_active = False
1381 inv_obj.cache_active = False
1378
1382
1379 Session().add(inv_obj)
1383 Session().add(inv_obj)
1380 Session().commit()
1384 Session().commit()
1381 except Exception:
1385 except Exception:
1382 log.error(traceback.format_exc())
1386 log.error(traceback.format_exc())
1383 Session().rollback()
1387 Session().rollback()
1384
1388
1385 @classmethod
1389 @classmethod
1386 def set_valid(cls, key):
1390 def set_valid(cls, key):
1387 """
1391 """
1388 Mark this cache key as active and currently cached
1392 Mark this cache key as active and currently cached
1389
1393
1390 :param key:
1394 :param key:
1391 """
1395 """
1392 inv_obj = cls.get_by_key(key)
1396 inv_obj = cls.get_by_key(key)
1393 inv_obj.cache_active = True
1397 inv_obj.cache_active = True
1394 Session().add(inv_obj)
1398 Session().add(inv_obj)
1395 Session().commit()
1399 Session().commit()
1396
1400
1397 @classmethod
1401 @classmethod
1398 def get_cache_map(cls):
1402 def get_cache_map(cls):
1399
1403
1400 class cachemapdict(dict):
1404 class cachemapdict(dict):
1401
1405
1402 def __init__(self, *args, **kwargs):
1406 def __init__(self, *args, **kwargs):
1403 fixkey = kwargs.get('fixkey')
1407 fixkey = kwargs.get('fixkey')
1404 if fixkey:
1408 if fixkey:
1405 del kwargs['fixkey']
1409 del kwargs['fixkey']
1406 self.fixkey = fixkey
1410 self.fixkey = fixkey
1407 super(cachemapdict, self).__init__(*args, **kwargs)
1411 super(cachemapdict, self).__init__(*args, **kwargs)
1408
1412
1409 def __getattr__(self, name):
1413 def __getattr__(self, name):
1410 key = name
1414 key = name
1411 if self.fixkey:
1415 if self.fixkey:
1412 key, _prefix, _org_key = cls._get_key(key)
1416 key, _prefix, _org_key = cls._get_key(key)
1413 if key in self.__dict__:
1417 if key in self.__dict__:
1414 return self.__dict__[key]
1418 return self.__dict__[key]
1415 else:
1419 else:
1416 return self[key]
1420 return self[key]
1417
1421
1418 def __getitem__(self, key):
1422 def __getitem__(self, key):
1419 if self.fixkey:
1423 if self.fixkey:
1420 key, _prefix, _org_key = cls._get_key(key)
1424 key, _prefix, _org_key = cls._get_key(key)
1421 try:
1425 try:
1422 return super(cachemapdict, self).__getitem__(key)
1426 return super(cachemapdict, self).__getitem__(key)
1423 except KeyError:
1427 except KeyError:
1424 return
1428 return
1425
1429
1426 cache_map = cachemapdict(fixkey=True)
1430 cache_map = cachemapdict(fixkey=True)
1427 for obj in cls.query().all():
1431 for obj in cls.query().all():
1428 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1432 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1429 return cache_map
1433 return cache_map
1430
1434
1431
1435
1432 class ChangesetComment(Base, BaseModel):
1436 class ChangesetComment(Base, BaseModel):
1433 __tablename__ = 'changeset_comments'
1437 __tablename__ = 'changeset_comments'
1434 __table_args__ = (
1438 __table_args__ = (
1435 Index('cc_revision_idx', 'revision'),
1439 Index('cc_revision_idx', 'revision'),
1436 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1440 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1437 'mysql_charset': 'utf8'},
1441 'mysql_charset': 'utf8'},
1438 )
1442 )
1439 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1443 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1440 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1444 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1441 revision = Column('revision', String(40), nullable=True)
1445 revision = Column('revision', String(40), nullable=True)
1442 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1446 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1443 line_no = Column('line_no', Unicode(10), nullable=True)
1447 line_no = Column('line_no', Unicode(10), nullable=True)
1444 f_path = Column('f_path', Unicode(1000), nullable=True)
1448 f_path = Column('f_path', Unicode(1000), nullable=True)
1445 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1449 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1446 text = Column('text', Unicode(25000), nullable=False)
1450 text = Column('text', Unicode(25000), nullable=False)
1447 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1451 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1448 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1452 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1449
1453
1450 author = relationship('User', lazy='joined')
1454 author = relationship('User', lazy='joined')
1451 repo = relationship('Repository')
1455 repo = relationship('Repository')
1452 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1456 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1453 pull_request = relationship('PullRequest', lazy='joined')
1457 pull_request = relationship('PullRequest', lazy='joined')
1454
1458
1455 @classmethod
1459 @classmethod
1456 def get_users(cls, revision=None, pull_request_id=None):
1460 def get_users(cls, revision=None, pull_request_id=None):
1457 """
1461 """
1458 Returns user associated with this ChangesetComment. ie those
1462 Returns user associated with this ChangesetComment. ie those
1459 who actually commented
1463 who actually commented
1460
1464
1461 :param cls:
1465 :param cls:
1462 :param revision:
1466 :param revision:
1463 """
1467 """
1464 q = Session().query(User)\
1468 q = Session().query(User)\
1465 .join(ChangesetComment.author)
1469 .join(ChangesetComment.author)
1466 if revision:
1470 if revision:
1467 q = q.filter(cls.revision == revision)
1471 q = q.filter(cls.revision == revision)
1468 elif pull_request_id:
1472 elif pull_request_id:
1469 q = q.filter(cls.pull_request_id == pull_request_id)
1473 q = q.filter(cls.pull_request_id == pull_request_id)
1470 return q.all()
1474 return q.all()
1471
1475
1472
1476
1473 class ChangesetStatus(Base, BaseModel):
1477 class ChangesetStatus(Base, BaseModel):
1474 __tablename__ = 'changeset_statuses'
1478 __tablename__ = 'changeset_statuses'
1475 __table_args__ = (
1479 __table_args__ = (
1476 Index('cs_revision_idx', 'revision'),
1480 Index('cs_revision_idx', 'revision'),
1477 Index('cs_version_idx', 'version'),
1481 Index('cs_version_idx', 'version'),
1478 UniqueConstraint('repo_id', 'revision', 'version'),
1482 UniqueConstraint('repo_id', 'revision', 'version'),
1479 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1483 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1480 'mysql_charset': 'utf8'}
1484 'mysql_charset': 'utf8'}
1481 )
1485 )
1482 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1486 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1483 STATUS_APPROVED = 'approved'
1487 STATUS_APPROVED = 'approved'
1484 STATUS_REJECTED = 'rejected'
1488 STATUS_REJECTED = 'rejected'
1485 STATUS_UNDER_REVIEW = 'under_review'
1489 STATUS_UNDER_REVIEW = 'under_review'
1486
1490
1487 STATUSES = [
1491 STATUSES = [
1488 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1492 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1489 (STATUS_APPROVED, _("Approved")),
1493 (STATUS_APPROVED, _("Approved")),
1490 (STATUS_REJECTED, _("Rejected")),
1494 (STATUS_REJECTED, _("Rejected")),
1491 (STATUS_UNDER_REVIEW, _("Under Review")),
1495 (STATUS_UNDER_REVIEW, _("Under Review")),
1492 ]
1496 ]
1493
1497
1494 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1498 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1495 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1499 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1496 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1500 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1497 revision = Column('revision', String(40), nullable=False)
1501 revision = Column('revision', String(40), nullable=False)
1498 status = Column('status', String(128), nullable=False, default=DEFAULT)
1502 status = Column('status', String(128), nullable=False, default=DEFAULT)
1499 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1503 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1500 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1504 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1501 version = Column('version', Integer(), nullable=False, default=0)
1505 version = Column('version', Integer(), nullable=False, default=0)
1502 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1506 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1503
1507
1504 author = relationship('User', lazy='joined')
1508 author = relationship('User', lazy='joined')
1505 repo = relationship('Repository')
1509 repo = relationship('Repository')
1506 comment = relationship('ChangesetComment', lazy='joined')
1510 comment = relationship('ChangesetComment', lazy='joined')
1507 pull_request = relationship('PullRequest', lazy='joined')
1511 pull_request = relationship('PullRequest', lazy='joined')
1508
1512
1509 def __unicode__(self):
1513 def __unicode__(self):
1510 return u"<%s('%s:%s')>" % (
1514 return u"<%s('%s:%s')>" % (
1511 self.__class__.__name__,
1515 self.__class__.__name__,
1512 self.status, self.author
1516 self.status, self.author
1513 )
1517 )
1514
1518
1515 @classmethod
1519 @classmethod
1516 def get_status_lbl(cls, value):
1520 def get_status_lbl(cls, value):
1517 return dict(cls.STATUSES).get(value)
1521 return dict(cls.STATUSES).get(value)
1518
1522
1519 @property
1523 @property
1520 def status_lbl(self):
1524 def status_lbl(self):
1521 return ChangesetStatus.get_status_lbl(self.status)
1525 return ChangesetStatus.get_status_lbl(self.status)
1522
1526
1523
1527
1524 class PullRequest(Base, BaseModel):
1528 class PullRequest(Base, BaseModel):
1525 __tablename__ = 'pull_requests'
1529 __tablename__ = 'pull_requests'
1526 __table_args__ = (
1530 __table_args__ = (
1527 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1531 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1528 'mysql_charset': 'utf8'},
1532 'mysql_charset': 'utf8'},
1529 )
1533 )
1530
1534
1531 STATUS_NEW = u'new'
1535 STATUS_NEW = u'new'
1532 STATUS_OPEN = u'open'
1536 STATUS_OPEN = u'open'
1533 STATUS_CLOSED = u'closed'
1537 STATUS_CLOSED = u'closed'
1534
1538
1535 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1539 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1536 title = Column('title', Unicode(256), nullable=True)
1540 title = Column('title', Unicode(256), nullable=True)
1537 description = Column('description', UnicodeText(10240), nullable=True)
1541 description = Column('description', UnicodeText(10240), nullable=True)
1538 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1542 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1539 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1543 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1540 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1544 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1541 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1545 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1542 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1546 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1543 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1547 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1544 org_ref = Column('org_ref', Unicode(256), nullable=False)
1548 org_ref = Column('org_ref', Unicode(256), nullable=False)
1545 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1549 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1546 other_ref = Column('other_ref', Unicode(256), nullable=False)
1550 other_ref = Column('other_ref', Unicode(256), nullable=False)
1547
1551
1548 @hybrid_property
1552 @hybrid_property
1549 def revisions(self):
1553 def revisions(self):
1550 return self._revisions.split(':')
1554 return self._revisions.split(':')
1551
1555
1552 @revisions.setter
1556 @revisions.setter
1553 def revisions(self, val):
1557 def revisions(self, val):
1554 self._revisions = ':'.join(val)
1558 self._revisions = ':'.join(val)
1555
1559
1556 author = relationship('User', lazy='joined')
1560 author = relationship('User', lazy='joined')
1557 reviewers = relationship('PullRequestReviewers',
1561 reviewers = relationship('PullRequestReviewers',
1558 cascade="all, delete, delete-orphan")
1562 cascade="all, delete, delete-orphan")
1559 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1563 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1560 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1564 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1561 statuses = relationship('ChangesetStatus')
1565 statuses = relationship('ChangesetStatus')
1562 comments = relationship('ChangesetComment',
1566 comments = relationship('ChangesetComment',
1563 cascade="all, delete, delete-orphan")
1567 cascade="all, delete, delete-orphan")
1564
1568
1565 def is_closed(self):
1569 def is_closed(self):
1566 return self.status == self.STATUS_CLOSED
1570 return self.status == self.STATUS_CLOSED
1567
1571
1568 def __json__(self):
1572 def __json__(self):
1569 return dict(
1573 return dict(
1570 revisions=self.revisions
1574 revisions=self.revisions
1571 )
1575 )
1572
1576
1573
1577
1574 class PullRequestReviewers(Base, BaseModel):
1578 class PullRequestReviewers(Base, BaseModel):
1575 __tablename__ = 'pull_request_reviewers'
1579 __tablename__ = 'pull_request_reviewers'
1576 __table_args__ = (
1580 __table_args__ = (
1577 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1581 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1578 'mysql_charset': 'utf8'},
1582 'mysql_charset': 'utf8'},
1579 )
1583 )
1580
1584
1581 def __init__(self, user=None, pull_request=None):
1585 def __init__(self, user=None, pull_request=None):
1582 self.user = user
1586 self.user = user
1583 self.pull_request = pull_request
1587 self.pull_request = pull_request
1584
1588
1585 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1589 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1586 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1590 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1587 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1591 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1588
1592
1589 user = relationship('User')
1593 user = relationship('User')
1590 pull_request = relationship('PullRequest')
1594 pull_request = relationship('PullRequest')
1591
1595
1592
1596
1593 class Notification(Base, BaseModel):
1597 class Notification(Base, BaseModel):
1594 __tablename__ = 'notifications'
1598 __tablename__ = 'notifications'
1595 __table_args__ = (
1599 __table_args__ = (
1596 Index('notification_type_idx', 'type'),
1600 Index('notification_type_idx', 'type'),
1597 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1601 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1598 'mysql_charset': 'utf8'},
1602 'mysql_charset': 'utf8'},
1599 )
1603 )
1600
1604
1601 TYPE_CHANGESET_COMMENT = u'cs_comment'
1605 TYPE_CHANGESET_COMMENT = u'cs_comment'
1602 TYPE_MESSAGE = u'message'
1606 TYPE_MESSAGE = u'message'
1603 TYPE_MENTION = u'mention'
1607 TYPE_MENTION = u'mention'
1604 TYPE_REGISTRATION = u'registration'
1608 TYPE_REGISTRATION = u'registration'
1605 TYPE_PULL_REQUEST = u'pull_request'
1609 TYPE_PULL_REQUEST = u'pull_request'
1606 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1610 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1607
1611
1608 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1612 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1609 subject = Column('subject', Unicode(512), nullable=True)
1613 subject = Column('subject', Unicode(512), nullable=True)
1610 body = Column('body', UnicodeText(50000), nullable=True)
1614 body = Column('body', UnicodeText(50000), nullable=True)
1611 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1615 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1612 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1616 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1613 type_ = Column('type', Unicode(256))
1617 type_ = Column('type', Unicode(256))
1614
1618
1615 created_by_user = relationship('User')
1619 created_by_user = relationship('User')
1616 notifications_to_users = relationship('UserNotification', lazy='joined',
1620 notifications_to_users = relationship('UserNotification', lazy='joined',
1617 cascade="all, delete, delete-orphan")
1621 cascade="all, delete, delete-orphan")
1618
1622
1619 @property
1623 @property
1620 def recipients(self):
1624 def recipients(self):
1621 return [x.user for x in UserNotification.query()\
1625 return [x.user for x in UserNotification.query()\
1622 .filter(UserNotification.notification == self)\
1626 .filter(UserNotification.notification == self)\
1623 .order_by(UserNotification.user_id.asc()).all()]
1627 .order_by(UserNotification.user_id.asc()).all()]
1624
1628
1625 @classmethod
1629 @classmethod
1626 def create(cls, created_by, subject, body, recipients, type_=None):
1630 def create(cls, created_by, subject, body, recipients, type_=None):
1627 if type_ is None:
1631 if type_ is None:
1628 type_ = Notification.TYPE_MESSAGE
1632 type_ = Notification.TYPE_MESSAGE
1629
1633
1630 notification = cls()
1634 notification = cls()
1631 notification.created_by_user = created_by
1635 notification.created_by_user = created_by
1632 notification.subject = subject
1636 notification.subject = subject
1633 notification.body = body
1637 notification.body = body
1634 notification.type_ = type_
1638 notification.type_ = type_
1635 notification.created_on = datetime.datetime.now()
1639 notification.created_on = datetime.datetime.now()
1636
1640
1637 for u in recipients:
1641 for u in recipients:
1638 assoc = UserNotification()
1642 assoc = UserNotification()
1639 assoc.notification = notification
1643 assoc.notification = notification
1640 u.notifications.append(assoc)
1644 u.notifications.append(assoc)
1641 Session().add(notification)
1645 Session().add(notification)
1642 return notification
1646 return notification
1643
1647
1644 @property
1648 @property
1645 def description(self):
1649 def description(self):
1646 from rhodecode.model.notification import NotificationModel
1650 from rhodecode.model.notification import NotificationModel
1647 return NotificationModel().make_description(self)
1651 return NotificationModel().make_description(self)
1648
1652
1649
1653
1650 class UserNotification(Base, BaseModel):
1654 class UserNotification(Base, BaseModel):
1651 __tablename__ = 'user_to_notification'
1655 __tablename__ = 'user_to_notification'
1652 __table_args__ = (
1656 __table_args__ = (
1653 UniqueConstraint('user_id', 'notification_id'),
1657 UniqueConstraint('user_id', 'notification_id'),
1654 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1658 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1655 'mysql_charset': 'utf8'}
1659 'mysql_charset': 'utf8'}
1656 )
1660 )
1657 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1661 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1658 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1662 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1659 read = Column('read', Boolean, default=False)
1663 read = Column('read', Boolean, default=False)
1660 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1664 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1661
1665
1662 user = relationship('User', lazy="joined")
1666 user = relationship('User', lazy="joined")
1663 notification = relationship('Notification', lazy="joined",
1667 notification = relationship('Notification', lazy="joined",
1664 order_by=lambda: Notification.created_on.desc(),)
1668 order_by=lambda: Notification.created_on.desc(),)
1665
1669
1666 def mark_as_read(self):
1670 def mark_as_read(self):
1667 self.read = True
1671 self.read = True
1668 Session().add(self)
1672 Session().add(self)
1669
1673
1670
1674
1671 class DbMigrateVersion(Base, BaseModel):
1675 class DbMigrateVersion(Base, BaseModel):
1672 __tablename__ = 'db_migrate_version'
1676 __tablename__ = 'db_migrate_version'
1673 __table_args__ = (
1677 __table_args__ = (
1674 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1678 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1675 'mysql_charset': 'utf8'},
1679 'mysql_charset': 'utf8'},
1676 )
1680 )
1677 repository_id = Column('repository_id', String(250), primary_key=True)
1681 repository_id = Column('repository_id', String(250), primary_key=True)
1678 repository_path = Column('repository_path', Text)
1682 repository_path = Column('repository_path', Text)
1679 version = Column('version', Integer)
1683 version = Column('version', Integer)
General Comments 0
You need to be logged in to leave comments. Login now