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