diff --git a/src/App/Pages/Vault/AddEditPage.xaml b/src/App/Pages/Vault/AddEditPage.xaml index b8c6c71a0..f1fc2f7c4 100644 --- a/src/App/Pages/Vault/AddEditPage.xaml +++ b/src/App/Pages/Vault/AddEditPage.xaml @@ -409,6 +409,9 @@ HorizontalOptions="End" /> + @@ -500,6 +503,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/App/Pages/Vault/AddEditPage.xaml.cs b/src/App/Pages/Vault/AddEditPage.xaml.cs index 703bcf147..c0a0fce03 100644 --- a/src/App/Pages/Vault/AddEditPage.xaml.cs +++ b/src/App/Pages/Vault/AddEditPage.xaml.cs @@ -1,4 +1,5 @@ using Bit.Core.Enums; +using System.Collections.Generic; using Xamarin.Forms; namespace Bit.App.Pages @@ -11,15 +12,14 @@ namespace Bit.App.Pages string cipherId = null, CipherType? type = null, string folderId = null, - string collectionId = null, - string organizationId = null) + string collectionId = null) { InitializeComponent(); _vm = BindingContext as AddEditPageViewModel; _vm.Page = this; _vm.CipherId = cipherId; _vm.FolderId = folderId; - _vm.OrganizationId = organizationId; + _vm.CollectionIds = collectionId != null ? new HashSet(new List { collectionId }) : null; _vm.Type = type; _vm.Init(); SetActivityIndicator(); @@ -29,6 +29,7 @@ namespace Bit.App.Pages _cardExpMonthPicker.ItemDisplayBinding = new Binding("Key"); _identityTitlePicker.ItemDisplayBinding = new Binding("Key"); _folderPicker.ItemDisplayBinding = new Binding("Key"); + _ownershipPicker.ItemDisplayBinding = new Binding("Key"); } protected override async void OnAppearing() @@ -67,5 +68,13 @@ namespace Bit.App.Pages { _vm.AddField(); } + + private void Attachments_Clicked(object sender, System.EventArgs e) + { + if(DoOnce()) + { + // await Navigation.PushModalAsync(); + } + } } } diff --git a/src/App/Pages/Vault/AddEditPageViewModel.cs b/src/App/Pages/Vault/AddEditPageViewModel.cs index 51a86a024..c10047d1a 100644 --- a/src/App/Pages/Vault/AddEditPageViewModel.cs +++ b/src/App/Pages/Vault/AddEditPageViewModel.cs @@ -17,6 +17,7 @@ namespace Bit.App.Pages private readonly IDeviceActionService _deviceActionService; private readonly ICipherService _cipherService; private readonly IFolderService _folderService; + private readonly ICollectionService _collectionService; private readonly IUserService _userService; private readonly IPlatformUtilsService _platformUtilsService; private readonly IAuditService _auditService; @@ -29,6 +30,9 @@ namespace Bit.App.Pages private int _cardExpMonthSelectedIndex; private int _identityTitleSelectedIndex; private int _folderSelectedIndex; + private int _ownershipSelectedIndex; + private bool _hasCollections; + private List _writeableCollections; private string[] _additionalCipherProperties = new string[] { nameof(IsLogin), @@ -67,6 +71,7 @@ namespace Bit.App.Pages _platformUtilsService = ServiceContainer.Resolve("platformUtilsService"); _auditService = ServiceContainer.Resolve("auditService"); _messagingService = ServiceContainer.Resolve("messagingService"); + _collectionService = ServiceContainer.Resolve("collectionService"); GeneratePasswordCommand = new Command(GeneratePassword); TogglePasswordCommand = new Command(TogglePassword); ToggleCardCodeCommand = new Command(ToggleCardCode); @@ -75,6 +80,7 @@ namespace Bit.App.Pages FieldOptionsCommand = new Command(FieldOptions); Uris = new ExtendedObservableCollection(); Fields = new ExtendedObservableCollection(); + Collections = new ExtendedObservableCollection(); TypeOptions = new List> { @@ -121,6 +127,7 @@ namespace Bit.App.Pages new KeyValuePair(AppResources.Dr, AppResources.Dr), }; FolderOptions = new List>(); + OwnershipOptions = new List>(); } public Command GeneratePasswordCommand { get; set; } @@ -133,21 +140,25 @@ namespace Bit.App.Pages public string OrganizationId { get; set; } public string FolderId { get; set; } public CipherType? Type { get; set; } - public List CollectionIds { get; set; } + public HashSet CollectionIds { get; set; } public List> TypeOptions { get; set; } public List> CardBrandOptions { get; set; } public List> CardExpMonthOptions { get; set; } public List> IdentityTitleOptions { get; set; } public List> FolderOptions { get; set; } + public List> OwnershipOptions { get; set; } public ExtendedObservableCollection Uris { get; set; } public ExtendedObservableCollection Fields { get; set; } + public ExtendedObservableCollection Collections { get; set; } public int TypeSelectedIndex { get => _typeSelectedIndex; set { - SetProperty(ref _typeSelectedIndex, value); - TypeChanged(); + if(SetProperty(ref _typeSelectedIndex, value)) + { + TypeChanged(); + } } } public int CardBrandSelectedIndex @@ -155,8 +166,10 @@ namespace Bit.App.Pages get => _cardBrandSelectedIndex; set { - SetProperty(ref _cardBrandSelectedIndex, value); - CardBrandChanged(); + if(SetProperty(ref _cardBrandSelectedIndex, value)) + { + CardBrandChanged(); + } } } public int CardExpMonthSelectedIndex @@ -164,8 +177,10 @@ namespace Bit.App.Pages get => _cardExpMonthSelectedIndex; set { - SetProperty(ref _cardExpMonthSelectedIndex, value); - CardExpMonthChanged(); + if(SetProperty(ref _cardExpMonthSelectedIndex, value)) + { + CardExpMonthChanged(); + } } } public int IdentityTitleSelectedIndex @@ -173,8 +188,10 @@ namespace Bit.App.Pages get => _identityTitleSelectedIndex; set { - SetProperty(ref _identityTitleSelectedIndex, value); - IdentityTitleChanged(); + if(SetProperty(ref _identityTitleSelectedIndex, value)) + { + IdentityTitleChanged(); + } } } public int FolderSelectedIndex @@ -182,8 +199,21 @@ namespace Bit.App.Pages get => _folderSelectedIndex; set { - SetProperty(ref _folderSelectedIndex, value); - FolderChanged(); + if(SetProperty(ref _folderSelectedIndex, value)) + { + FolderChanged(); + } + } + } + public int OwnershipSelectedIndex + { + get => _ownershipSelectedIndex; + set + { + if(SetProperty(ref _ownershipSelectedIndex, value)) + { + OrganizationChanged(); + } } } public CipherView Cipher @@ -209,6 +239,11 @@ namespace Bit.App.Pages nameof(ShowCardCodeIcon) }); } + public bool HasCollections + { + get => _hasCollections; + set => SetProperty(ref _hasCollections, value); + } public bool EditMode => !string.IsNullOrWhiteSpace(CipherId); public bool IsLogin => Cipher?.Type == CipherType.Login; public bool IsIdentity => Cipher?.Type == CipherType.Identity; @@ -230,7 +265,25 @@ namespace Bit.App.Pages public async Task LoadAsync() { - // TODO: load collections + var myEmail = await _userService.GetEmailAsync(); + OwnershipOptions.Add(new KeyValuePair(myEmail, null)); + var orgs = await _userService.GetAllOrganizationAsync(); + foreach(var org in orgs.OrderBy(o => o.Name)) + { + if(org.Enabled && org.Status == OrganizationUserStatusType.Confirmed) + { + OwnershipOptions.Add(new KeyValuePair(org.Name, org.Id)); + } + } + + var allCollections = await _collectionService.GetAllDecryptedAsync(); + _writeableCollections = allCollections.Where(c => !c.ReadOnly).ToList(); + if(CollectionIds?.Any() ?? false) + { + var colId = CollectionIds.First(); + var collection = _writeableCollections.FirstOrDefault(c => c.Id == colId); + OrganizationId = collection?.OrganizationId; + } var folders = await _folderService.GetAllDecryptedAsync(); FolderOptions = folders.Select(f => new KeyValuePair(f.Name, f.Id)).ToList(); @@ -240,21 +293,6 @@ namespace Bit.App.Pages { var cipher = await _cipherService.GetAsync(CipherId); Cipher = await cipher.DecryptAsync(); - - FolderSelectedIndex = string.IsNullOrWhiteSpace(Cipher.FolderId) ? FolderOptions.Count - 1 : - FolderOptions.FindIndex(k => k.Value == Cipher.FolderId); ; - if(Cipher.Card != null) - { - CardBrandSelectedIndex = string.IsNullOrWhiteSpace(Cipher.Card.Brand) ? 0 : - CardBrandOptions.FindIndex(k => k.Value == Cipher.Card.Brand); - CardExpMonthSelectedIndex = string.IsNullOrWhiteSpace(Cipher.Card.ExpMonth) ? 0 : - CardExpMonthOptions.FindIndex(k => k.Value == Cipher.Card.ExpMonth); - } - if(Cipher.Identity != null) - { - IdentityTitleSelectedIndex = string.IsNullOrWhiteSpace(Cipher.Identity.Title) ? 0 : - IdentityTitleOptions.FindIndex(k => k.Value == Cipher.Identity.Title); - } } else { @@ -270,15 +308,27 @@ namespace Bit.App.Pages }; Cipher.Login.Uris = new List { new LoginUriView() }; Cipher.SecureNote.Type = SecureNoteType.Generic; - TypeSelectedIndex = TypeOptions.FindIndex(k => k.Value == Cipher.Type); - CardBrandSelectedIndex = 0; - CardExpMonthSelectedIndex = 0; - IdentityTitleSelectedIndex = 0; - FolderSelectedIndex = FolderOptions.Count - 1; - // TODO: org/collection stuff } + FolderSelectedIndex = string.IsNullOrWhiteSpace(Cipher.FolderId) ? FolderOptions.Count - 1 : + FolderOptions.FindIndex(k => k.Value == Cipher.FolderId); + CardBrandSelectedIndex = string.IsNullOrWhiteSpace(Cipher?.Card.Brand) ? 0 : + CardBrandOptions.FindIndex(k => k.Value == Cipher.Card.Brand); + CardExpMonthSelectedIndex = string.IsNullOrWhiteSpace(Cipher?.Card.ExpMonth) ? 0 : + CardExpMonthOptions.FindIndex(k => k.Value == Cipher.Card.ExpMonth); + IdentityTitleSelectedIndex = string.IsNullOrWhiteSpace(Cipher?.Identity.Title) ? 0 : + IdentityTitleOptions.FindIndex(k => k.Value == Cipher.Identity.Title); + OwnershipSelectedIndex = string.IsNullOrWhiteSpace(Cipher.OrganizationId) ? 0 : + OwnershipOptions.FindIndex(k => k.Value == Cipher.OrganizationId); + + if(!EditMode && (CollectionIds?.Any() ?? false)) + { + foreach(var col in Collections) + { + col.Checked = CollectionIds.Contains(col.Collection.Id); + } + } if(Cipher.Login.Uris != null) { Uris.ResetWithRange(Cipher.Login.Uris); @@ -310,7 +360,8 @@ namespace Bit.App.Pages if(!EditMode && Cipher.OrganizationId != null) { - // TODO: filter cipher collection ids + Cipher.CollectionIds = Collections.Any() ? + new HashSet(Collections.Where(c => c.Checked).Select(c => c.Collection.Id)) : null; } var cipher = await _cipherService.EncryptAsync(Cipher); @@ -514,6 +565,26 @@ namespace Bit.App.Pages } } + private void OrganizationChanged() + { + if(Cipher != null && OwnershipSelectedIndex > -1) + { + Cipher.OrganizationId = OwnershipOptions[OwnershipSelectedIndex].Value; + TriggerCipherChanged(); + } + if(Cipher.OrganizationId != null) + { + var cols = _writeableCollections.Where(c => c.OrganizationId == Cipher.OrganizationId) + .Select(c => new AddEditPageCollectionViewModel { Collection = c }).ToList(); + Collections.ResetWithRange(cols); + } + else + { + Collections.ResetWithRange(new List()); + } + HasCollections = Collections.Any(); + } + private void TriggerCipherChanged() { TriggerPropertyChanged(nameof(Cipher), _additionalCipherProperties); @@ -544,7 +615,19 @@ namespace Bit.App.Pages } } - public class AddEditPageFieldViewModel : BaseViewModel + public class AddEditPageCollectionViewModel : ExtendedViewModel + { + private bool _checked; + + public Core.Models.View.CollectionView Collection { get; set; } + public bool Checked + { + get => _checked; + set => SetProperty(ref _checked, value); + } + } + + public class AddEditPageFieldViewModel : ExtendedViewModel { private FieldView _field; private bool _showHiddenValue; diff --git a/src/App/Pages/Vault/ViewPageViewModel.cs b/src/App/Pages/Vault/ViewPageViewModel.cs index 31afbf75a..f9a1c469c 100644 --- a/src/App/Pages/Vault/ViewPageViewModel.cs +++ b/src/App/Pages/Vault/ViewPageViewModel.cs @@ -425,7 +425,7 @@ namespace Bit.App.Pages } } - public class ViewPageFieldViewModel : BaseViewModel + public class ViewPageFieldViewModel : ExtendedViewModel { private FieldView _field; private bool _showHiddenValue; diff --git a/src/App/Resources/AppResources.Designer.cs b/src/App/Resources/AppResources.Designer.cs index 793cc35d8..d76366112 100644 --- a/src/App/Resources/AppResources.Designer.cs +++ b/src/App/Resources/AppResources.Designer.cs @@ -2436,6 +2436,15 @@ namespace Bit.App.Resources { } } + /// + /// Looks up a localized string similar to There are no collections to list.. + /// + public static string NoCollectionsToList { + get { + return ResourceManager.GetString("NoCollectionsToList", resourceCulture); + } + } + /// /// Looks up a localized string similar to There are no favorites in your vault.. /// @@ -2625,6 +2634,15 @@ namespace Bit.App.Resources { } } + /// + /// Looks up a localized string similar to Ownership. + /// + public static string Ownership { + get { + return ResourceManager.GetString("Ownership", resourceCulture); + } + } + /// /// Looks up a localized string similar to Passport Number. /// @@ -3651,6 +3669,15 @@ namespace Bit.App.Resources { } } + /// + /// Looks up a localized string similar to Who owns this item?. + /// + public static string WhoOwnsThisItem { + get { + return ResourceManager.GetString("WhoOwnsThisItem", resourceCulture); + } + } + /// /// Looks up a localized string similar to Windows Hello. /// diff --git a/src/App/Resources/AppResources.resx b/src/App/Resources/AppResources.resx index 85a6dad6f..997332209 100644 --- a/src/App/Resources/AppResources.resx +++ b/src/App/Resources/AppResources.resx @@ -1420,4 +1420,13 @@ Miscellaneous + + Ownership + + + Who owns this item? + + + There are no collections to list. + \ No newline at end of file