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