/// <summary> /// Build a merged list of safes. /// </summary> /// <param name="leftItems">List of safes from the left repository, whose order has precedence.</param> /// <param name="rightItems">List of safes from the right side repository.</param> /// <returns>List of safes for the new merged repository.</returns> private static SafeListModel BuildMergedListOfSafes(SafeListModel leftItems, SafeListModel rightItems) { SafeListModel result = new SafeListModel(); var map = OuterJoin(leftItems, rightItems, item => item.Id); foreach (Tuple <SafeModel, SafeModel> pair in map) { if (pair.Item1 == null) { // Only available on the right side result.Add(pair.Item2.Clone()); } else if (pair.Item2 == null) { // Only available on the left side result.Add(pair.Item1.Clone()); } else { // Take the more recent SafeModel lastModifiedItem = ChooseLastModified( pair.Item1, pair.Item2, item => item.ModifiedAt, null); result.Add(lastModifiedItem.Clone()); } } return(result); }
/// <inheritdoc/> public ShortenedNoteViewModel( INavigationService navigationService, ILanguageService languageService, ISvgIconService svgIconService, IThemeService themeService, IBaseUrlService webviewBaseUrl, SearchableHtmlConverter searchableTextConverter, IRepositoryStorageService repositoryService, IFeedbackService feedbackService, ISettingsService settingsService, ICryptor cryptor, SafeListModel safes, NoteModel noteFromRepository) : base(navigationService, languageService, svgIconService, themeService, webviewBaseUrl, searchableTextConverter, repositoryService, feedbackService, settingsService, cryptor, safes, noteFromRepository) { if (_unlockedContent != null) { // Create a short version for large notes, with only the first part of the note. // This is a performance improvement if there are large notes in the repository. HtmlShortener shortener = new HtmlShortener(); shortener.WantedLength = 600; // Should be enough even for settings with shortener.WantedTagNumber = 20; // small font and very height notes. string shortenedContent = shortener.Shorten(_unlockedContent); if (shortenedContent.Length != _unlockedContent.Length) { // Because the note will be truncated, we have to build the searchable content // first, before overwriting the original content. string dummy = SearchableContent; _unlockedContent = shortenedContent; } } }
/// <summary> /// Initializes a new instance of the <see cref="NoteViewModel"/> class. /// </summary> public NoteViewModel( INavigationService navigationService, ILanguageService languageService, ISvgIconService svgIconService, IThemeService themeService, IBaseUrlService webviewBaseUrl, SearchableHtmlConverter searchableTextConverter, IRepositoryStorageService repositoryService, IFeedbackService feedbackService, ISettingsService settingsService, ICryptor cryptor, SafeListModel safes, NoteModel noteFromRepository) : base(navigationService, languageService, svgIconService, themeService, webviewBaseUrl) { _repositoryService = repositoryService; _feedbackService = feedbackService; _settingsService = settingsService; _searchableTextConverter = searchableTextConverter; _cryptor = cryptor; _safes = safes; MarkSearchableContentAsDirty(); PushNoteToOnlineStorageCommand = new RelayCommand(PushNoteToOnlineStorage); PullNoteFromOnlineStorageCommand = new RelayCommand(PullNoteFromOnlineStorage); ToggleShoppingModeCommand = new RelayCommand(ToggleShoppingMode); GoBackCommand = new RelayCommand(GoBack); Model = noteFromRepository; _unlockedContent = IsInSafe ? UnlockIfSafeOpen(Model.HtmlContent) : Model.HtmlContent; }
public void DoesNotShowContentWhenSafeIsClosed() { SearchableHtmlConverter searchableConverter = new SearchableHtmlConverter(); Mock <IRepositoryStorageService> repositoryService = new Mock <IRepositoryStorageService>(); Mock <ISettingsService> settingsService = new Mock <ISettingsService>(); Mock <ICryptor> cryptor = new Mock <ICryptor>(); SafeListModel safes = new SafeListModel(new[] { new SafeModel { Id = Guid.NewGuid() }, }); NoteModel note = new NoteModel { Id = Guid.NewGuid(), HtmlContent = "secret", SafeId = safes[0].Id }; NoteViewModel noteViewModel = new NoteViewModel( new Mock <INavigationService>().Object, new Mock <ILanguageService>().Object, new Mock <ISvgIconService>().Object, new Mock <IThemeService>().Object, new Mock <IBaseUrlService>().Object, searchableConverter, repositoryService.Object, new Mock <IFeedbackService>().Object, settingsService.Object, cryptor.Object, safes, new List <string>(), note); Assert.IsNull(noteViewModel.UnlockedHtmlContent); Assert.IsNull(noteViewModel.SearchableContent); }
public void MergeSafes() { Guid safe1Id = new Guid("10000000000000000000000000000000"); Guid safe2Id = new Guid("20000000000000000000000000000000"); Guid safe3Id = new Guid("30000000000000000000000000000000"); Guid safe4Id = new Guid("40000000000000000000000000000000"); Guid safe5Id = new Guid("50000000000000000000000000000000"); Guid safe6Id = new Guid("60000000000000000000000000000000"); DateTime newerDate = new DateTime(2008, 08, 08); DateTime middleDate = new DateTime(2006, 06, 06); NoteRepositoryModel serverRepo = new NoteRepositoryModel(); SafeModel safeS1 = new SafeModel { Id = safe2Id, SerializeableKey = "s1", ModifiedAt = newerDate }; SafeModel safeS2 = new SafeModel { Id = safe4Id, SerializeableKey = "s2", ModifiedAt = middleDate }; SafeModel safeS3 = new SafeModel { Id = safe6Id, SerializeableKey = "s3", ModifiedAt = middleDate }; serverRepo.Safes.AddRange(new[] { safeS1, safeS2, safeS3 }); AddNotesWithSafeIds(serverRepo, new[] { safe2Id, safe4Id, safe6Id }); NoteRepositoryModel clientRepo = new NoteRepositoryModel(); SafeModel safeC1 = new SafeModel { Id = safe5Id, SerializeableKey = "c1", ModifiedAt = middleDate }; SafeModel safeC2 = new SafeModel { Id = safe4Id, SerializeableKey = "c2", ModifiedAt = newerDate }; SafeModel safeC3 = new SafeModel { Id = safe2Id, SerializeableKey = "c3", ModifiedAt = middleDate }; SafeModel safeC4 = new SafeModel { Id = safe1Id, SerializeableKey = "c4", ModifiedAt = middleDate }; SafeModel safeC5 = new SafeModel { Id = safe3Id, SerializeableKey = "c5", ModifiedAt = middleDate }; clientRepo.Safes.AddRange(new[] { safeC1, safeC2, safeC3, safeC4, safeC5 }); AddNotesWithSafeIds(clientRepo, new[] { safe5Id, safe4Id, safe2Id, safe1Id, safe3Id }); NoteRepositoryMerger merger = new NoteRepositoryMerger(); NoteRepositoryModel result = merger.Merge(clientRepo, serverRepo); SafeListModel safes = result.Safes; Assert.AreEqual(6, safes.Count); Assert.AreEqual(safe5Id, safes[0].Id); Assert.AreEqual("c1", safes[0].SerializeableKey); Assert.AreEqual(safe2Id, safes[1].Id); Assert.AreEqual("s1", safes[1].SerializeableKey); Assert.AreEqual(safe1Id, safes[2].Id); Assert.AreEqual("c4", safes[2].SerializeableKey); Assert.AreEqual(safe3Id, safes[3].Id); Assert.AreEqual("c5", safes[3].SerializeableKey); Assert.AreEqual(safe4Id, safes[4].Id); Assert.AreEqual("c2", safes[4].SerializeableKey); Assert.AreEqual(safe6Id, safes[5].Id); Assert.AreEqual("s3", safes[5].SerializeableKey); }
public void ModifiedNoteIsEncryptedAndStored() { string modelHtmlContent = "c2VjcmV0"; byte[] safeKey = new byte[] { 88 }; SettingsModel settingsModel = new SettingsModel(); SearchableHtmlConverter searchableConverter = new SearchableHtmlConverter(); Mock <IRepositoryStorageService> repositoryService = new Mock <IRepositoryStorageService>(); Mock <ISettingsService> settingsService = new Mock <ISettingsService>(); settingsService. Setup(m => m.LoadSettingsOrDefault()).Returns(settingsModel); Mock <ICryptor> cryptor = new Mock <ICryptor>(); cryptor. Setup(m => m.Decrypt(It.IsAny <byte[]>(), It.Is <byte[]>(v => v == safeKey))). Returns(CryptoUtils.StringToBytes("not secret anymore")); SafeListModel safes = new SafeListModel(new[] { new SafeModel { Id = Guid.NewGuid(), Key = safeKey }, }); NoteModel note = new NoteModel { Id = Guid.NewGuid(), HtmlContent = modelHtmlContent, SafeId = safes[0].Id }; NoteViewModel noteViewModel = new NoteViewModel( new Mock <INavigationService>().Object, new Mock <ILanguageService>().Object, new Mock <ISvgIconService>().Object, new Mock <IThemeService>().Object, new Mock <IBaseUrlService>().Object, searchableConverter, repositoryService.Object, new Mock <IFeedbackService>().Object, settingsService.Object, cryptor.Object, safes, new List <string>(), note); noteViewModel.UnlockedHtmlContent = "something new"; noteViewModel.OnStoringUnsavedData(); cryptor.Verify(m => m.Encrypt( It.Is <byte[]>(p => p.SequenceEqual(CryptoUtils.StringToBytes("something new"))), It.Is <byte[]>(p => p.SequenceEqual(safeKey)), It.IsAny <string>(), It.Is <string>(p => p == null)), Times.Once); repositoryService.Verify(m => m.TrySaveRepository(It.IsAny <NoteRepositoryModel>()), Times.Once); }
public void ShowsUnlockedContentWhenSafeIsOpen() { string modelHtmlContent = "c2VjcmV0"; byte[] safeKey = new byte[] { 88 }; SearchableHtmlConverter searchableConverter = new SearchableHtmlConverter(); Mock <IRepositoryStorageService> repositoryService = new Mock <IRepositoryStorageService>(); Mock <ISettingsService> settingsService = new Mock <ISettingsService>(); Mock <ICryptor> cryptor = new Mock <ICryptor>(); cryptor. Setup(m => m.Decrypt(It.IsAny <byte[]>(), It.Is <byte[]>(v => v == safeKey))). Returns(CryptoUtils.StringToBytes("not secret anymore")); SafeListModel safes = new SafeListModel(new[] { new SafeModel { Id = Guid.NewGuid(), Key = safeKey }, }); NoteModel note = new NoteModel { Id = Guid.NewGuid(), HtmlContent = modelHtmlContent, SafeId = safes[0].Id }; NoteViewModel noteViewModel = new NoteViewModel( new Mock <INavigationService>().Object, new Mock <ILanguageService>().Object, new Mock <ISvgIconService>().Object, new Mock <IThemeService>().Object, new Mock <IBaseUrlService>().Object, searchableConverter, repositoryService.Object, new Mock <IFeedbackService>().Object, settingsService.Object, cryptor.Object, safes, new List <string>(), note); Assert.AreEqual("not secret anymore", noteViewModel.UnlockedHtmlContent); Assert.AreEqual("not secret anymore", noteViewModel.SearchableContent); }
public void FindOldestOpenSafeWorksCorrectly() { SafeListModel list = new SafeListModel(); byte[] testKey = new byte[0]; // Search in empty list Assert.IsNull(list.FindOldestOpenSafe()); // Search with only one element SafeModel safe2002 = new SafeModel { CreatedAt = new DateTime(2002, 02, 02), Key = testKey }; list.Add(safe2002); Assert.AreSame(safe2002, list.FindOldestOpenSafe()); // Add newer element SafeModel safe2003 = new SafeModel { CreatedAt = new DateTime(2003, 03, 03), Key = testKey }; list.Add(safe2003); Assert.AreSame(safe2002, list.FindOldestOpenSafe()); // Add closed safe SafeModel safe2000 = new SafeModel { CreatedAt = new DateTime(2000, 01, 01), Key = null }; list.Add(safe2000); Assert.AreSame(safe2002, list.FindOldestOpenSafe()); // Add older element SafeModel safe2001 = new SafeModel { CreatedAt = new DateTime(2001, 01, 01), Key = testKey }; list.Add(safe2001); Assert.AreSame(safe2001, list.FindOldestOpenSafe()); }