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