##// END OF EJS Templates
autocomplete: improve the lookup of value_display by re-using the object....
marcink -
r1091:c9d29b6e default
parent child Browse files
Show More
@@ -1,1053 +1,1055 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Repository model for rhodecode
22 Repository model for rhodecode
23 """
23 """
24
24
25 import logging
25 import logging
26 import os
26 import os
27 import re
27 import re
28 import shutil
28 import shutil
29 import time
29 import time
30 import traceback
30 import traceback
31 from datetime import datetime
31 from datetime import datetime
32
32
33 from sqlalchemy.sql import func
33 from sqlalchemy.sql import func
34 from sqlalchemy.sql.expression import true, or_
34 from sqlalchemy.sql.expression import true, or_
35 from zope.cachedescriptors.property import Lazy as LazyProperty
35 from zope.cachedescriptors.property import Lazy as LazyProperty
36
36
37 from rhodecode import events
37 from rhodecode import events
38 from rhodecode.lib import helpers as h
38 from rhodecode.lib import helpers as h
39 from rhodecode.lib.auth import HasUserGroupPermissionAny
39 from rhodecode.lib.auth import HasUserGroupPermissionAny
40 from rhodecode.lib.caching_query import FromCache
40 from rhodecode.lib.caching_query import FromCache
41 from rhodecode.lib.exceptions import AttachedForksError
41 from rhodecode.lib.exceptions import AttachedForksError
42 from rhodecode.lib.hooks_base import log_delete_repository
42 from rhodecode.lib.hooks_base import log_delete_repository
43 from rhodecode.lib.markup_renderer import MarkupRenderer
43 from rhodecode.lib.markup_renderer import MarkupRenderer
44 from rhodecode.lib.utils import make_db_config
44 from rhodecode.lib.utils import make_db_config
45 from rhodecode.lib.utils2 import (
45 from rhodecode.lib.utils2 import (
46 safe_str, safe_unicode, remove_prefix, obfuscate_url_pw,
46 safe_str, safe_unicode, remove_prefix, obfuscate_url_pw,
47 get_current_rhodecode_user, safe_int, datetime_to_time, action_logger_generic)
47 get_current_rhodecode_user, safe_int, datetime_to_time, action_logger_generic)
48 from rhodecode.lib.vcs.backends import get_backend
48 from rhodecode.lib.vcs.backends import get_backend
49 from rhodecode.lib.vcs.exceptions import NodeDoesNotExistError
49 from rhodecode.lib.vcs.exceptions import NodeDoesNotExistError
50 from rhodecode.model import BaseModel
50 from rhodecode.model import BaseModel
51 from rhodecode.model.db import (
51 from rhodecode.model.db import (
52 Repository, UserRepoToPerm, UserGroupRepoToPerm, UserRepoGroupToPerm,
52 Repository, UserRepoToPerm, UserGroupRepoToPerm, UserRepoGroupToPerm,
53 UserGroupRepoGroupToPerm, User, Permission, Statistics, UserGroup,
53 UserGroupRepoGroupToPerm, User, Permission, Statistics, UserGroup,
54 RepoGroup, RepositoryField)
54 RepoGroup, RepositoryField)
55 from rhodecode.model.scm import UserGroupList
55 from rhodecode.model.scm import UserGroupList
56 from rhodecode.model.settings import VcsSettingsModel
56 from rhodecode.model.settings import VcsSettingsModel
57
57
58
58
59 log = logging.getLogger(__name__)
59 log = logging.getLogger(__name__)
60
60
61
61
62 class RepoModel(BaseModel):
62 class RepoModel(BaseModel):
63
63
64 cls = Repository
64 cls = Repository
65
65
66 def _get_user_group(self, users_group):
66 def _get_user_group(self, users_group):
67 return self._get_instance(UserGroup, users_group,
67 return self._get_instance(UserGroup, users_group,
68 callback=UserGroup.get_by_group_name)
68 callback=UserGroup.get_by_group_name)
69
69
70 def _get_repo_group(self, repo_group):
70 def _get_repo_group(self, repo_group):
71 return self._get_instance(RepoGroup, repo_group,
71 return self._get_instance(RepoGroup, repo_group,
72 callback=RepoGroup.get_by_group_name)
72 callback=RepoGroup.get_by_group_name)
73
73
74 def _create_default_perms(self, repository, private):
74 def _create_default_perms(self, repository, private):
75 # create default permission
75 # create default permission
76 default = 'repository.read'
76 default = 'repository.read'
77 def_user = User.get_default_user()
77 def_user = User.get_default_user()
78 for p in def_user.user_perms:
78 for p in def_user.user_perms:
79 if p.permission.permission_name.startswith('repository.'):
79 if p.permission.permission_name.startswith('repository.'):
80 default = p.permission.permission_name
80 default = p.permission.permission_name
81 break
81 break
82
82
83 default_perm = 'repository.none' if private else default
83 default_perm = 'repository.none' if private else default
84
84
85 repo_to_perm = UserRepoToPerm()
85 repo_to_perm = UserRepoToPerm()
86 repo_to_perm.permission = Permission.get_by_key(default_perm)
86 repo_to_perm.permission = Permission.get_by_key(default_perm)
87
87
88 repo_to_perm.repository = repository
88 repo_to_perm.repository = repository
89 repo_to_perm.user_id = def_user.user_id
89 repo_to_perm.user_id = def_user.user_id
90
90
91 return repo_to_perm
91 return repo_to_perm
92
92
93 @LazyProperty
93 @LazyProperty
94 def repos_path(self):
94 def repos_path(self):
95 """
95 """
96 Gets the repositories root path from database
96 Gets the repositories root path from database
97 """
97 """
98 settings_model = VcsSettingsModel(sa=self.sa)
98 settings_model = VcsSettingsModel(sa=self.sa)
99 return settings_model.get_repos_location()
99 return settings_model.get_repos_location()
100
100
101 def get(self, repo_id, cache=False):
101 def get(self, repo_id, cache=False):
102 repo = self.sa.query(Repository) \
102 repo = self.sa.query(Repository) \
103 .filter(Repository.repo_id == repo_id)
103 .filter(Repository.repo_id == repo_id)
104
104
105 if cache:
105 if cache:
106 repo = repo.options(FromCache("sql_cache_short",
106 repo = repo.options(FromCache("sql_cache_short",
107 "get_repo_%s" % repo_id))
107 "get_repo_%s" % repo_id))
108 return repo.scalar()
108 return repo.scalar()
109
109
110 def get_repo(self, repository):
110 def get_repo(self, repository):
111 return self._get_repo(repository)
111 return self._get_repo(repository)
112
112
113 def get_by_repo_name(self, repo_name, cache=False):
113 def get_by_repo_name(self, repo_name, cache=False):
114 repo = self.sa.query(Repository) \
114 repo = self.sa.query(Repository) \
115 .filter(Repository.repo_name == repo_name)
115 .filter(Repository.repo_name == repo_name)
116
116
117 if cache:
117 if cache:
118 repo = repo.options(FromCache("sql_cache_short",
118 repo = repo.options(FromCache("sql_cache_short",
119 "get_repo_%s" % repo_name))
119 "get_repo_%s" % repo_name))
120 return repo.scalar()
120 return repo.scalar()
121
121
122 def _extract_id_from_repo_name(self, repo_name):
122 def _extract_id_from_repo_name(self, repo_name):
123 if repo_name.startswith('/'):
123 if repo_name.startswith('/'):
124 repo_name = repo_name.lstrip('/')
124 repo_name = repo_name.lstrip('/')
125 by_id_match = re.match(r'^_(\d{1,})', repo_name)
125 by_id_match = re.match(r'^_(\d{1,})', repo_name)
126 if by_id_match:
126 if by_id_match:
127 return by_id_match.groups()[0]
127 return by_id_match.groups()[0]
128
128
129 def get_repo_by_id(self, repo_name):
129 def get_repo_by_id(self, repo_name):
130 """
130 """
131 Extracts repo_name by id from special urls.
131 Extracts repo_name by id from special urls.
132 Example url is _11/repo_name
132 Example url is _11/repo_name
133
133
134 :param repo_name:
134 :param repo_name:
135 :return: repo object if matched else None
135 :return: repo object if matched else None
136 """
136 """
137 try:
137 try:
138 _repo_id = self._extract_id_from_repo_name(repo_name)
138 _repo_id = self._extract_id_from_repo_name(repo_name)
139 if _repo_id:
139 if _repo_id:
140 return self.get(_repo_id)
140 return self.get(_repo_id)
141 except Exception:
141 except Exception:
142 log.exception('Failed to extract repo_name from URL')
142 log.exception('Failed to extract repo_name from URL')
143
143
144 return None
144 return None
145
145
146 def get_url(self, repo):
146 def get_url(self, repo):
147 return h.url('summary_home', repo_name=safe_str(repo.repo_name),
147 return h.url('summary_home', repo_name=safe_str(repo.repo_name),
148 qualified=True)
148 qualified=True)
149
149
150 def get_users(self, name_contains=None, limit=20, only_active=True):
150 def get_users(self, name_contains=None, limit=20, only_active=True):
151
151 # TODO: mikhail: move this method to the UserModel.
152 # TODO: mikhail: move this method to the UserModel.
152 query = self.sa.query(User)
153 query = self.sa.query(User)
153 if only_active:
154 if only_active:
154 query = query.filter(User.active == true())
155 query = query.filter(User.active == true())
155
156
156 if name_contains:
157 if name_contains:
157 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
158 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
158 query = query.filter(
159 query = query.filter(
159 or_(
160 or_(
160 User.name.ilike(ilike_expression),
161 User.name.ilike(ilike_expression),
161 User.lastname.ilike(ilike_expression),
162 User.lastname.ilike(ilike_expression),
162 User.username.ilike(ilike_expression)
163 User.username.ilike(ilike_expression)
163 )
164 )
164 )
165 )
165 query = query.limit(limit)
166 query = query.limit(limit)
166 users = query.all()
167 users = query.all()
167
168
168 _users = [
169 _users = [
169 {
170 {
170 'id': user.user_id,
171 'id': user.user_id,
171 'first_name': user.name,
172 'first_name': user.name,
172 'last_name': user.lastname,
173 'last_name': user.lastname,
173 'username': user.username,
174 'username': user.username,
175 'email': user.email,
174 'icon_link': h.gravatar_url(user.email, 30),
176 'icon_link': h.gravatar_url(user.email, 30),
175 'value_display': h.person(user.email),
177 'value_display': h.person(user),
176 'value': user.username,
178 'value': user.username,
177 'value_type': 'user',
179 'value_type': 'user',
178 'active': user.active,
180 'active': user.active,
179 }
181 }
180 for user in users
182 for user in users
181 ]
183 ]
182 return _users
184 return _users
183
185
184 def get_user_groups(self, name_contains=None, limit=20, only_active=True):
186 def get_user_groups(self, name_contains=None, limit=20, only_active=True):
185 # TODO: mikhail: move this method to the UserGroupModel.
187 # TODO: mikhail: move this method to the UserGroupModel.
186 query = self.sa.query(UserGroup)
188 query = self.sa.query(UserGroup)
187 if only_active:
189 if only_active:
188 query = query.filter(UserGroup.users_group_active == true())
190 query = query.filter(UserGroup.users_group_active == true())
189
191
190 if name_contains:
192 if name_contains:
191 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
193 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
192 query = query.filter(
194 query = query.filter(
193 UserGroup.users_group_name.ilike(ilike_expression))\
195 UserGroup.users_group_name.ilike(ilike_expression))\
194 .order_by(func.length(UserGroup.users_group_name))\
196 .order_by(func.length(UserGroup.users_group_name))\
195 .order_by(UserGroup.users_group_name)
197 .order_by(UserGroup.users_group_name)
196
198
197 query = query.limit(limit)
199 query = query.limit(limit)
198 user_groups = query.all()
200 user_groups = query.all()
199 perm_set = ['usergroup.read', 'usergroup.write', 'usergroup.admin']
201 perm_set = ['usergroup.read', 'usergroup.write', 'usergroup.admin']
200 user_groups = UserGroupList(user_groups, perm_set=perm_set)
202 user_groups = UserGroupList(user_groups, perm_set=perm_set)
201
203
202 _groups = [
204 _groups = [
203 {
205 {
204 'id': group.users_group_id,
206 'id': group.users_group_id,
205 # TODO: marcink figure out a way to generate the url for the
207 # TODO: marcink figure out a way to generate the url for the
206 # icon
208 # icon
207 'icon_link': '',
209 'icon_link': '',
208 'value_display': 'Group: %s (%d members)' % (
210 'value_display': 'Group: %s (%d members)' % (
209 group.users_group_name, len(group.members),),
211 group.users_group_name, len(group.members),),
210 'value': group.users_group_name,
212 'value': group.users_group_name,
211 'value_type': 'user_group',
213 'value_type': 'user_group',
212 'active': group.users_group_active,
214 'active': group.users_group_active,
213 }
215 }
214 for group in user_groups
216 for group in user_groups
215 ]
217 ]
216 return _groups
218 return _groups
217
219
218 @classmethod
220 @classmethod
219 def update_repoinfo(cls, repositories=None):
221 def update_repoinfo(cls, repositories=None):
220 if not repositories:
222 if not repositories:
221 repositories = Repository.getAll()
223 repositories = Repository.getAll()
222 for repo in repositories:
224 for repo in repositories:
223 repo.update_commit_cache()
225 repo.update_commit_cache()
224
226
225 def get_repos_as_dict(self, repo_list=None, admin=False,
227 def get_repos_as_dict(self, repo_list=None, admin=False,
226 super_user_actions=False):
228 super_user_actions=False):
227
229
228 from rhodecode.lib.utils import PartialRenderer
230 from rhodecode.lib.utils import PartialRenderer
229 _render = PartialRenderer('data_table/_dt_elements.html')
231 _render = PartialRenderer('data_table/_dt_elements.html')
230 c = _render.c
232 c = _render.c
231
233
232 def quick_menu(repo_name):
234 def quick_menu(repo_name):
233 return _render('quick_menu', repo_name)
235 return _render('quick_menu', repo_name)
234
236
235 def repo_lnk(name, rtype, rstate, private, fork_of):
237 def repo_lnk(name, rtype, rstate, private, fork_of):
236 return _render('repo_name', name, rtype, rstate, private, fork_of,
238 return _render('repo_name', name, rtype, rstate, private, fork_of,
237 short_name=not admin, admin=False)
239 short_name=not admin, admin=False)
238
240
239 def last_change(last_change):
241 def last_change(last_change):
240 return _render("last_change", last_change)
242 return _render("last_change", last_change)
241
243
242 def rss_lnk(repo_name):
244 def rss_lnk(repo_name):
243 return _render("rss", repo_name)
245 return _render("rss", repo_name)
244
246
245 def atom_lnk(repo_name):
247 def atom_lnk(repo_name):
246 return _render("atom", repo_name)
248 return _render("atom", repo_name)
247
249
248 def last_rev(repo_name, cs_cache):
250 def last_rev(repo_name, cs_cache):
249 return _render('revision', repo_name, cs_cache.get('revision'),
251 return _render('revision', repo_name, cs_cache.get('revision'),
250 cs_cache.get('raw_id'), cs_cache.get('author'),
252 cs_cache.get('raw_id'), cs_cache.get('author'),
251 cs_cache.get('message'))
253 cs_cache.get('message'))
252
254
253 def desc(desc):
255 def desc(desc):
254 if c.visual.stylify_metatags:
256 if c.visual.stylify_metatags:
255 return h.urlify_text(h.escaped_stylize(h.truncate(desc, 60)))
257 return h.urlify_text(h.escaped_stylize(h.truncate(desc, 60)))
256 else:
258 else:
257 return h.urlify_text(h.html_escape(h.truncate(desc, 60)))
259 return h.urlify_text(h.html_escape(h.truncate(desc, 60)))
258
260
259 def state(repo_state):
261 def state(repo_state):
260 return _render("repo_state", repo_state)
262 return _render("repo_state", repo_state)
261
263
262 def repo_actions(repo_name):
264 def repo_actions(repo_name):
263 return _render('repo_actions', repo_name, super_user_actions)
265 return _render('repo_actions', repo_name, super_user_actions)
264
266
265 def user_profile(username):
267 def user_profile(username):
266 return _render('user_profile', username)
268 return _render('user_profile', username)
267
269
268 repos_data = []
270 repos_data = []
269 for repo in repo_list:
271 for repo in repo_list:
270 cs_cache = repo.changeset_cache
272 cs_cache = repo.changeset_cache
271 row = {
273 row = {
272 "menu": quick_menu(repo.repo_name),
274 "menu": quick_menu(repo.repo_name),
273
275
274 "name": repo_lnk(repo.repo_name, repo.repo_type,
276 "name": repo_lnk(repo.repo_name, repo.repo_type,
275 repo.repo_state, repo.private, repo.fork),
277 repo.repo_state, repo.private, repo.fork),
276 "name_raw": repo.repo_name.lower(),
278 "name_raw": repo.repo_name.lower(),
277
279
278 "last_change": last_change(repo.last_db_change),
280 "last_change": last_change(repo.last_db_change),
279 "last_change_raw": datetime_to_time(repo.last_db_change),
281 "last_change_raw": datetime_to_time(repo.last_db_change),
280
282
281 "last_changeset": last_rev(repo.repo_name, cs_cache),
283 "last_changeset": last_rev(repo.repo_name, cs_cache),
282 "last_changeset_raw": cs_cache.get('revision'),
284 "last_changeset_raw": cs_cache.get('revision'),
283
285
284 "desc": desc(repo.description),
286 "desc": desc(repo.description),
285 "owner": user_profile(repo.user.username),
287 "owner": user_profile(repo.user.username),
286
288
287 "state": state(repo.repo_state),
289 "state": state(repo.repo_state),
288 "rss": rss_lnk(repo.repo_name),
290 "rss": rss_lnk(repo.repo_name),
289
291
290 "atom": atom_lnk(repo.repo_name),
292 "atom": atom_lnk(repo.repo_name),
291 }
293 }
292 if admin:
294 if admin:
293 row.update({
295 row.update({
294 "action": repo_actions(repo.repo_name),
296 "action": repo_actions(repo.repo_name),
295 })
297 })
296 repos_data.append(row)
298 repos_data.append(row)
297
299
298 return repos_data
300 return repos_data
299
301
300 def _get_defaults(self, repo_name):
302 def _get_defaults(self, repo_name):
301 """
303 """
302 Gets information about repository, and returns a dict for
304 Gets information about repository, and returns a dict for
303 usage in forms
305 usage in forms
304
306
305 :param repo_name:
307 :param repo_name:
306 """
308 """
307
309
308 repo_info = Repository.get_by_repo_name(repo_name)
310 repo_info = Repository.get_by_repo_name(repo_name)
309
311
310 if repo_info is None:
312 if repo_info is None:
311 return None
313 return None
312
314
313 defaults = repo_info.get_dict()
315 defaults = repo_info.get_dict()
314 defaults['repo_name'] = repo_info.just_name
316 defaults['repo_name'] = repo_info.just_name
315
317
316 groups = repo_info.groups_with_parents
318 groups = repo_info.groups_with_parents
317 parent_group = groups[-1] if groups else None
319 parent_group = groups[-1] if groups else None
318
320
319 # we use -1 as this is how in HTML, we mark an empty group
321 # we use -1 as this is how in HTML, we mark an empty group
320 defaults['repo_group'] = getattr(parent_group, 'group_id', -1)
322 defaults['repo_group'] = getattr(parent_group, 'group_id', -1)
321
323
322 keys_to_process = (
324 keys_to_process = (
323 {'k': 'repo_type', 'strip': False},
325 {'k': 'repo_type', 'strip': False},
324 {'k': 'repo_enable_downloads', 'strip': True},
326 {'k': 'repo_enable_downloads', 'strip': True},
325 {'k': 'repo_description', 'strip': True},
327 {'k': 'repo_description', 'strip': True},
326 {'k': 'repo_enable_locking', 'strip': True},
328 {'k': 'repo_enable_locking', 'strip': True},
327 {'k': 'repo_landing_rev', 'strip': True},
329 {'k': 'repo_landing_rev', 'strip': True},
328 {'k': 'clone_uri', 'strip': False},
330 {'k': 'clone_uri', 'strip': False},
329 {'k': 'repo_private', 'strip': True},
331 {'k': 'repo_private', 'strip': True},
330 {'k': 'repo_enable_statistics', 'strip': True}
332 {'k': 'repo_enable_statistics', 'strip': True}
331 )
333 )
332
334
333 for item in keys_to_process:
335 for item in keys_to_process:
334 attr = item['k']
336 attr = item['k']
335 if item['strip']:
337 if item['strip']:
336 attr = remove_prefix(item['k'], 'repo_')
338 attr = remove_prefix(item['k'], 'repo_')
337
339
338 val = defaults[attr]
340 val = defaults[attr]
339 if item['k'] == 'repo_landing_rev':
341 if item['k'] == 'repo_landing_rev':
340 val = ':'.join(defaults[attr])
342 val = ':'.join(defaults[attr])
341 defaults[item['k']] = val
343 defaults[item['k']] = val
342 if item['k'] == 'clone_uri':
344 if item['k'] == 'clone_uri':
343 defaults['clone_uri_hidden'] = repo_info.clone_uri_hidden
345 defaults['clone_uri_hidden'] = repo_info.clone_uri_hidden
344
346
345 # fill owner
347 # fill owner
346 if repo_info.user:
348 if repo_info.user:
347 defaults.update({'user': repo_info.user.username})
349 defaults.update({'user': repo_info.user.username})
348 else:
350 else:
349 replacement_user = User.get_first_super_admin().username
351 replacement_user = User.get_first_super_admin().username
350 defaults.update({'user': replacement_user})
352 defaults.update({'user': replacement_user})
351
353
352 # fill repository users
354 # fill repository users
353 for p in repo_info.repo_to_perm:
355 for p in repo_info.repo_to_perm:
354 defaults.update({'u_perm_%s' % p.user.user_id:
356 defaults.update({'u_perm_%s' % p.user.user_id:
355 p.permission.permission_name})
357 p.permission.permission_name})
356
358
357 # fill repository groups
359 # fill repository groups
358 for p in repo_info.users_group_to_perm:
360 for p in repo_info.users_group_to_perm:
359 defaults.update({'g_perm_%s' % p.users_group.users_group_id:
361 defaults.update({'g_perm_%s' % p.users_group.users_group_id:
360 p.permission.permission_name})
362 p.permission.permission_name})
361
363
362 return defaults
364 return defaults
363
365
364 def update(self, repo, **kwargs):
366 def update(self, repo, **kwargs):
365 try:
367 try:
366 cur_repo = self._get_repo(repo)
368 cur_repo = self._get_repo(repo)
367 source_repo_name = cur_repo.repo_name
369 source_repo_name = cur_repo.repo_name
368 if 'user' in kwargs:
370 if 'user' in kwargs:
369 cur_repo.user = User.get_by_username(kwargs['user'])
371 cur_repo.user = User.get_by_username(kwargs['user'])
370
372
371 if 'repo_group' in kwargs:
373 if 'repo_group' in kwargs:
372 cur_repo.group = RepoGroup.get(kwargs['repo_group'])
374 cur_repo.group = RepoGroup.get(kwargs['repo_group'])
373 log.debug('Updating repo %s with params:%s', cur_repo, kwargs)
375 log.debug('Updating repo %s with params:%s', cur_repo, kwargs)
374
376
375 update_keys = [
377 update_keys = [
376 (1, 'repo_enable_downloads'),
378 (1, 'repo_enable_downloads'),
377 (1, 'repo_description'),
379 (1, 'repo_description'),
378 (1, 'repo_enable_locking'),
380 (1, 'repo_enable_locking'),
379 (1, 'repo_landing_rev'),
381 (1, 'repo_landing_rev'),
380 (1, 'repo_private'),
382 (1, 'repo_private'),
381 (1, 'repo_enable_statistics'),
383 (1, 'repo_enable_statistics'),
382 (0, 'clone_uri'),
384 (0, 'clone_uri'),
383 (0, 'fork_id')
385 (0, 'fork_id')
384 ]
386 ]
385 for strip, k in update_keys:
387 for strip, k in update_keys:
386 if k in kwargs:
388 if k in kwargs:
387 val = kwargs[k]
389 val = kwargs[k]
388 if strip:
390 if strip:
389 k = remove_prefix(k, 'repo_')
391 k = remove_prefix(k, 'repo_')
390 if k == 'clone_uri':
392 if k == 'clone_uri':
391 from rhodecode.model.validators import Missing
393 from rhodecode.model.validators import Missing
392 _change = kwargs.get('clone_uri_change')
394 _change = kwargs.get('clone_uri_change')
393 if _change in [Missing, 'OLD']:
395 if _change in [Missing, 'OLD']:
394 # we don't change the value, so use original one
396 # we don't change the value, so use original one
395 val = cur_repo.clone_uri
397 val = cur_repo.clone_uri
396
398
397 setattr(cur_repo, k, val)
399 setattr(cur_repo, k, val)
398
400
399 new_name = cur_repo.get_new_name(kwargs['repo_name'])
401 new_name = cur_repo.get_new_name(kwargs['repo_name'])
400 cur_repo.repo_name = new_name
402 cur_repo.repo_name = new_name
401
403
402 # if private flag is set, reset default permission to NONE
404 # if private flag is set, reset default permission to NONE
403 if kwargs.get('repo_private'):
405 if kwargs.get('repo_private'):
404 EMPTY_PERM = 'repository.none'
406 EMPTY_PERM = 'repository.none'
405 RepoModel().grant_user_permission(
407 RepoModel().grant_user_permission(
406 repo=cur_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM
408 repo=cur_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM
407 )
409 )
408
410
409 # handle extra fields
411 # handle extra fields
410 for field in filter(lambda k: k.startswith(RepositoryField.PREFIX),
412 for field in filter(lambda k: k.startswith(RepositoryField.PREFIX),
411 kwargs):
413 kwargs):
412 k = RepositoryField.un_prefix_key(field)
414 k = RepositoryField.un_prefix_key(field)
413 ex_field = RepositoryField.get_by_key_name(
415 ex_field = RepositoryField.get_by_key_name(
414 key=k, repo=cur_repo)
416 key=k, repo=cur_repo)
415 if ex_field:
417 if ex_field:
416 ex_field.field_value = kwargs[field]
418 ex_field.field_value = kwargs[field]
417 self.sa.add(ex_field)
419 self.sa.add(ex_field)
418 self.sa.add(cur_repo)
420 self.sa.add(cur_repo)
419
421
420 if source_repo_name != new_name:
422 if source_repo_name != new_name:
421 # rename repository
423 # rename repository
422 self._rename_filesystem_repo(
424 self._rename_filesystem_repo(
423 old=source_repo_name, new=new_name)
425 old=source_repo_name, new=new_name)
424
426
425 return cur_repo
427 return cur_repo
426 except Exception:
428 except Exception:
427 log.error(traceback.format_exc())
429 log.error(traceback.format_exc())
428 raise
430 raise
429
431
430 def _create_repo(self, repo_name, repo_type, description, owner,
432 def _create_repo(self, repo_name, repo_type, description, owner,
431 private=False, clone_uri=None, repo_group=None,
433 private=False, clone_uri=None, repo_group=None,
432 landing_rev='rev:tip', fork_of=None,
434 landing_rev='rev:tip', fork_of=None,
433 copy_fork_permissions=False, enable_statistics=False,
435 copy_fork_permissions=False, enable_statistics=False,
434 enable_locking=False, enable_downloads=False,
436 enable_locking=False, enable_downloads=False,
435 copy_group_permissions=False,
437 copy_group_permissions=False,
436 state=Repository.STATE_PENDING):
438 state=Repository.STATE_PENDING):
437 """
439 """
438 Create repository inside database with PENDING state, this should be
440 Create repository inside database with PENDING state, this should be
439 only executed by create() repo. With exception of importing existing
441 only executed by create() repo. With exception of importing existing
440 repos
442 repos
441 """
443 """
442 from rhodecode.model.scm import ScmModel
444 from rhodecode.model.scm import ScmModel
443
445
444 owner = self._get_user(owner)
446 owner = self._get_user(owner)
445 fork_of = self._get_repo(fork_of)
447 fork_of = self._get_repo(fork_of)
446 repo_group = self._get_repo_group(safe_int(repo_group))
448 repo_group = self._get_repo_group(safe_int(repo_group))
447
449
448 try:
450 try:
449 repo_name = safe_unicode(repo_name)
451 repo_name = safe_unicode(repo_name)
450 description = safe_unicode(description)
452 description = safe_unicode(description)
451 # repo name is just a name of repository
453 # repo name is just a name of repository
452 # while repo_name_full is a full qualified name that is combined
454 # while repo_name_full is a full qualified name that is combined
453 # with name and path of group
455 # with name and path of group
454 repo_name_full = repo_name
456 repo_name_full = repo_name
455 repo_name = repo_name.split(Repository.NAME_SEP)[-1]
457 repo_name = repo_name.split(Repository.NAME_SEP)[-1]
456
458
457 new_repo = Repository()
459 new_repo = Repository()
458 new_repo.repo_state = state
460 new_repo.repo_state = state
459 new_repo.enable_statistics = False
461 new_repo.enable_statistics = False
460 new_repo.repo_name = repo_name_full
462 new_repo.repo_name = repo_name_full
461 new_repo.repo_type = repo_type
463 new_repo.repo_type = repo_type
462 new_repo.user = owner
464 new_repo.user = owner
463 new_repo.group = repo_group
465 new_repo.group = repo_group
464 new_repo.description = description or repo_name
466 new_repo.description = description or repo_name
465 new_repo.private = private
467 new_repo.private = private
466 new_repo.clone_uri = clone_uri
468 new_repo.clone_uri = clone_uri
467 new_repo.landing_rev = landing_rev
469 new_repo.landing_rev = landing_rev
468
470
469 new_repo.enable_statistics = enable_statistics
471 new_repo.enable_statistics = enable_statistics
470 new_repo.enable_locking = enable_locking
472 new_repo.enable_locking = enable_locking
471 new_repo.enable_downloads = enable_downloads
473 new_repo.enable_downloads = enable_downloads
472
474
473 if repo_group:
475 if repo_group:
474 new_repo.enable_locking = repo_group.enable_locking
476 new_repo.enable_locking = repo_group.enable_locking
475
477
476 if fork_of:
478 if fork_of:
477 parent_repo = fork_of
479 parent_repo = fork_of
478 new_repo.fork = parent_repo
480 new_repo.fork = parent_repo
479
481
480 events.trigger(events.RepoPreCreateEvent(new_repo))
482 events.trigger(events.RepoPreCreateEvent(new_repo))
481
483
482 self.sa.add(new_repo)
484 self.sa.add(new_repo)
483
485
484 EMPTY_PERM = 'repository.none'
486 EMPTY_PERM = 'repository.none'
485 if fork_of and copy_fork_permissions:
487 if fork_of and copy_fork_permissions:
486 repo = fork_of
488 repo = fork_of
487 user_perms = UserRepoToPerm.query() \
489 user_perms = UserRepoToPerm.query() \
488 .filter(UserRepoToPerm.repository == repo).all()
490 .filter(UserRepoToPerm.repository == repo).all()
489 group_perms = UserGroupRepoToPerm.query() \
491 group_perms = UserGroupRepoToPerm.query() \
490 .filter(UserGroupRepoToPerm.repository == repo).all()
492 .filter(UserGroupRepoToPerm.repository == repo).all()
491
493
492 for perm in user_perms:
494 for perm in user_perms:
493 UserRepoToPerm.create(
495 UserRepoToPerm.create(
494 perm.user, new_repo, perm.permission)
496 perm.user, new_repo, perm.permission)
495
497
496 for perm in group_perms:
498 for perm in group_perms:
497 UserGroupRepoToPerm.create(
499 UserGroupRepoToPerm.create(
498 perm.users_group, new_repo, perm.permission)
500 perm.users_group, new_repo, perm.permission)
499 # in case we copy permissions and also set this repo to private
501 # in case we copy permissions and also set this repo to private
500 # override the default user permission to make it a private
502 # override the default user permission to make it a private
501 # repo
503 # repo
502 if private:
504 if private:
503 RepoModel(self.sa).grant_user_permission(
505 RepoModel(self.sa).grant_user_permission(
504 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
506 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
505
507
506 elif repo_group and copy_group_permissions:
508 elif repo_group and copy_group_permissions:
507 user_perms = UserRepoGroupToPerm.query() \
509 user_perms = UserRepoGroupToPerm.query() \
508 .filter(UserRepoGroupToPerm.group == repo_group).all()
510 .filter(UserRepoGroupToPerm.group == repo_group).all()
509
511
510 group_perms = UserGroupRepoGroupToPerm.query() \
512 group_perms = UserGroupRepoGroupToPerm.query() \
511 .filter(UserGroupRepoGroupToPerm.group == repo_group).all()
513 .filter(UserGroupRepoGroupToPerm.group == repo_group).all()
512
514
513 for perm in user_perms:
515 for perm in user_perms:
514 perm_name = perm.permission.permission_name.replace(
516 perm_name = perm.permission.permission_name.replace(
515 'group.', 'repository.')
517 'group.', 'repository.')
516 perm_obj = Permission.get_by_key(perm_name)
518 perm_obj = Permission.get_by_key(perm_name)
517 UserRepoToPerm.create(perm.user, new_repo, perm_obj)
519 UserRepoToPerm.create(perm.user, new_repo, perm_obj)
518
520
519 for perm in group_perms:
521 for perm in group_perms:
520 perm_name = perm.permission.permission_name.replace(
522 perm_name = perm.permission.permission_name.replace(
521 'group.', 'repository.')
523 'group.', 'repository.')
522 perm_obj = Permission.get_by_key(perm_name)
524 perm_obj = Permission.get_by_key(perm_name)
523 UserGroupRepoToPerm.create(
525 UserGroupRepoToPerm.create(
524 perm.users_group, new_repo, perm_obj)
526 perm.users_group, new_repo, perm_obj)
525
527
526 if private:
528 if private:
527 RepoModel(self.sa).grant_user_permission(
529 RepoModel(self.sa).grant_user_permission(
528 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
530 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
529
531
530 else:
532 else:
531 perm_obj = self._create_default_perms(new_repo, private)
533 perm_obj = self._create_default_perms(new_repo, private)
532 self.sa.add(perm_obj)
534 self.sa.add(perm_obj)
533
535
534 # now automatically start following this repository as owner
536 # now automatically start following this repository as owner
535 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
537 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
536 owner.user_id)
538 owner.user_id)
537
539
538 # we need to flush here, in order to check if database won't
540 # we need to flush here, in order to check if database won't
539 # throw any exceptions, create filesystem dirs at the very end
541 # throw any exceptions, create filesystem dirs at the very end
540 self.sa.flush()
542 self.sa.flush()
541 events.trigger(events.RepoCreateEvent(new_repo))
543 events.trigger(events.RepoCreateEvent(new_repo))
542 return new_repo
544 return new_repo
543
545
544 except Exception:
546 except Exception:
545 log.error(traceback.format_exc())
547 log.error(traceback.format_exc())
546 raise
548 raise
547
549
548 def create(self, form_data, cur_user):
550 def create(self, form_data, cur_user):
549 """
551 """
550 Create repository using celery tasks
552 Create repository using celery tasks
551
553
552 :param form_data:
554 :param form_data:
553 :param cur_user:
555 :param cur_user:
554 """
556 """
555 from rhodecode.lib.celerylib import tasks, run_task
557 from rhodecode.lib.celerylib import tasks, run_task
556 return run_task(tasks.create_repo, form_data, cur_user)
558 return run_task(tasks.create_repo, form_data, cur_user)
557
559
558 def update_permissions(self, repo, perm_additions=None, perm_updates=None,
560 def update_permissions(self, repo, perm_additions=None, perm_updates=None,
559 perm_deletions=None, check_perms=True,
561 perm_deletions=None, check_perms=True,
560 cur_user=None):
562 cur_user=None):
561 if not perm_additions:
563 if not perm_additions:
562 perm_additions = []
564 perm_additions = []
563 if not perm_updates:
565 if not perm_updates:
564 perm_updates = []
566 perm_updates = []
565 if not perm_deletions:
567 if not perm_deletions:
566 perm_deletions = []
568 perm_deletions = []
567
569
568 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
570 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
569
571
570 # update permissions
572 # update permissions
571 for member_id, perm, member_type in perm_updates:
573 for member_id, perm, member_type in perm_updates:
572 member_id = int(member_id)
574 member_id = int(member_id)
573 if member_type == 'user':
575 if member_type == 'user':
574 # this updates also current one if found
576 # this updates also current one if found
575 self.grant_user_permission(
577 self.grant_user_permission(
576 repo=repo, user=member_id, perm=perm)
578 repo=repo, user=member_id, perm=perm)
577 else: # set for user group
579 else: # set for user group
578 # check if we have permissions to alter this usergroup
580 # check if we have permissions to alter this usergroup
579 member_name = UserGroup.get(member_id).users_group_name
581 member_name = UserGroup.get(member_id).users_group_name
580 if not check_perms or HasUserGroupPermissionAny(
582 if not check_perms or HasUserGroupPermissionAny(
581 *req_perms)(member_name, user=cur_user):
583 *req_perms)(member_name, user=cur_user):
582 self.grant_user_group_permission(
584 self.grant_user_group_permission(
583 repo=repo, group_name=member_id, perm=perm)
585 repo=repo, group_name=member_id, perm=perm)
584
586
585 # set new permissions
587 # set new permissions
586 for member_id, perm, member_type in perm_additions:
588 for member_id, perm, member_type in perm_additions:
587 member_id = int(member_id)
589 member_id = int(member_id)
588 if member_type == 'user':
590 if member_type == 'user':
589 self.grant_user_permission(
591 self.grant_user_permission(
590 repo=repo, user=member_id, perm=perm)
592 repo=repo, user=member_id, perm=perm)
591 else: # set for user group
593 else: # set for user group
592 # check if we have permissions to alter this usergroup
594 # check if we have permissions to alter this usergroup
593 member_name = UserGroup.get(member_id).users_group_name
595 member_name = UserGroup.get(member_id).users_group_name
594 if not check_perms or HasUserGroupPermissionAny(
596 if not check_perms or HasUserGroupPermissionAny(
595 *req_perms)(member_name, user=cur_user):
597 *req_perms)(member_name, user=cur_user):
596 self.grant_user_group_permission(
598 self.grant_user_group_permission(
597 repo=repo, group_name=member_id, perm=perm)
599 repo=repo, group_name=member_id, perm=perm)
598
600
599 # delete permissions
601 # delete permissions
600 for member_id, perm, member_type in perm_deletions:
602 for member_id, perm, member_type in perm_deletions:
601 member_id = int(member_id)
603 member_id = int(member_id)
602 if member_type == 'user':
604 if member_type == 'user':
603 self.revoke_user_permission(repo=repo, user=member_id)
605 self.revoke_user_permission(repo=repo, user=member_id)
604 else: # set for user group
606 else: # set for user group
605 # check if we have permissions to alter this usergroup
607 # check if we have permissions to alter this usergroup
606 member_name = UserGroup.get(member_id).users_group_name
608 member_name = UserGroup.get(member_id).users_group_name
607 if not check_perms or HasUserGroupPermissionAny(
609 if not check_perms or HasUserGroupPermissionAny(
608 *req_perms)(member_name, user=cur_user):
610 *req_perms)(member_name, user=cur_user):
609 self.revoke_user_group_permission(
611 self.revoke_user_group_permission(
610 repo=repo, group_name=member_id)
612 repo=repo, group_name=member_id)
611
613
612 def create_fork(self, form_data, cur_user):
614 def create_fork(self, form_data, cur_user):
613 """
615 """
614 Simple wrapper into executing celery task for fork creation
616 Simple wrapper into executing celery task for fork creation
615
617
616 :param form_data:
618 :param form_data:
617 :param cur_user:
619 :param cur_user:
618 """
620 """
619 from rhodecode.lib.celerylib import tasks, run_task
621 from rhodecode.lib.celerylib import tasks, run_task
620 return run_task(tasks.create_repo_fork, form_data, cur_user)
622 return run_task(tasks.create_repo_fork, form_data, cur_user)
621
623
622 def delete(self, repo, forks=None, fs_remove=True, cur_user=None):
624 def delete(self, repo, forks=None, fs_remove=True, cur_user=None):
623 """
625 """
624 Delete given repository, forks parameter defines what do do with
626 Delete given repository, forks parameter defines what do do with
625 attached forks. Throws AttachedForksError if deleted repo has attached
627 attached forks. Throws AttachedForksError if deleted repo has attached
626 forks
628 forks
627
629
628 :param repo:
630 :param repo:
629 :param forks: str 'delete' or 'detach'
631 :param forks: str 'delete' or 'detach'
630 :param fs_remove: remove(archive) repo from filesystem
632 :param fs_remove: remove(archive) repo from filesystem
631 """
633 """
632 if not cur_user:
634 if not cur_user:
633 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
635 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
634 repo = self._get_repo(repo)
636 repo = self._get_repo(repo)
635 if repo:
637 if repo:
636 if forks == 'detach':
638 if forks == 'detach':
637 for r in repo.forks:
639 for r in repo.forks:
638 r.fork = None
640 r.fork = None
639 self.sa.add(r)
641 self.sa.add(r)
640 elif forks == 'delete':
642 elif forks == 'delete':
641 for r in repo.forks:
643 for r in repo.forks:
642 self.delete(r, forks='delete')
644 self.delete(r, forks='delete')
643 elif [f for f in repo.forks]:
645 elif [f for f in repo.forks]:
644 raise AttachedForksError()
646 raise AttachedForksError()
645
647
646 old_repo_dict = repo.get_dict()
648 old_repo_dict = repo.get_dict()
647 events.trigger(events.RepoPreDeleteEvent(repo))
649 events.trigger(events.RepoPreDeleteEvent(repo))
648 try:
650 try:
649 self.sa.delete(repo)
651 self.sa.delete(repo)
650 if fs_remove:
652 if fs_remove:
651 self._delete_filesystem_repo(repo)
653 self._delete_filesystem_repo(repo)
652 else:
654 else:
653 log.debug('skipping removal from filesystem')
655 log.debug('skipping removal from filesystem')
654 old_repo_dict.update({
656 old_repo_dict.update({
655 'deleted_by': cur_user,
657 'deleted_by': cur_user,
656 'deleted_on': time.time(),
658 'deleted_on': time.time(),
657 })
659 })
658 log_delete_repository(**old_repo_dict)
660 log_delete_repository(**old_repo_dict)
659 events.trigger(events.RepoDeleteEvent(repo))
661 events.trigger(events.RepoDeleteEvent(repo))
660 except Exception:
662 except Exception:
661 log.error(traceback.format_exc())
663 log.error(traceback.format_exc())
662 raise
664 raise
663
665
664 def grant_user_permission(self, repo, user, perm):
666 def grant_user_permission(self, repo, user, perm):
665 """
667 """
666 Grant permission for user on given repository, or update existing one
668 Grant permission for user on given repository, or update existing one
667 if found
669 if found
668
670
669 :param repo: Instance of Repository, repository_id, or repository name
671 :param repo: Instance of Repository, repository_id, or repository name
670 :param user: Instance of User, user_id or username
672 :param user: Instance of User, user_id or username
671 :param perm: Instance of Permission, or permission_name
673 :param perm: Instance of Permission, or permission_name
672 """
674 """
673 user = self._get_user(user)
675 user = self._get_user(user)
674 repo = self._get_repo(repo)
676 repo = self._get_repo(repo)
675 permission = self._get_perm(perm)
677 permission = self._get_perm(perm)
676
678
677 # check if we have that permission already
679 # check if we have that permission already
678 obj = self.sa.query(UserRepoToPerm) \
680 obj = self.sa.query(UserRepoToPerm) \
679 .filter(UserRepoToPerm.user == user) \
681 .filter(UserRepoToPerm.user == user) \
680 .filter(UserRepoToPerm.repository == repo) \
682 .filter(UserRepoToPerm.repository == repo) \
681 .scalar()
683 .scalar()
682 if obj is None:
684 if obj is None:
683 # create new !
685 # create new !
684 obj = UserRepoToPerm()
686 obj = UserRepoToPerm()
685 obj.repository = repo
687 obj.repository = repo
686 obj.user = user
688 obj.user = user
687 obj.permission = permission
689 obj.permission = permission
688 self.sa.add(obj)
690 self.sa.add(obj)
689 log.debug('Granted perm %s to %s on %s', perm, user, repo)
691 log.debug('Granted perm %s to %s on %s', perm, user, repo)
690 action_logger_generic(
692 action_logger_generic(
691 'granted permission: {} to user: {} on repo: {}'.format(
693 'granted permission: {} to user: {} on repo: {}'.format(
692 perm, user, repo), namespace='security.repo')
694 perm, user, repo), namespace='security.repo')
693 return obj
695 return obj
694
696
695 def revoke_user_permission(self, repo, user):
697 def revoke_user_permission(self, repo, user):
696 """
698 """
697 Revoke permission for user on given repository
699 Revoke permission for user on given repository
698
700
699 :param repo: Instance of Repository, repository_id, or repository name
701 :param repo: Instance of Repository, repository_id, or repository name
700 :param user: Instance of User, user_id or username
702 :param user: Instance of User, user_id or username
701 """
703 """
702
704
703 user = self._get_user(user)
705 user = self._get_user(user)
704 repo = self._get_repo(repo)
706 repo = self._get_repo(repo)
705
707
706 obj = self.sa.query(UserRepoToPerm) \
708 obj = self.sa.query(UserRepoToPerm) \
707 .filter(UserRepoToPerm.repository == repo) \
709 .filter(UserRepoToPerm.repository == repo) \
708 .filter(UserRepoToPerm.user == user) \
710 .filter(UserRepoToPerm.user == user) \
709 .scalar()
711 .scalar()
710 if obj:
712 if obj:
711 self.sa.delete(obj)
713 self.sa.delete(obj)
712 log.debug('Revoked perm on %s on %s', repo, user)
714 log.debug('Revoked perm on %s on %s', repo, user)
713 action_logger_generic(
715 action_logger_generic(
714 'revoked permission from user: {} on repo: {}'.format(
716 'revoked permission from user: {} on repo: {}'.format(
715 user, repo), namespace='security.repo')
717 user, repo), namespace='security.repo')
716
718
717 def grant_user_group_permission(self, repo, group_name, perm):
719 def grant_user_group_permission(self, repo, group_name, perm):
718 """
720 """
719 Grant permission for user group on given repository, or update
721 Grant permission for user group on given repository, or update
720 existing one if found
722 existing one if found
721
723
722 :param repo: Instance of Repository, repository_id, or repository name
724 :param repo: Instance of Repository, repository_id, or repository name
723 :param group_name: Instance of UserGroup, users_group_id,
725 :param group_name: Instance of UserGroup, users_group_id,
724 or user group name
726 or user group name
725 :param perm: Instance of Permission, or permission_name
727 :param perm: Instance of Permission, or permission_name
726 """
728 """
727 repo = self._get_repo(repo)
729 repo = self._get_repo(repo)
728 group_name = self._get_user_group(group_name)
730 group_name = self._get_user_group(group_name)
729 permission = self._get_perm(perm)
731 permission = self._get_perm(perm)
730
732
731 # check if we have that permission already
733 # check if we have that permission already
732 obj = self.sa.query(UserGroupRepoToPerm) \
734 obj = self.sa.query(UserGroupRepoToPerm) \
733 .filter(UserGroupRepoToPerm.users_group == group_name) \
735 .filter(UserGroupRepoToPerm.users_group == group_name) \
734 .filter(UserGroupRepoToPerm.repository == repo) \
736 .filter(UserGroupRepoToPerm.repository == repo) \
735 .scalar()
737 .scalar()
736
738
737 if obj is None:
739 if obj is None:
738 # create new
740 # create new
739 obj = UserGroupRepoToPerm()
741 obj = UserGroupRepoToPerm()
740
742
741 obj.repository = repo
743 obj.repository = repo
742 obj.users_group = group_name
744 obj.users_group = group_name
743 obj.permission = permission
745 obj.permission = permission
744 self.sa.add(obj)
746 self.sa.add(obj)
745 log.debug('Granted perm %s to %s on %s', perm, group_name, repo)
747 log.debug('Granted perm %s to %s on %s', perm, group_name, repo)
746 action_logger_generic(
748 action_logger_generic(
747 'granted permission: {} to usergroup: {} on repo: {}'.format(
749 'granted permission: {} to usergroup: {} on repo: {}'.format(
748 perm, group_name, repo), namespace='security.repo')
750 perm, group_name, repo), namespace='security.repo')
749
751
750 return obj
752 return obj
751
753
752 def revoke_user_group_permission(self, repo, group_name):
754 def revoke_user_group_permission(self, repo, group_name):
753 """
755 """
754 Revoke permission for user group on given repository
756 Revoke permission for user group on given repository
755
757
756 :param repo: Instance of Repository, repository_id, or repository name
758 :param repo: Instance of Repository, repository_id, or repository name
757 :param group_name: Instance of UserGroup, users_group_id,
759 :param group_name: Instance of UserGroup, users_group_id,
758 or user group name
760 or user group name
759 """
761 """
760 repo = self._get_repo(repo)
762 repo = self._get_repo(repo)
761 group_name = self._get_user_group(group_name)
763 group_name = self._get_user_group(group_name)
762
764
763 obj = self.sa.query(UserGroupRepoToPerm) \
765 obj = self.sa.query(UserGroupRepoToPerm) \
764 .filter(UserGroupRepoToPerm.repository == repo) \
766 .filter(UserGroupRepoToPerm.repository == repo) \
765 .filter(UserGroupRepoToPerm.users_group == group_name) \
767 .filter(UserGroupRepoToPerm.users_group == group_name) \
766 .scalar()
768 .scalar()
767 if obj:
769 if obj:
768 self.sa.delete(obj)
770 self.sa.delete(obj)
769 log.debug('Revoked perm to %s on %s', repo, group_name)
771 log.debug('Revoked perm to %s on %s', repo, group_name)
770 action_logger_generic(
772 action_logger_generic(
771 'revoked permission from usergroup: {} on repo: {}'.format(
773 'revoked permission from usergroup: {} on repo: {}'.format(
772 group_name, repo), namespace='security.repo')
774 group_name, repo), namespace='security.repo')
773
775
774 def delete_stats(self, repo_name):
776 def delete_stats(self, repo_name):
775 """
777 """
776 removes stats for given repo
778 removes stats for given repo
777
779
778 :param repo_name:
780 :param repo_name:
779 """
781 """
780 repo = self._get_repo(repo_name)
782 repo = self._get_repo(repo_name)
781 try:
783 try:
782 obj = self.sa.query(Statistics) \
784 obj = self.sa.query(Statistics) \
783 .filter(Statistics.repository == repo).scalar()
785 .filter(Statistics.repository == repo).scalar()
784 if obj:
786 if obj:
785 self.sa.delete(obj)
787 self.sa.delete(obj)
786 except Exception:
788 except Exception:
787 log.error(traceback.format_exc())
789 log.error(traceback.format_exc())
788 raise
790 raise
789
791
790 def add_repo_field(self, repo_name, field_key, field_label, field_value='',
792 def add_repo_field(self, repo_name, field_key, field_label, field_value='',
791 field_type='str', field_desc=''):
793 field_type='str', field_desc=''):
792
794
793 repo = self._get_repo(repo_name)
795 repo = self._get_repo(repo_name)
794
796
795 new_field = RepositoryField()
797 new_field = RepositoryField()
796 new_field.repository = repo
798 new_field.repository = repo
797 new_field.field_key = field_key
799 new_field.field_key = field_key
798 new_field.field_type = field_type # python type
800 new_field.field_type = field_type # python type
799 new_field.field_value = field_value
801 new_field.field_value = field_value
800 new_field.field_desc = field_desc
802 new_field.field_desc = field_desc
801 new_field.field_label = field_label
803 new_field.field_label = field_label
802 self.sa.add(new_field)
804 self.sa.add(new_field)
803 return new_field
805 return new_field
804
806
805 def delete_repo_field(self, repo_name, field_key):
807 def delete_repo_field(self, repo_name, field_key):
806 repo = self._get_repo(repo_name)
808 repo = self._get_repo(repo_name)
807 field = RepositoryField.get_by_key_name(field_key, repo)
809 field = RepositoryField.get_by_key_name(field_key, repo)
808 if field:
810 if field:
809 self.sa.delete(field)
811 self.sa.delete(field)
810
812
811 def _create_filesystem_repo(self, repo_name, repo_type, repo_group,
813 def _create_filesystem_repo(self, repo_name, repo_type, repo_group,
812 clone_uri=None, repo_store_location=None,
814 clone_uri=None, repo_store_location=None,
813 use_global_config=False):
815 use_global_config=False):
814 """
816 """
815 makes repository on filesystem. It's group aware means it'll create
817 makes repository on filesystem. It's group aware means it'll create
816 a repository within a group, and alter the paths accordingly of
818 a repository within a group, and alter the paths accordingly of
817 group location
819 group location
818
820
819 :param repo_name:
821 :param repo_name:
820 :param alias:
822 :param alias:
821 :param parent:
823 :param parent:
822 :param clone_uri:
824 :param clone_uri:
823 :param repo_store_location:
825 :param repo_store_location:
824 """
826 """
825 from rhodecode.lib.utils import is_valid_repo, is_valid_repo_group
827 from rhodecode.lib.utils import is_valid_repo, is_valid_repo_group
826 from rhodecode.model.scm import ScmModel
828 from rhodecode.model.scm import ScmModel
827
829
828 if Repository.NAME_SEP in repo_name:
830 if Repository.NAME_SEP in repo_name:
829 raise ValueError(
831 raise ValueError(
830 'repo_name must not contain groups got `%s`' % repo_name)
832 'repo_name must not contain groups got `%s`' % repo_name)
831
833
832 if isinstance(repo_group, RepoGroup):
834 if isinstance(repo_group, RepoGroup):
833 new_parent_path = os.sep.join(repo_group.full_path_splitted)
835 new_parent_path = os.sep.join(repo_group.full_path_splitted)
834 else:
836 else:
835 new_parent_path = repo_group or ''
837 new_parent_path = repo_group or ''
836
838
837 if repo_store_location:
839 if repo_store_location:
838 _paths = [repo_store_location]
840 _paths = [repo_store_location]
839 else:
841 else:
840 _paths = [self.repos_path, new_parent_path, repo_name]
842 _paths = [self.repos_path, new_parent_path, repo_name]
841 # we need to make it str for mercurial
843 # we need to make it str for mercurial
842 repo_path = os.path.join(*map(lambda x: safe_str(x), _paths))
844 repo_path = os.path.join(*map(lambda x: safe_str(x), _paths))
843
845
844 # check if this path is not a repository
846 # check if this path is not a repository
845 if is_valid_repo(repo_path, self.repos_path):
847 if is_valid_repo(repo_path, self.repos_path):
846 raise Exception('This path %s is a valid repository' % repo_path)
848 raise Exception('This path %s is a valid repository' % repo_path)
847
849
848 # check if this path is a group
850 # check if this path is a group
849 if is_valid_repo_group(repo_path, self.repos_path):
851 if is_valid_repo_group(repo_path, self.repos_path):
850 raise Exception('This path %s is a valid group' % repo_path)
852 raise Exception('This path %s is a valid group' % repo_path)
851
853
852 log.info('creating repo %s in %s from url: `%s`',
854 log.info('creating repo %s in %s from url: `%s`',
853 repo_name, safe_unicode(repo_path),
855 repo_name, safe_unicode(repo_path),
854 obfuscate_url_pw(clone_uri))
856 obfuscate_url_pw(clone_uri))
855
857
856 backend = get_backend(repo_type)
858 backend = get_backend(repo_type)
857
859
858 config_repo = None if use_global_config else repo_name
860 config_repo = None if use_global_config else repo_name
859 if config_repo and new_parent_path:
861 if config_repo and new_parent_path:
860 config_repo = Repository.NAME_SEP.join(
862 config_repo = Repository.NAME_SEP.join(
861 (new_parent_path, config_repo))
863 (new_parent_path, config_repo))
862 config = make_db_config(clear_session=False, repo=config_repo)
864 config = make_db_config(clear_session=False, repo=config_repo)
863 config.set('extensions', 'largefiles', '')
865 config.set('extensions', 'largefiles', '')
864
866
865 # patch and reset hooks section of UI config to not run any
867 # patch and reset hooks section of UI config to not run any
866 # hooks on creating remote repo
868 # hooks on creating remote repo
867 config.clear_section('hooks')
869 config.clear_section('hooks')
868
870
869 # TODO: johbo: Unify this, hardcoded "bare=True" does not look nice
871 # TODO: johbo: Unify this, hardcoded "bare=True" does not look nice
870 if repo_type == 'git':
872 if repo_type == 'git':
871 repo = backend(
873 repo = backend(
872 repo_path, config=config, create=True, src_url=clone_uri,
874 repo_path, config=config, create=True, src_url=clone_uri,
873 bare=True)
875 bare=True)
874 else:
876 else:
875 repo = backend(
877 repo = backend(
876 repo_path, config=config, create=True, src_url=clone_uri)
878 repo_path, config=config, create=True, src_url=clone_uri)
877
879
878 ScmModel().install_hooks(repo, repo_type=repo_type)
880 ScmModel().install_hooks(repo, repo_type=repo_type)
879
881
880 log.debug('Created repo %s with %s backend',
882 log.debug('Created repo %s with %s backend',
881 safe_unicode(repo_name), safe_unicode(repo_type))
883 safe_unicode(repo_name), safe_unicode(repo_type))
882 return repo
884 return repo
883
885
884 def _rename_filesystem_repo(self, old, new):
886 def _rename_filesystem_repo(self, old, new):
885 """
887 """
886 renames repository on filesystem
888 renames repository on filesystem
887
889
888 :param old: old name
890 :param old: old name
889 :param new: new name
891 :param new: new name
890 """
892 """
891 log.info('renaming repo from %s to %s', old, new)
893 log.info('renaming repo from %s to %s', old, new)
892
894
893 old_path = os.path.join(self.repos_path, old)
895 old_path = os.path.join(self.repos_path, old)
894 new_path = os.path.join(self.repos_path, new)
896 new_path = os.path.join(self.repos_path, new)
895 if os.path.isdir(new_path):
897 if os.path.isdir(new_path):
896 raise Exception(
898 raise Exception(
897 'Was trying to rename to already existing dir %s' % new_path
899 'Was trying to rename to already existing dir %s' % new_path
898 )
900 )
899 shutil.move(old_path, new_path)
901 shutil.move(old_path, new_path)
900
902
901 def _delete_filesystem_repo(self, repo):
903 def _delete_filesystem_repo(self, repo):
902 """
904 """
903 removes repo from filesystem, the removal is acctually made by
905 removes repo from filesystem, the removal is acctually made by
904 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
906 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
905 repository is no longer valid for rhodecode, can be undeleted later on
907 repository is no longer valid for rhodecode, can be undeleted later on
906 by reverting the renames on this repository
908 by reverting the renames on this repository
907
909
908 :param repo: repo object
910 :param repo: repo object
909 """
911 """
910 rm_path = os.path.join(self.repos_path, repo.repo_name)
912 rm_path = os.path.join(self.repos_path, repo.repo_name)
911 repo_group = repo.group
913 repo_group = repo.group
912 log.info("Removing repository %s", rm_path)
914 log.info("Removing repository %s", rm_path)
913 # disable hg/git internal that it doesn't get detected as repo
915 # disable hg/git internal that it doesn't get detected as repo
914 alias = repo.repo_type
916 alias = repo.repo_type
915
917
916 config = make_db_config(clear_session=False)
918 config = make_db_config(clear_session=False)
917 config.set('extensions', 'largefiles', '')
919 config.set('extensions', 'largefiles', '')
918 bare = getattr(repo.scm_instance(config=config), 'bare', False)
920 bare = getattr(repo.scm_instance(config=config), 'bare', False)
919
921
920 # skip this for bare git repos
922 # skip this for bare git repos
921 if not bare:
923 if not bare:
922 # disable VCS repo
924 # disable VCS repo
923 vcs_path = os.path.join(rm_path, '.%s' % alias)
925 vcs_path = os.path.join(rm_path, '.%s' % alias)
924 if os.path.exists(vcs_path):
926 if os.path.exists(vcs_path):
925 shutil.move(vcs_path, os.path.join(rm_path, 'rm__.%s' % alias))
927 shutil.move(vcs_path, os.path.join(rm_path, 'rm__.%s' % alias))
926
928
927 _now = datetime.now()
929 _now = datetime.now()
928 _ms = str(_now.microsecond).rjust(6, '0')
930 _ms = str(_now.microsecond).rjust(6, '0')
929 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
931 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
930 repo.just_name)
932 repo.just_name)
931 if repo_group:
933 if repo_group:
932 # if repository is in group, prefix the removal path with the group
934 # if repository is in group, prefix the removal path with the group
933 args = repo_group.full_path_splitted + [_d]
935 args = repo_group.full_path_splitted + [_d]
934 _d = os.path.join(*args)
936 _d = os.path.join(*args)
935
937
936 if os.path.isdir(rm_path):
938 if os.path.isdir(rm_path):
937 shutil.move(rm_path, os.path.join(self.repos_path, _d))
939 shutil.move(rm_path, os.path.join(self.repos_path, _d))
938
940
939
941
940 class ReadmeFinder:
942 class ReadmeFinder:
941 """
943 """
942 Utility which knows how to find a readme for a specific commit.
944 Utility which knows how to find a readme for a specific commit.
943
945
944 The main idea is that this is a configurable algorithm. When creating an
946 The main idea is that this is a configurable algorithm. When creating an
945 instance you can define parameters, currently only the `default_renderer`.
947 instance you can define parameters, currently only the `default_renderer`.
946 Based on this configuration the method :meth:`search` behaves slightly
948 Based on this configuration the method :meth:`search` behaves slightly
947 different.
949 different.
948 """
950 """
949
951
950 readme_re = re.compile(r'^readme(\.[^\.]+)?$', re.IGNORECASE)
952 readme_re = re.compile(r'^readme(\.[^\.]+)?$', re.IGNORECASE)
951 path_re = re.compile(r'^docs?', re.IGNORECASE)
953 path_re = re.compile(r'^docs?', re.IGNORECASE)
952
954
953 default_priorities = {
955 default_priorities = {
954 None: 0,
956 None: 0,
955 '.text': 2,
957 '.text': 2,
956 '.txt': 3,
958 '.txt': 3,
957 '.rst': 1,
959 '.rst': 1,
958 '.rest': 2,
960 '.rest': 2,
959 '.md': 1,
961 '.md': 1,
960 '.mkdn': 2,
962 '.mkdn': 2,
961 '.mdown': 3,
963 '.mdown': 3,
962 '.markdown': 4,
964 '.markdown': 4,
963 }
965 }
964
966
965 path_priority = {
967 path_priority = {
966 'doc': 0,
968 'doc': 0,
967 'docs': 1,
969 'docs': 1,
968 }
970 }
969
971
970 FALLBACK_PRIORITY = 99
972 FALLBACK_PRIORITY = 99
971
973
972 RENDERER_TO_EXTENSION = {
974 RENDERER_TO_EXTENSION = {
973 'rst': ['.rst', '.rest'],
975 'rst': ['.rst', '.rest'],
974 'markdown': ['.md', 'mkdn', '.mdown', '.markdown'],
976 'markdown': ['.md', 'mkdn', '.mdown', '.markdown'],
975 }
977 }
976
978
977 def __init__(self, default_renderer=None):
979 def __init__(self, default_renderer=None):
978 self._default_renderer = default_renderer
980 self._default_renderer = default_renderer
979 self._renderer_extensions = self.RENDERER_TO_EXTENSION.get(
981 self._renderer_extensions = self.RENDERER_TO_EXTENSION.get(
980 default_renderer, [])
982 default_renderer, [])
981
983
982 def search(self, commit, path='/'):
984 def search(self, commit, path='/'):
983 """
985 """
984 Find a readme in the given `commit`.
986 Find a readme in the given `commit`.
985 """
987 """
986 nodes = commit.get_nodes(path)
988 nodes = commit.get_nodes(path)
987 matches = self._match_readmes(nodes)
989 matches = self._match_readmes(nodes)
988 matches = self._sort_according_to_priority(matches)
990 matches = self._sort_according_to_priority(matches)
989 if matches:
991 if matches:
990 return matches[0].node
992 return matches[0].node
991
993
992 paths = self._match_paths(nodes)
994 paths = self._match_paths(nodes)
993 paths = self._sort_paths_according_to_priority(paths)
995 paths = self._sort_paths_according_to_priority(paths)
994 for path in paths:
996 for path in paths:
995 match = self.search(commit, path=path)
997 match = self.search(commit, path=path)
996 if match:
998 if match:
997 return match
999 return match
998
1000
999 return None
1001 return None
1000
1002
1001 def _match_readmes(self, nodes):
1003 def _match_readmes(self, nodes):
1002 for node in nodes:
1004 for node in nodes:
1003 if not node.is_file():
1005 if not node.is_file():
1004 continue
1006 continue
1005 path = node.path.rsplit('/', 1)[-1]
1007 path = node.path.rsplit('/', 1)[-1]
1006 match = self.readme_re.match(path)
1008 match = self.readme_re.match(path)
1007 if match:
1009 if match:
1008 extension = match.group(1)
1010 extension = match.group(1)
1009 yield ReadmeMatch(node, match, self._priority(extension))
1011 yield ReadmeMatch(node, match, self._priority(extension))
1010
1012
1011 def _match_paths(self, nodes):
1013 def _match_paths(self, nodes):
1012 for node in nodes:
1014 for node in nodes:
1013 if not node.is_dir():
1015 if not node.is_dir():
1014 continue
1016 continue
1015 match = self.path_re.match(node.path)
1017 match = self.path_re.match(node.path)
1016 if match:
1018 if match:
1017 yield node.path
1019 yield node.path
1018
1020
1019 def _priority(self, extension):
1021 def _priority(self, extension):
1020 renderer_priority = (
1022 renderer_priority = (
1021 0 if extension in self._renderer_extensions else 1)
1023 0 if extension in self._renderer_extensions else 1)
1022 extension_priority = self.default_priorities.get(
1024 extension_priority = self.default_priorities.get(
1023 extension, self.FALLBACK_PRIORITY)
1025 extension, self.FALLBACK_PRIORITY)
1024 return (renderer_priority, extension_priority)
1026 return (renderer_priority, extension_priority)
1025
1027
1026 def _sort_according_to_priority(self, matches):
1028 def _sort_according_to_priority(self, matches):
1027
1029
1028 def priority_and_path(match):
1030 def priority_and_path(match):
1029 return (match.priority, match.path)
1031 return (match.priority, match.path)
1030
1032
1031 return sorted(matches, key=priority_and_path)
1033 return sorted(matches, key=priority_and_path)
1032
1034
1033 def _sort_paths_according_to_priority(self, paths):
1035 def _sort_paths_according_to_priority(self, paths):
1034
1036
1035 def priority_and_path(path):
1037 def priority_and_path(path):
1036 return (self.path_priority.get(path, self.FALLBACK_PRIORITY), path)
1038 return (self.path_priority.get(path, self.FALLBACK_PRIORITY), path)
1037
1039
1038 return sorted(paths, key=priority_and_path)
1040 return sorted(paths, key=priority_and_path)
1039
1041
1040
1042
1041 class ReadmeMatch:
1043 class ReadmeMatch:
1042
1044
1043 def __init__(self, node, match, priority):
1045 def __init__(self, node, match, priority):
1044 self.node = node
1046 self.node = node
1045 self._match = match
1047 self._match = match
1046 self.priority = priority
1048 self.priority = priority
1047
1049
1048 @property
1050 @property
1049 def path(self):
1051 def path(self):
1050 return self.node.path
1052 return self.node.path
1051
1053
1052 def __repr__(self):
1054 def __repr__(self):
1053 return '<ReadmeMatch {} priority={}'.format(self.path, self.priority)
1055 return '<ReadmeMatch {} priority={}'.format(self.path, self.priority)
General Comments 0
You need to be logged in to leave comments. Login now