##// END OF EJS Templates
users: added option to detach pull requests for users which we delete....
dan -
r4351:2d86851b default
parent child Browse files
Show More
@@ -39,7 +39,8 b' from rhodecode.model.db import true, Use'
39 from rhodecode.lib import audit_logger, rc_cache
39 from rhodecode.lib import audit_logger, rc_cache
40 from rhodecode.lib.exceptions import (
40 from rhodecode.lib.exceptions import (
41 UserCreationError, UserOwnsReposException, UserOwnsRepoGroupsException,
41 UserCreationError, UserOwnsReposException, UserOwnsRepoGroupsException,
42 UserOwnsUserGroupsException, DefaultUserException)
42 UserOwnsUserGroupsException, UserOwnsPullRequestsException,
43 UserOwnsArtifactsException, DefaultUserException)
43 from rhodecode.lib.ext_json import json
44 from rhodecode.lib.ext_json import json
44 from rhodecode.lib.auth import (
45 from rhodecode.lib.auth import (
45 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
46 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
@@ -377,11 +378,13 b' class UsersView(UserAppView):'
377 _repos = c.user.repositories
378 _repos = c.user.repositories
378 _repo_groups = c.user.repository_groups
379 _repo_groups = c.user.repository_groups
379 _user_groups = c.user.user_groups
380 _user_groups = c.user.user_groups
381 _pull_requests = c.user.user_pull_requests
380 _artifacts = c.user.artifacts
382 _artifacts = c.user.artifacts
381
383
382 handle_repos = None
384 handle_repos = None
383 handle_repo_groups = None
385 handle_repo_groups = None
384 handle_user_groups = None
386 handle_user_groups = None
387 handle_pull_requests = None
385 handle_artifacts = None
388 handle_artifacts = None
386
389
387 # calls for flash of handle based on handle case detach or delete
390 # calls for flash of handle based on handle case detach or delete
@@ -412,6 +415,15 b' class UsersView(UserAppView):'
412 h.flash(_('Deleted %s user groups') % len(_user_groups),
415 h.flash(_('Deleted %s user groups') % len(_user_groups),
413 category='success')
416 category='success')
414
417
418 def set_handle_flash_pull_requests():
419 handle = handle_pull_requests
420 if handle == 'detach':
421 h.flash(_('Detached %s pull requests') % len(_pull_requests),
422 category='success')
423 elif handle == 'delete':
424 h.flash(_('Deleted %s pull requests') % len(_pull_requests),
425 category='success')
426
415 def set_handle_flash_artifacts():
427 def set_handle_flash_artifacts():
416 handle = handle_artifacts
428 handle = handle_artifacts
417 if handle == 'detach':
429 if handle == 'detach':
@@ -421,6 +433,12 b' class UsersView(UserAppView):'
421 h.flash(_('Deleted %s artifacts') % len(_artifacts),
433 h.flash(_('Deleted %s artifacts') % len(_artifacts),
422 category='success')
434 category='success')
423
435
436 handle_user = User.get_first_super_admin()
437 handle_user_id = safe_int(self.request.POST.get('detach_user_id'))
438 if handle_user_id:
439 # NOTE(marcink): we get new owner for objects...
440 handle_user = User.get_or_404(handle_user_id)
441
424 if _repos and self.request.POST.get('user_repos'):
442 if _repos and self.request.POST.get('user_repos'):
425 handle_repos = self.request.POST['user_repos']
443 handle_repos = self.request.POST['user_repos']
426
444
@@ -430,16 +448,25 b' class UsersView(UserAppView):'
430 if _user_groups and self.request.POST.get('user_user_groups'):
448 if _user_groups and self.request.POST.get('user_user_groups'):
431 handle_user_groups = self.request.POST['user_user_groups']
449 handle_user_groups = self.request.POST['user_user_groups']
432
450
451 if _pull_requests and self.request.POST.get('user_pull_requests'):
452 handle_pull_requests = self.request.POST['user_pull_requests']
453
433 if _artifacts and self.request.POST.get('user_artifacts'):
454 if _artifacts and self.request.POST.get('user_artifacts'):
434 handle_artifacts = self.request.POST['user_artifacts']
455 handle_artifacts = self.request.POST['user_artifacts']
435
456
436 old_values = c.user.get_api_data()
457 old_values = c.user.get_api_data()
437
458
438 try:
459 try:
439 UserModel().delete(c.user, handle_repos=handle_repos,
460
461 UserModel().delete(
462 c.user,
463 handle_repos=handle_repos,
440 handle_repo_groups=handle_repo_groups,
464 handle_repo_groups=handle_repo_groups,
441 handle_user_groups=handle_user_groups,
465 handle_user_groups=handle_user_groups,
442 handle_artifacts=handle_artifacts)
466 handle_pull_requests=handle_pull_requests,
467 handle_artifacts=handle_artifacts,
468 handle_new_owner=handle_user
469 )
443
470
444 audit_logger.store_web(
471 audit_logger.store_web(
445 'user.delete', action_data={'old_data': old_values},
472 'user.delete', action_data={'old_data': old_values},
@@ -449,11 +476,13 b' class UsersView(UserAppView):'
449 set_handle_flash_repos()
476 set_handle_flash_repos()
450 set_handle_flash_repo_groups()
477 set_handle_flash_repo_groups()
451 set_handle_flash_user_groups()
478 set_handle_flash_user_groups()
479 set_handle_flash_pull_requests()
452 set_handle_flash_artifacts()
480 set_handle_flash_artifacts()
453 username = h.escape(old_values['username'])
481 username = h.escape(old_values['username'])
454 h.flash(_('Successfully deleted user `{}`').format(username), category='success')
482 h.flash(_('Successfully deleted user `{}`').format(username), category='success')
455 except (UserOwnsReposException, UserOwnsRepoGroupsException,
483 except (UserOwnsReposException, UserOwnsRepoGroupsException,
456 UserOwnsUserGroupsException, DefaultUserException) as e:
484 UserOwnsUserGroupsException, UserOwnsPullRequestsException,
485 UserOwnsArtifactsException, DefaultUserException) as e:
457 h.flash(e, category='warning')
486 h.flash(e, category='warning')
458 except Exception:
487 except Exception:
459 log.exception("Exception during deletion of user")
488 log.exception("Exception during deletion of user")
@@ -502,6 +531,11 b' class UsersView(UserAppView):'
502 user_id = self.db_user_id
531 user_id = self.db_user_id
503 c.user = self.db_user
532 c.user = self.db_user
504
533
534 c.detach_user = User.get_first_super_admin()
535 detach_user_id = safe_int(self.request.GET.get('detach_user_id'))
536 if detach_user_id:
537 c.detach_user = User.get_or_404(detach_user_id)
538
505 c.active = 'advanced'
539 c.active = 'advanced'
506 c.personal_repo_group = RepoGroup.get_user_personal_repo_group(user_id)
540 c.personal_repo_group = RepoGroup.get_user_personal_repo_group(user_id)
507 c.personal_repo_group_name = RepoGroupModel()\
541 c.personal_repo_group_name = RepoGroupModel()\
@@ -511,7 +545,6 b' class UsersView(UserAppView):'
511 (x.user for x in c.user.user_review_rules),
545 (x.user for x in c.user.user_review_rules),
512 key=lambda u: u.username.lower())
546 key=lambda u: u.username.lower())
513
547
514 c.first_admin = User.get_first_super_admin()
515 defaults = c.user.get_dict()
548 defaults = c.user.get_dict()
516
549
517 # Interim workaround if the user participated on any pull requests as a
550 # Interim workaround if the user participated on any pull requests as a
@@ -58,6 +58,10 b' class UserOwnsUserGroupsException(Except'
58 pass
58 pass
59
59
60
60
61 class UserOwnsPullRequestsException(Exception):
62 pass
63
64
61 class UserOwnsArtifactsException(Exception):
65 class UserOwnsArtifactsException(Exception):
62 pass
66 pass
63
67
@@ -617,6 +617,7 b' class User(Base, BaseModel):'
617 user_gists = relationship('Gist', cascade='all')
617 user_gists = relationship('Gist', cascade='all')
618 # user pull requests
618 # user pull requests
619 user_pull_requests = relationship('PullRequest', cascade='all')
619 user_pull_requests = relationship('PullRequest', cascade='all')
620
620 # external identities
621 # external identities
621 external_identities = relationship(
622 external_identities = relationship(
622 'ExternalIdentity',
623 'ExternalIdentity',
@@ -43,7 +43,9 b' from rhodecode.lib.compat import Ordered'
43 from rhodecode.lib.hooks_daemon import prepare_callback_daemon
43 from rhodecode.lib.hooks_daemon import prepare_callback_daemon
44 from rhodecode.lib.markup_renderer import (
44 from rhodecode.lib.markup_renderer import (
45 DEFAULT_COMMENTS_RENDERER, RstTemplateRenderer)
45 DEFAULT_COMMENTS_RENDERER, RstTemplateRenderer)
46 from rhodecode.lib.utils2 import safe_unicode, safe_str, md5_safe, AttributeDict, safe_int
46 from rhodecode.lib.utils2 import (
47 safe_unicode, safe_str, md5_safe, AttributeDict, safe_int,
48 get_current_rhodecode_user)
47 from rhodecode.lib.vcs.backends.base import (
49 from rhodecode.lib.vcs.backends.base import (
48 Reference, MergeResponse, MergeFailureReason, UpdateFailureReason,
50 Reference, MergeResponse, MergeFailureReason, UpdateFailureReason,
49 TargetRefMissing, SourceRefMissing)
51 TargetRefMissing, SourceRefMissing)
@@ -1427,7 +1429,10 b' class PullRequestModel(BaseModel):'
1427 email_kwargs=email_kwargs,
1429 email_kwargs=email_kwargs,
1428 )
1430 )
1429
1431
1430 def delete(self, pull_request, user):
1432 def delete(self, pull_request, user=None):
1433 if not user:
1434 user = getattr(get_current_rhodecode_user(), 'username', None)
1435
1431 pull_request = self.__get_pull_request(pull_request)
1436 pull_request = self.__get_pull_request(pull_request)
1432 old_data = pull_request.get_api_data(with_merge_state=False)
1437 old_data = pull_request.get_api_data(with_merge_state=False)
1433 self._cleanup_merge_workspace(pull_request)
1438 self._cleanup_merge_workspace(pull_request)
@@ -37,17 +37,17 b' from rhodecode.lib.utils2 import ('
37 AttributeDict, str2bool)
37 AttributeDict, str2bool)
38 from rhodecode.lib.exceptions import (
38 from rhodecode.lib.exceptions import (
39 DefaultUserException, UserOwnsReposException, UserOwnsRepoGroupsException,
39 DefaultUserException, UserOwnsReposException, UserOwnsRepoGroupsException,
40 UserOwnsUserGroupsException, NotAllowedToCreateUserError, UserOwnsArtifactsException)
40 UserOwnsUserGroupsException, NotAllowedToCreateUserError,
41 UserOwnsPullRequestsException, UserOwnsArtifactsException)
41 from rhodecode.lib.caching_query import FromCache
42 from rhodecode.lib.caching_query import FromCache
42 from rhodecode.model import BaseModel
43 from rhodecode.model import BaseModel
43 from rhodecode.model.auth_token import AuthTokenModel
44 from rhodecode.model.db import (
44 from rhodecode.model.db import (
45 _hash_key, true, false, or_, joinedload, User, UserToPerm,
45 _hash_key, true, false, or_, joinedload, User, UserToPerm,
46 UserEmailMap, UserIpMap, UserLog)
46 UserEmailMap, UserIpMap, UserLog)
47 from rhodecode.model.meta import Session
47 from rhodecode.model.meta import Session
48 from rhodecode.model.auth_token import AuthTokenModel
48 from rhodecode.model.repo_group import RepoGroupModel
49 from rhodecode.model.repo_group import RepoGroupModel
49
50
50
51 log = logging.getLogger(__name__)
51 log = logging.getLogger(__name__)
52
52
53
53
@@ -261,7 +261,7 b' class UserModel(BaseModel):'
261 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
261 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
262
262
263 from rhodecode.lib.auth import (
263 from rhodecode.lib.auth import (
264 get_crypt_password, check_password, generate_auth_token)
264 get_crypt_password, check_password)
265 from rhodecode.lib.hooks_base import (
265 from rhodecode.lib.hooks_base import (
266 log_create_user, check_allowed_create_user)
266 log_create_user, check_allowed_create_user)
267
267
@@ -443,15 +443,16 b' class UserModel(BaseModel):'
443 log.error(traceback.format_exc())
443 log.error(traceback.format_exc())
444 raise
444 raise
445
445
446 def _handle_user_repos(self, username, repositories, handle_mode=None):
446 def _handle_user_repos(self, username, repositories, handle_user,
447 _superadmin = self.cls.get_first_super_admin()
447 handle_mode=None):
448
448 left_overs = True
449 left_overs = True
449
450
450 from rhodecode.model.repo import RepoModel
451 from rhodecode.model.repo import RepoModel
451
452
452 if handle_mode == 'detach':
453 if handle_mode == 'detach':
453 for obj in repositories:
454 for obj in repositories:
454 obj.user = _superadmin
455 obj.user = handle_user
455 # set description we know why we super admin now owns
456 # set description we know why we super admin now owns
456 # additional repositories that were orphaned !
457 # additional repositories that were orphaned !
457 obj.description += ' \n::detached repository from deleted user: %s' % (username,)
458 obj.description += ' \n::detached repository from deleted user: %s' % (username,)
@@ -465,16 +466,16 b' class UserModel(BaseModel):'
465 # if nothing is done we have left overs left
466 # if nothing is done we have left overs left
466 return left_overs
467 return left_overs
467
468
468 def _handle_user_repo_groups(self, username, repository_groups,
469 def _handle_user_repo_groups(self, username, repository_groups, handle_user,
469 handle_mode=None):
470 handle_mode=None):
470 _superadmin = self.cls.get_first_super_admin()
471
471 left_overs = True
472 left_overs = True
472
473
473 from rhodecode.model.repo_group import RepoGroupModel
474 from rhodecode.model.repo_group import RepoGroupModel
474
475
475 if handle_mode == 'detach':
476 if handle_mode == 'detach':
476 for r in repository_groups:
477 for r in repository_groups:
477 r.user = _superadmin
478 r.user = handle_user
478 # set description we know why we super admin now owns
479 # set description we know why we super admin now owns
479 # additional repositories that were orphaned !
480 # additional repositories that were orphaned !
480 r.group_description += ' \n::detached repository group from deleted user: %s' % (username,)
481 r.group_description += ' \n::detached repository group from deleted user: %s' % (username,)
@@ -489,8 +490,9 b' class UserModel(BaseModel):'
489 # if nothing is done we have left overs left
490 # if nothing is done we have left overs left
490 return left_overs
491 return left_overs
491
492
492 def _handle_user_user_groups(self, username, user_groups, handle_mode=None):
493 def _handle_user_user_groups(self, username, user_groups, handle_user,
493 _superadmin = self.cls.get_first_super_admin()
494 handle_mode=None):
495
494 left_overs = True
496 left_overs = True
495
497
496 from rhodecode.model.user_group import UserGroupModel
498 from rhodecode.model.user_group import UserGroupModel
@@ -499,8 +501,8 b' class UserModel(BaseModel):'
499 for r in user_groups:
501 for r in user_groups:
500 for user_user_group_to_perm in r.user_user_group_to_perm:
502 for user_user_group_to_perm in r.user_user_group_to_perm:
501 if user_user_group_to_perm.user.username == username:
503 if user_user_group_to_perm.user.username == username:
502 user_user_group_to_perm.user = _superadmin
504 user_user_group_to_perm.user = handle_user
503 r.user = _superadmin
505 r.user = handle_user
504 # set description we know why we super admin now owns
506 # set description we know why we super admin now owns
505 # additional repositories that were orphaned !
507 # additional repositories that were orphaned !
506 r.user_group_description += ' \n::detached user group from deleted user: %s' % (username,)
508 r.user_group_description += ' \n::detached user group from deleted user: %s' % (username,)
@@ -514,13 +516,37 b' class UserModel(BaseModel):'
514 # if nothing is done we have left overs left
516 # if nothing is done we have left overs left
515 return left_overs
517 return left_overs
516
518
517 def _handle_user_artifacts(self, username, artifacts, handle_mode=None):
519 def _handle_user_pull_requests(self, username, pull_requests, handle_user,
518 _superadmin = self.cls.get_first_super_admin()
520 handle_mode=None):
521 left_overs = True
522
523 from rhodecode.model.pull_request import PullRequestModel
524
525 if handle_mode == 'detach':
526 for pr in pull_requests:
527 pr.user_id = handle_user.user_id
528 # set description we know why we super admin now owns
529 # additional repositories that were orphaned !
530 pr.description += ' \n::detached pull requests from deleted user: %s' % (username,)
531 self.sa.add(pr)
532 left_overs = False
533 elif handle_mode == 'delete':
534 for pr in pull_requests:
535 PullRequestModel().delete(pr)
536
537 left_overs = False
538
539 # if nothing is done we have left overs left
540 return left_overs
541
542 def _handle_user_artifacts(self, username, artifacts, handle_user,
543 handle_mode=None):
544
519 left_overs = True
545 left_overs = True
520
546
521 if handle_mode == 'detach':
547 if handle_mode == 'detach':
522 for a in artifacts:
548 for a in artifacts:
523 a.upload_user = _superadmin
549 a.upload_user = handle_user
524 # set description we know why we super admin now owns
550 # set description we know why we super admin now owns
525 # additional artifacts that were orphaned !
551 # additional artifacts that were orphaned !
526 a.file_description += ' \n::detached artifact from deleted user: %s' % (username,)
552 a.file_description += ' \n::detached artifact from deleted user: %s' % (username,)
@@ -528,7 +554,8 b' class UserModel(BaseModel):'
528 left_overs = False
554 left_overs = False
529 elif handle_mode == 'delete':
555 elif handle_mode == 'delete':
530 from rhodecode.apps.file_store import utils as store_utils
556 from rhodecode.apps.file_store import utils as store_utils
531 storage = store_utils.get_file_storage(self.request.registry.settings)
557 request = get_current_request()
558 storage = store_utils.get_file_storage(request.registry.settings)
532 for a in artifacts:
559 for a in artifacts:
533 file_uid = a.file_uid
560 file_uid = a.file_uid
534 storage.delete(file_uid)
561 storage.delete(file_uid)
@@ -540,11 +567,13 b' class UserModel(BaseModel):'
540 return left_overs
567 return left_overs
541
568
542 def delete(self, user, cur_user=None, handle_repos=None,
569 def delete(self, user, cur_user=None, handle_repos=None,
543 handle_repo_groups=None, handle_user_groups=None, handle_artifacts=None):
570 handle_repo_groups=None, handle_user_groups=None,
571 handle_pull_requests=None, handle_artifacts=None, handle_new_owner=None):
544 from rhodecode.lib.hooks_base import log_delete_user
572 from rhodecode.lib.hooks_base import log_delete_user
545
573
546 if not cur_user:
574 if not cur_user:
547 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
575 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
576
548 user = self._get_user(user)
577 user = self._get_user(user)
549
578
550 try:
579 try:
@@ -552,9 +581,11 b' class UserModel(BaseModel):'
552 raise DefaultUserException(
581 raise DefaultUserException(
553 u"You can't remove this user since it's"
582 u"You can't remove this user since it's"
554 u" crucial for entire application")
583 u" crucial for entire application")
584 handle_user = handle_new_owner or self.cls.get_first_super_admin()
585 log.debug('New detached objects owner %s', handle_user)
555
586
556 left_overs = self._handle_user_repos(
587 left_overs = self._handle_user_repos(
557 user.username, user.repositories, handle_repos)
588 user.username, user.repositories, handle_user, handle_repos)
558 if left_overs and user.repositories:
589 if left_overs and user.repositories:
559 repos = [x.repo_name for x in user.repositories]
590 repos = [x.repo_name for x in user.repositories]
560 raise UserOwnsReposException(
591 raise UserOwnsReposException(
@@ -564,7 +595,7 b' class UserModel(BaseModel):'
564 'list_repos': ', '.join(repos)})
595 'list_repos': ', '.join(repos)})
565
596
566 left_overs = self._handle_user_repo_groups(
597 left_overs = self._handle_user_repo_groups(
567 user.username, user.repository_groups, handle_repo_groups)
598 user.username, user.repository_groups, handle_user, handle_repo_groups)
568 if left_overs and user.repository_groups:
599 if left_overs and user.repository_groups:
569 repo_groups = [x.group_name for x in user.repository_groups]
600 repo_groups = [x.group_name for x in user.repository_groups]
570 raise UserOwnsRepoGroupsException(
601 raise UserOwnsRepoGroupsException(
@@ -574,7 +605,7 b' class UserModel(BaseModel):'
574 'list_repo_groups': ', '.join(repo_groups)})
605 'list_repo_groups': ', '.join(repo_groups)})
575
606
576 left_overs = self._handle_user_user_groups(
607 left_overs = self._handle_user_user_groups(
577 user.username, user.user_groups, handle_user_groups)
608 user.username, user.user_groups, handle_user, handle_user_groups)
578 if left_overs and user.user_groups:
609 if left_overs and user.user_groups:
579 user_groups = [x.users_group_name for x in user.user_groups]
610 user_groups = [x.users_group_name for x in user.user_groups]
580 raise UserOwnsUserGroupsException(
611 raise UserOwnsUserGroupsException(
@@ -582,8 +613,17 b' class UserModel(BaseModel):'
582 u'removed. Switch owners or remove those user groups:%s'
613 u'removed. Switch owners or remove those user groups:%s'
583 % (user.username, len(user_groups), ', '.join(user_groups)))
614 % (user.username, len(user_groups), ', '.join(user_groups)))
584
615
616 left_overs = self._handle_user_pull_requests(
617 user.username, user.user_pull_requests, handle_user, handle_pull_requests)
618 if left_overs and user.user_pull_requests:
619 pull_requests = ['!{}'.format(x.pull_request_id) for x in user.user_pull_requests]
620 raise UserOwnsPullRequestsException(
621 u'user "%s" still owns %s pull requests and cannot be '
622 u'removed. Switch owners or remove those pull requests:%s'
623 % (user.username, len(pull_requests), ', '.join(pull_requests)))
624
585 left_overs = self._handle_user_artifacts(
625 left_overs = self._handle_user_artifacts(
586 user.username, user.artifacts, handle_artifacts)
626 user.username, user.artifacts, handle_user, handle_artifacts)
587 if left_overs and user.artifacts:
627 if left_overs and user.artifacts:
588 artifacts = [x.file_uid for x in user.artifacts]
628 artifacts = [x.file_uid for x in user.artifacts]
589 raise UserOwnsArtifactsException(
629 raise UserOwnsArtifactsException(
@@ -878,7 +918,7 b' class UserModel(BaseModel):'
878 end_ip = ipaddress.ip_address(safe_unicode(end_ip.strip()))
918 end_ip = ipaddress.ip_address(safe_unicode(end_ip.strip()))
879 parsed_ip_range = []
919 parsed_ip_range = []
880
920
881 for index in xrange(int(start_ip), int(end_ip) + 1):
921 for index in range(int(start_ip), int(end_ip) + 1):
882 new_ip = ipaddress.ip_address(index)
922 new_ip = ipaddress.ip_address(index)
883 parsed_ip_range.append(str(new_ip))
923 parsed_ip_range.append(str(new_ip))
884 ip_list.extend(parsed_ip_range)
924 ip_list.extend(parsed_ip_range)
@@ -149,6 +149,18 b''
149
149
150 <tr>
150 <tr>
151 <td>
151 <td>
152 ${_ungettext('This user owns %s pull request.', 'This user owns %s pull requests.', len(c.user.user_pull_requests)) % len(c.user.user_pull_requests)}
153 </td>
154 <td>
155 <input type="radio" id="user_pull_requests_1" name="user_pull_requests" value="detach" checked="checked" ${'disabled=1' if len(c.user.user_pull_requests) == 0 else ''}/> <label for="user_pull_requests_1">${_('Detach pull requests')}</label>
156 </td>
157 <td>
158 <input type="radio" id="user_pull_requests_2" name="user_pull_requests" value="delete" ${'disabled=1' if len(c.user.user_pull_requests) == 0 else ''}/> <label for="user_pull_requests_2">${_('Delete pull requests')}</label>
159 </td>
160 </tr>
161
162 <tr>
163 <td>
152 ${_ungettext('This user owns %s artifact.', 'This user owns %s artifacts.', len(c.user.artifacts)) % len(c.user.artifacts)}
164 ${_ungettext('This user owns %s artifact.', 'This user owns %s artifacts.', len(c.user.artifacts)) % len(c.user.artifacts)}
153 </td>
165 </td>
154 <td>
166 <td>
@@ -166,7 +178,8 b''
166 % endif
178 % endif
167
179
168 <span style="padding: 0 5px 0 0">${_('New owner for detached objects')}:</span>
180 <span style="padding: 0 5px 0 0">${_('New owner for detached objects')}:</span>
169 <div class="pull-right">${base.gravatar_with_user(c.first_admin.email, 16)}</div>
181 <div class="pull-right">${base.gravatar_with_user(c.detach_user.email, 16, tooltip=True)}</div>
182 <input type="hidden" name="detach_user_id" value="${c.detach_user.user_id}">
170 </div>
183 </div>
171 <div style="clear: both">
184 <div style="clear: both">
172
185
@@ -186,11 +199,11 b''
186 <div style="margin: 0 0 20px 0" class="fake-space"></div>
199 <div style="margin: 0 0 20px 0" class="fake-space"></div>
187
200
188 <div class="field">
201 <div class="field">
189 <button class="btn btn-small btn-danger" type="submit"
202 <input class="btn btn-small btn-danger" id="remove_user" name="remove_user"
190 onclick="return confirm('${_('Confirm to delete this user: %s') % c.user.username}');"
203 onclick="submitConfirm(event, this, _gettext('Confirm to delete this user'), _gettext('Confirm Delete'), '${c.user.username}')"
191 ${"disabled" if not c.can_delete_user else ""}>
204 ${("disabled=1" if not c.can_delete_user else "")}
192 ${_('Delete this user')}
205 type="submit" value="${_('Delete this user')}"
193 </button>
206 >
194 </div>
207 </div>
195
208
196 ${h.end_form()}
209 ${h.end_form()}
General Comments 0
You need to be logged in to leave comments. Login now