|
27 | 27 |
|
28 | 28 | from . import base
|
29 | 29 | from .base import dump_report_data, field_to_title, fields_to_titles, raise_parse_exception, suppress_exit, Command, \
|
30 |
| - GroupCommand, FolderMixin |
| 30 | + GroupCommand, FolderMixin, user_choice |
31 | 31 | from .helpers.record import get_record_uids
|
32 | 32 | from .helpers.timeout import parse_timeout
|
33 | 33 | from .record import RecordRemoveCommand
|
|
42 | 42 | from ..sox.sox_types import Record
|
43 | 43 | from ..subfolder import BaseFolderNode, SharedFolderNode, SharedFolderFolderNode, try_resolve_path, get_folder_path, \
|
44 | 44 | get_folder_uids, get_contained_record_uids
|
| 45 | +from ..utils import is_email |
45 | 46 |
|
46 | 47 |
|
47 | 48 | def register_commands(commands):
|
@@ -71,6 +72,9 @@ def register_command_info(aliases, command_info):
|
71 | 72 |
|
72 | 73 | share_record_parser = argparse.ArgumentParser(prog='share-record', description='Change the sharing permissions of an individual record')
|
73 | 74 | share_record_parser.add_argument('-e', '--email', dest='email', action='append', required=True, help='account email')
|
| 75 | +share_record_parser.add_argument('--contacts-only', action='store_true', help="Share only to known targets; Allows routing to" |
| 76 | + " alternate domains with matching usernames if needed") |
| 77 | +share_record_parser.add_argument('-f', '--force', action='store_true', help='Skip confirmation prompts') |
74 | 78 | share_record_parser.add_argument('-a', '--action', dest='action', choices=['grant', 'revoke', 'owner', 'cancel'],
|
75 | 79 | default='grant', action='store', help='user share action. \'grant\' if omitted')
|
76 | 80 | share_record_parser.add_argument('-s', '--share', dest='can_share', action='store_true', help='can re-share record')
|
@@ -651,7 +655,31 @@ def execute(self, params, **kwargs):
|
651 | 655 | raise CommandError('share-record', '\'email\' parameter is missing')
|
652 | 656 |
|
653 | 657 | dry_run = kwargs.get('dry_run') is True
|
| 658 | + force = kwargs.get('force') is True |
654 | 659 | action = kwargs.get('action') or 'grant'
|
| 660 | + use_contacts = kwargs.get('contacts_only') |
| 661 | + |
| 662 | + def get_contact(user, contacts): |
| 663 | + get_username = lambda addr: next(iter(addr.split('@')), '') |
| 664 | + matches = [c for c in contacts if get_username(user) == get_username(c)] |
| 665 | + if len(matches) > 1: |
| 666 | + raise CommandError('More than 1 matching usernames found. Aborting') |
| 667 | + return next(iter(matches), None) |
| 668 | + |
| 669 | + if use_contacts: |
| 670 | + known_users = api.get_share_objects(params).get('users', {}) |
| 671 | + is_unknown = lambda e: e not in known_users and is_email(e) |
| 672 | + unknowns = [e for e in emails if is_unknown(e)] |
| 673 | + if unknowns: |
| 674 | + username_map = {e: get_contact(e, known_users) for e in unknowns} |
| 675 | + table = [[k, v] for k, v in username_map.items()] |
| 676 | + logging.info(f'{len(unknowns)} unrecognized share recipient(s) and closest matching contact(s)') |
| 677 | + dump_report_data(table, ['Username', 'From Contacts']) |
| 678 | + confirmed = force or user_choice('\tReplace with known matching contact(s)?', 'yn', default='n') == 'y' |
| 679 | + if confirmed: |
| 680 | + good_emails = [e for e in emails if e not in unknowns] |
| 681 | + replacements = [e for e in username_map.values() if e] |
| 682 | + emails = [*good_emails, *replacements] |
655 | 683 |
|
656 | 684 | if action == 'cancel':
|
657 | 685 | answer = base.user_choice(
|
|
0 commit comments