Index: Application/Ringtoets/src/Application.Ringtoets.Storage/StorageSqLite.cs =================================================================== diff -u -ref1c61d94f2aec3b4ff32fcf03253d7ad386c8e5 -r276383b754de8eafea064dd780e118d922379d76 --- Application/Ringtoets/src/Application.Ringtoets.Storage/StorageSqLite.cs (.../StorageSqLite.cs) (revision ef1c61d94f2aec3b4ff32fcf03253d7ad386c8e5) +++ Application/Ringtoets/src/Application.Ringtoets.Storage/StorageSqLite.cs (.../StorageSqLite.cs) (revision 276383b754de8eafea064dd780e118d922379d76) @@ -25,17 +25,14 @@ using System.Data.Entity; using System.IO; using System.Linq; - using Application.Ringtoets.Storage.DbContext; using Application.Ringtoets.Storage.Exceptions; using Application.Ringtoets.Storage.Persistors; using Application.Ringtoets.Storage.Properties; - using Core.Common.Base.Data; using Core.Common.Base.Storage; using Core.Common.Utils; using Core.Common.Utils.Builders; - using UtilsResources = Core.Common.Utils.Properties.Resources; namespace Application.Ringtoets.Storage @@ -198,6 +195,31 @@ } } + public bool HasChanges(Project project) + { + if (string.IsNullOrWhiteSpace(connectionString)) + { + return true; + } + + using (var dbContext = new RingtoetsEntities(connectionString)) + { + var projectEntityPersistor = new ProjectEntityPersistor(dbContext); + + try + { + ICollection projectEntities = dbContext.ProjectEntities.ToList(); + + projectEntityPersistor.UpdateModel(project); + projectEntityPersistor.RemoveUnModifiedEntries(projectEntities); + return dbContext.ChangeTracker.HasChanges(); + } + catch (EntityNotFoundException) {} + catch (SystemException) {} + return false; + } + } + /// /// Throws a configured instance of when writing to the storage file failed. /// Index: Application/Ringtoets/test/Application.Ringtoets.Storage.Test/StorageSqLiteTest.cs =================================================================== diff -u -ref1c61d94f2aec3b4ff32fcf03253d7ad386c8e5 -r276383b754de8eafea064dd780e118d922379d76 --- Application/Ringtoets/test/Application.Ringtoets.Storage.Test/StorageSqLiteTest.cs (.../StorageSqLiteTest.cs) (revision ef1c61d94f2aec3b4ff32fcf03253d7ad386c8e5) +++ Application/Ringtoets/test/Application.Ringtoets.Storage.Test/StorageSqLiteTest.cs (.../StorageSqLiteTest.cs) (revision 276383b754de8eafea064dd780e118d922379d76) @@ -6,6 +6,7 @@ using Core.Common.Base.Storage; using Core.Common.TestUtil; using NUnit.Framework; +using Rhino.Mocks; using UtilsResources = Core.Common.Utils.Properties.Resources; namespace Application.Ringtoets.Storage.Test @@ -373,6 +374,79 @@ TearDownTempRingtoetsFile(tempRingtoetsFile); } + [Test] + public void HasChanges_NoConnectionSet_ReturnsTrue() + { + // Setup + StorageSqLite storageSqLite = new StorageSqLite(); + var mockingrepository = new MockRepository(); + var projectMock = mockingrepository.StrictMock(); + + // Call + bool hasChanges = storageSqLite.HasChanges(projectMock); + + // Assert + Assert.IsTrue(hasChanges); + } + + [Test] + public void HasChanges_ValidProjectLoaded_ReturnsFalse() + { + // Setup + StorageSqLite storageSqLite = new StorageSqLite(); + var dbFile = Path.Combine(testDataPath, "ValidRingtoetsDatabase.rtd"); + + // Precondition + Assert.IsTrue(File.Exists(dbFile), "Precondition: file must exist."); + Project loadedProject = storageSqLite.LoadProject(dbFile); + + // Call + bool hasChanges = storageSqLite.HasChanges(loadedProject); + + // Assert + Assert.IsFalse(hasChanges); + } + + [Test] + public void HasChanges_ValidProjectLoadedWithUnaffectedChange_ReturnsFalse() + { + // Setup + StorageSqLite storageSqLite = new StorageSqLite(); + var dbFile = Path.Combine(testDataPath, "ValidRingtoetsDatabase.rtd"); + var changedName = "some name"; + + // Precondition + Assert.IsTrue(File.Exists(dbFile), "Precondition: file must exist."); + Project loadedProject = storageSqLite.LoadProject(dbFile); + + // Call + loadedProject.Name = changedName; + bool hasChanges = storageSqLite.HasChanges(loadedProject); + + // Assert + Assert.IsFalse(hasChanges); + } + + [Test] + public void HasChanges_ValidProjectLoadedWithAffectedChange_ReturnsTrue() + { + // Setup + StorageSqLite storageSqLite = new StorageSqLite(); + var dbFile = Path.Combine(testDataPath, "ValidRingtoetsDatabase.rtd"); + var changedDescription = "some description"; + + // Precondition + Assert.IsTrue(File.Exists(dbFile), "Precondition: file must exist."); + Project loadedProject = storageSqLite.LoadProject(dbFile); + + // Call + loadedProject.Description = changedDescription; + bool hasChanges = storageSqLite.HasChanges(loadedProject); + + // Assert + Assert.IsTrue(hasChanges); + } + private static void SetUpTempRingtoetsFile(string filePath) { if (File.Exists(filePath)) Index: Core/Common/src/Core.Common.Base/Storage/IStoreProject.cs =================================================================== diff -u -ref1c61d94f2aec3b4ff32fcf03253d7ad386c8e5 -r276383b754de8eafea064dd780e118d922379d76 --- Core/Common/src/Core.Common.Base/Storage/IStoreProject.cs (.../IStoreProject.cs) (revision ef1c61d94f2aec3b4ff32fcf03253d7ad386c8e5) +++ Core/Common/src/Core.Common.Base/Storage/IStoreProject.cs (.../IStoreProject.cs) (revision 276383b754de8eafea064dd780e118d922379d76) @@ -55,11 +55,11 @@ /// /// Converts to an existing entity in the storage. /// - /// + /// Connection arguments. /// The to save. /// Returns the number of changes that were saved. /// Thrown when is null. - /// is invalid. + /// is invalid. /// Thrown when /// /// does not exist. @@ -86,5 +86,12 @@ /// /// Project LoadProject(string connectionArguments); + + /// + /// Checks if differs from the last saved or loaded , if any. + /// + /// The to save. + /// true if last was set and is different from , false otherwise. + bool HasChanges(Project project); } } \ No newline at end of file Index: Core/Common/src/Core.Common.Gui/Commands/StorageCommandHandler.cs =================================================================== diff -u -ref1c61d94f2aec3b4ff32fcf03253d7ad386c8e5 -r276383b754de8eafea064dd780e118d922379d76 --- Core/Common/src/Core.Common.Gui/Commands/StorageCommandHandler.cs (.../StorageCommandHandler.cs) (revision ef1c61d94f2aec3b4ff32fcf03253d7ad386c8e5) +++ Core/Common/src/Core.Common.Gui/Commands/StorageCommandHandler.cs (.../StorageCommandHandler.cs) (revision 276383b754de8eafea064dd780e118d922379d76) @@ -71,13 +71,32 @@ this.projectOwner.ProjectClosing += ApplicationProjectClosing; } + /// + /// Checks if an action may continue when changes are detected. + /// + /// True if the action should continue, false otherwise. + public bool ContinueIfHasChanges() + { + var project = projectOwner.Project; + if (project == null || project.Equals(new Project()) || !projectPersistor.HasChanges(project)) + { + return true; + } + return OpenSaveOrDiscardProjectDialog(); + } + public void UpdateObserver() { mainWindowController.RefreshGui(); } public void CreateNewProject() { + if (!ContinueIfHasChanges()) + { + log.Info(Resources.StorageCommandHandler_NewProject_Creating_new_project_cancelled); + return; + } CloseProject(); log.Info(Resources.StorageCommandHandler_NewProject_Creating_new_project); @@ -97,7 +116,7 @@ RestoreDirectory = true }) { - if (openFileDialog.ShowDialog(mainWindowController.MainWindow) != DialogResult.Cancel) + if (openFileDialog.ShowDialog(mainWindowController.MainWindow) != DialogResult.Cancel && ContinueIfHasChanges()) { return OpenExistingProject(openFileDialog.FileName); } @@ -206,6 +225,26 @@ projectOwner.ProjectClosing -= ApplicationProjectClosing; } + private bool OpenSaveOrDiscardProjectDialog() + { + var confirmation = MessageBox.Show( + String.Format(Resources.StorageCommandHandler_OpenSaveOrDiscardProjectDialog_SaveChangesToProject_0, projectOwner.Project.Name), + Resources.StorageCommandHandler_ClosingProject_Title, + MessageBoxButtons.YesNoCancel); + + switch (confirmation) + { + case DialogResult.Cancel: + return false; + case DialogResult.Yes: + SaveProject(); + break; + case DialogResult.No: + break; + } + return true; + } + /// /// Loads the project from the . /// Index: Core/Common/src/Core.Common.Gui/GuiCore.cs =================================================================== diff -u -rb743d495d10779d51c8f75b7cb04b5babb4b226f -r276383b754de8eafea064dd780e118d922379d76 --- Core/Common/src/Core.Common.Gui/GuiCore.cs (.../GuiCore.cs) (revision b743d495d10779d51c8f75b7cb04b5babb4b226f) +++ Core/Common/src/Core.Common.Gui/GuiCore.cs (.../GuiCore.cs) (revision 276383b754de8eafea064dd780e118d922379d76) @@ -179,6 +179,13 @@ return; //already got here before } + // Store project? + if (!storageCommandHandler.ContinueIfHasChanges()) + { + // User pressed cancel + return; + } + isExiting = true; CopyDefaultViewsToUserSettings(); @@ -300,7 +307,7 @@ var propertyCacheInfo = reflectTypeDescriptionProviderType.GetField("_propertyCache", BindingFlags.Static | BindingFlags.NonPublic); - var propertyCache = (Hashtable)propertyCacheInfo.GetValue(null); + var propertyCache = (Hashtable) propertyCacheInfo.GetValue(null); if (propertyCache != null) { propertyCache.Clear(); Index: Core/Common/src/Core.Common.Gui/Properties/Resources.Designer.cs =================================================================== diff -u -r23673a3afadbf542d649f724cb6557c1da742cf8 -r276383b754de8eafea064dd780e118d922379d76 --- Core/Common/src/Core.Common.Gui/Properties/Resources.Designer.cs (.../Resources.Designer.cs) (revision 23673a3afadbf542d649f724cb6557c1da742cf8) +++ Core/Common/src/Core.Common.Gui/Properties/Resources.Designer.cs (.../Resources.Designer.cs) (revision 276383b754de8eafea064dd780e118d922379d76) @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.18444 +// Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -1638,6 +1638,15 @@ } /// + /// Looks up a localized string similar to Project aan het sluiten.... + /// + public static string StorageCommandHandler_ClosingProject_Title { + get { + return ResourceManager.GetString("StorageCommandHandler_ClosingProject_Title", resourceCulture); + } + } + + /// /// Looks up a localized string similar to Nieuw Ringtoetsproject succesvol geopend.. /// public static string StorageCommandHandler_NewProject_Created_new_project_succesful { @@ -1656,6 +1665,15 @@ } /// + /// Looks up a localized string similar to Openen van een nieuw Ringtoetsproject geannuleerd.. + /// + public static string StorageCommandHandler_NewProject_Creating_new_project_cancelled { + get { + return ResourceManager.GetString("StorageCommandHandler_NewProject_Creating_new_project_cancelled", resourceCulture); + } + } + + /// /// Looks up a localized string similar to Openen van bestaand Ringtoetsproject.. /// public static string StorageCommandHandler_OpenExistingProject_Opening_existing_project { @@ -1692,6 +1710,15 @@ } /// + /// Looks up a localized string similar to Sla wijzigingen in het project op: {0}?. + /// + public static string StorageCommandHandler_OpenSaveOrDiscardProjectDialog_SaveChangesToProject_0 { + get { + return ResourceManager.GetString("StorageCommandHandler_OpenSaveOrDiscardProjectDialog_SaveChangesToProject_0", resourceCulture); + } + } + + /// /// Looks up a localized string similar to Opslaan van het Ringtoetsproject geannuleerd.. /// public static string StorageCommandHandler_SaveProject_Saving_project_cancelled { Index: Core/Common/src/Core.Common.Gui/Properties/Resources.resx =================================================================== diff -u -r23673a3afadbf542d649f724cb6557c1da742cf8 -r276383b754de8eafea064dd780e118d922379d76 --- Core/Common/src/Core.Common.Gui/Properties/Resources.resx (.../Resources.resx) (revision 23673a3afadbf542d649f724cb6557c1da742cf8) +++ Core/Common/src/Core.Common.Gui/Properties/Resources.resx (.../Resources.resx) (revision 276383b754de8eafea064dd780e118d922379d76) @@ -679,4 +679,13 @@ Onbekend + + Sla wijzigingen in het project op: {0}? + + + Project aan het sluiten... + + + Openen van een nieuw Ringtoetsproject geannuleerd. + \ No newline at end of file Index: Core/Common/test/Core.Common.Gui.Test/Commands/StorageCommandHandlerTest.cs =================================================================== diff -u -ref1c61d94f2aec3b4ff32fcf03253d7ad386c8e5 -r276383b754de8eafea064dd780e118d922379d76 --- Core/Common/test/Core.Common.Gui.Test/Commands/StorageCommandHandlerTest.cs (.../StorageCommandHandlerTest.cs) (revision ef1c61d94f2aec3b4ff32fcf03253d7ad386c8e5) +++ Core/Common/test/Core.Common.Gui.Test/Commands/StorageCommandHandlerTest.cs (.../StorageCommandHandlerTest.cs) (revision 276383b754de8eafea064dd780e118d922379d76) @@ -1,20 +1,18 @@ using System; - using Core.Common.Base; using Core.Common.Base.Data; using Core.Common.Base.Storage; using Core.Common.Gui.Commands; using Core.Common.Gui.Selection; using Core.Common.TestUtil; - +using NUnit.Extensions.Forms; using NUnit.Framework; - using Rhino.Mocks; namespace Core.Common.Gui.Test.Commands { [TestFixture] - public class StorageCommandHandlerTest + public class StorageCommandHandlerTest : NUnitFormTest { private MockRepository mocks; @@ -500,5 +498,183 @@ Assert.IsNull(applicationSelection.Selection); mocks.VerifyAll(); } + + [Test] + public void ContinueIfHasChanges_NoProjectSet_ReturnsTrue() + { + // Setup + var projectStorage = mocks.Stub(); + var applicationSelection = mocks.Stub(); + var mainWindowController = mocks.Stub(); + var toolViewController = mocks.StrictMock(); + var viewCommands = mocks.StrictMock(); + var projectOwner = mocks.Stub(); + mocks.ReplayAll(); + + using (var commandHandler = new StorageCommandHandler(projectStorage, projectOwner, applicationSelection, + mainWindowController, toolViewController, viewCommands)) + { + // Call + bool result = commandHandler.ContinueIfHasChanges(); + + // Assert + Assert.IsTrue(result); + } + mocks.VerifyAll(); + } + + [Test] + public void ContinueIfHasChanges_ProjectSetNoChange_ReturnsTrue() + { + // Setup + var viewCommandsMock = mocks.StrictMock(); + var applicationSelection = mocks.Stub(); + var mainWindowController = mocks.Stub(); + var toolViewController = mocks.Stub(); + var projectMock = mocks.StrictMock(); + var projectStorageMock = mocks.Stub(); + projectStorageMock.Expect(p => p.HasChanges(null)).IgnoreArguments().Return(false); + + var projectOwnerMock = mocks.Stub(); + projectOwnerMock.Project = projectMock; + mocks.ReplayAll(); + + using (var storageCommandHandler = new StorageCommandHandler(projectStorageMock, projectOwnerMock, applicationSelection, + mainWindowController, toolViewController, viewCommandsMock)) + { + // Call + bool actionMaycontinue = storageCommandHandler.ContinueIfHasChanges(); + + // Assert + Assert.IsTrue(actionMaycontinue); + } + mocks.VerifyAll(); + } + + [Test] + [RequiresSTA] + public void ContinueIfHasChanges_ProjectSetWithChangeCancelPressed_ReturnsFalse() + { + // Setup + var viewCommandsMock = mocks.StrictMock(); + var applicationSelection = mocks.Stub(); + var mainWindowController = mocks.Stub(); + var toolViewController = mocks.Stub(); + var projectMock = mocks.StrictMock(); + var projectStorageMock = mocks.Stub(); + projectStorageMock.Expect(p => p.HasChanges(null)).IgnoreArguments().Return(true); + + var projectOwnerMock = mocks.Stub(); + projectOwnerMock.Project = projectMock; + mocks.ReplayAll(); + + string messageBoxText = null; + string expectedMessage = "Sla wijzigingen in het project op: Project?"; + + using (var storageCommandHandler = new StorageCommandHandler(projectStorageMock, projectOwnerMock, applicationSelection, + mainWindowController, toolViewController, viewCommandsMock)) + { + DialogBoxHandler = (name, wnd) => + { + var helper = new MessageBoxTester(wnd); + messageBoxText = helper.Text; + helper.ClickCancel(); + }; + + // Call + bool actionMaycontinue = storageCommandHandler.ContinueIfHasChanges(); + + // Assert + Assert.IsFalse(actionMaycontinue); + } + mocks.VerifyAll(); + Assert.AreEqual(expectedMessage, messageBoxText); + } + + [Test] + [RequiresSTA] + public void ContinueIfHasChanges_ProjectSetWithChangeNoPressed_ReturnsTrue() + { + // Setup + var viewCommandsMock = mocks.StrictMock(); + var applicationSelection = mocks.Stub(); + var mainWindowController = mocks.Stub(); + var toolViewController = mocks.Stub(); + var projectMock = mocks.StrictMock(); + var projectStorageMock = mocks.Stub(); + projectStorageMock.Expect(p => p.HasChanges(null)).IgnoreArguments().Return(true); + + var projectOwnerMock = mocks.Stub(); + projectOwnerMock.Project = projectMock; + mocks.ReplayAll(); + + string messageBoxText = null; + string expectedMessage = "Sla wijzigingen in het project op: Project?"; + + using (var storageCommandHandler = new StorageCommandHandler(projectStorageMock, projectOwnerMock, applicationSelection, + mainWindowController, toolViewController, viewCommandsMock)) + { + DialogBoxHandler = (name, wnd) => + { + var helper = new MessageBoxTester(wnd); + messageBoxText = helper.Text; + helper.SendCommand(MessageBoxTester.Command.No); + }; + + // Call + bool actionMaycontinue = storageCommandHandler.ContinueIfHasChanges(); + + // Assert + Assert.IsTrue(actionMaycontinue); + } + mocks.VerifyAll(); + Assert.AreEqual(expectedMessage, messageBoxText); + } + + [Test] + [RequiresSTA] + public void ContinueIfHasChanges_ProjectSetWithChangeYesPressed_ReturnsTrue() + { + // Setup + var viewCommandsMock = mocks.StrictMock(); + var applicationSelection = mocks.Stub(); + var mainWindowController = mocks.Stub(); + var toolViewController = mocks.Stub(); + var projectMock = mocks.StrictMock(); + + var projectFilePath = "some path"; + + var projectStorageMock = mocks.Stub(); + projectStorageMock.Expect(p => p.HasChanges(null)).IgnoreArguments().Return(true); + + var projectOwnerMock = mocks.Stub(); + projectOwnerMock.Project = projectMock; + projectOwnerMock.ProjectFilePath = projectFilePath; + + projectStorageMock.Expect(p => p.SaveProject(projectFilePath, projectMock)).Return(1); + mocks.ReplayAll(); + + string messageBoxText = null; + string expectedMessage = "Sla wijzigingen in het project op: Project?"; + + using (var storageCommandHandler = new StorageCommandHandler(projectStorageMock, projectOwnerMock, applicationSelection, + mainWindowController, toolViewController, viewCommandsMock)) + { + DialogBoxHandler = (name, wnd) => + { + var helper = new MessageBoxTester(wnd); + messageBoxText = helper.Text; + helper.SendCommand(MessageBoxTester.Command.Yes); + }; + + // Call + bool actionMaycontinue = storageCommandHandler.ContinueIfHasChanges(); + + // Assert + Assert.IsTrue(actionMaycontinue); + } + mocks.VerifyAll(); + Assert.AreEqual(expectedMessage, messageBoxText); + } } } \ No newline at end of file