diff --git a/CHANGELOG.md b/CHANGELOG.md index c714305d7..fe01a43cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,68 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added - [SIL.Core] Added macOS support for `GlobalMutex` +- [SIL.Archiving] Added ArchivingDlgViewModel.Standard and ArchivingDlgViewModel.StringId emumerations. +- [SIL.Archiving] Added public delegate ArchivingDlgViewModel.ExceptionHandler and event ArchivingDlgViewModel.OnExceptionDuringLaunch. +- [SIL.Archiving] Added IArchivingProgressDisplay interface. +- [SIL.Archiving] Added overload of ArchivingDlgViewModel.DisplayMessage to take format parameters. +- [SIL.Archiving] Added public overload of ArchivingDlgViewModel.LaunchArchivingProgram. +- [SIL.Archiving] Added protected methods to ArchivingDlgViewModel: ReportMajorProgressPoint, ReportProgress, CleanUp +- [SIL.Windows.Forms.Archiving] Added protected virtual properties ArchiveTypeForTitleBar and InformativeText to ArchivingDlg. +- [SIL.Windows.Forms.Archiving] Added public virtual method GetMessage to ArchivingDlg. +- [SIL.Windows.Forms.Archiving] Added public virtual property ArchiveTypeName to ArchivingDlg. +- [SIL.Windows.Forms.Archiving] Added protected methods DisplayMessage and Initialize (async) to ArchivingDlg. +- [SIL.Windows.Forms.Archiving] Added protected virtual method PackageCreationComplete to ArchivingDlg. +- [SIL.Windows.Forms.Archiving] Added (public) override of property ArchiveTypeName to IMDIArchivingDlg. +- [SIL.Windows.Forms.Archiving] Added (protected) override of property InformativeText to IMDIArchivingDlg. +- [SIL.Windows.Forms.Archiving] Added (protected) override of method PackageCreationComplete to IMDIArchivingDlg. +- [SIL.Windows.Forms.Archiving] Added (public) override of method GetMessage to IMDIArchivingDlg. +- [SIL.Windows.Forms.Archiving] Added public extensions class LinkLabelExtensions with some methods that were formerly in Extensions class (now in SIL.Archiving). +- [SIL.Archiving] Added public property isValid to IMDIPackage. +- [SIL.Archiving] Added public event InitializationFailed to IMDIArchivingDlgViewModel. + +### Changed + +- [SIL.Windows.Forms.Archiving] Split SIL.Archiving, moving Winforms portions (including dependency on L10nSharp) to SIL.Windows.Forms.Archiving. +- [SIL.Archiving] Required ArchivingDlgViewModel implementations to implement IDisposable. +- [SIL.Archiving] Made protected members in ArchivingDlgViewModel private, adding protected accessors as needed. +- [SIL.Archiving] In ArchivingDlgViewModel, renamed DisplayMessageEventHandler to MessageEventHandler, OnDisplayMessage to OnReportMessage, DisplayErrorEventHandler to ErrorEventHandler, and OnDisplayError to OnError. +- [SIL.Archiving] Changed signature of ArchivingDlgViewModel.OverrideDisplayInitialSummary to include a CancellationToken. +- [SIL.Archiving] Made ArchivingDlgViewModel.ArchiveType property public and changed it from a string to Standard (new enum). +- [SIL.Archiving] Changed signature of setFilesToArchive delegate in ArchivingDlgViewModel's protected constructor. +- [SIL.Archiving] Changed return type of ArchivingDlgViewModel.Initialize (to make it async) and added two parameters. +- [SIL.Archiving] Changed ArchivingDlgViewModel.DisplayMessage from public to protected. +- [SIL.Archiving] Changed the signature of protected methods in ArchivingDlgViewModel: LaunchArchivingProgram, GetFileExcludedMsg. +- [SIL.Archiving] Changed the signature of the public method ArchivingDlgViewModel.CreatePackage. +- [SIL.Archiving] Changed underlying type of public enums VernacularMaterialsType and SilDomain from ulong to long. +- [SIL.Archiving] Replaced protected _keys field (now private) in abstract class ArchivingPackage with protected accessor property Keys. +- [SIL.Archiving] IMDIArchivingDlgViewModel (subclass of ArchivingDlgViewModel) affected by many of the changes to the base class. +- [SIL.Archiving] IMDIArchivingDlgViewModel and RampArchivingDlgViewModel (subclasses of ArchivingDlgViewModel) affected by many of the changes to the base class. +- [SIL.Archiving] IMDIArchivingDlgViewModel constructor signature changed. +- [SIL.Archiving] RampArchivingDlgViewModel constructor signature changed. +- [SIL.Windows.Forms.Archiving] Made ArchivingDlg implement IArchivingProgressDisplay. +- [SIL.Windows.Forms.Archiving] ArchivingDlg constructor signature changed: removed localizationManagerId; added optional archiveInfoHyperlinkText; made some other parameters optional. +- [SIL.Windows.Forms.Archiving] IMDIArchivingDlg constructor signature changed: added appSpecificArchivalProcessInfo. +- [SIL.Windows.Forms] Split ClearShare code, moving non-Winforms portions to SIL.Core (SIL.Core.ClearShare namespace) +- [SIL.Core] Added optional parameter to OlacSystem.GetRoles to allow caller to provide its own XML with role definitions. +- [SIL.Windows.Forms] Split License into a base class called License and a derived LicenseWithLogo, so that License could be in SIL.Core. +- [SIL.Archiving] Changed IArchivingSession.Files (and Session.Files) into an IReadonlyList. +- [SIL.Archiving] Made IMDIPackage.CreateIMDIPackage asynchronous, changing its signature to take a CancellationToken parameter and return Task. +- [SIL.Archiving] Made MetaTranscript.WriteCorpusImdiFile asynchronous, changing its signature to return Task. + +### Fixed +- [SIL.Archiving] Fixed typo in RampArchivingDlgViewModel for Ethnomusicology performance collection. +- [SIL.Archiving] Changed URLs that used http: to https: in resource EmptyMets.xml. + +### Removed + +- [SIL.Windows.Forms] Removed previously deprecated CreativeCommonsLicense.IntergovernmentalOriganizationQualifier +- [SIL.Archiving] Removed abstract properties from ArchivingDlgViewModel: InformativeText and ArchiveInfoHyperlinkText. +- [SIL.Archiving] Removed public method ArchivingDlgViewModel.Cancel. (Now handled via cancellation tokens.) +- [SIL.Archiving] Removed protected methods from ArchivingDlgViewModel: PreparingFilesMsg, GetSavingFilesMsg +- [SIL.Archiving] Removed protected fields (renamed and made private) from ArchivingLanguage: _iso3Code, _englishName +- [SIL.Archiving] Removed protected fields (made private) from ArchivingFile: _fullName, _fileName, _fileSize, _mimeType, _descriptions, _accessProtocol +- [SIL.Archiving] Removed public methods CreateMetsFile and CreateRampPackage from RampArchivingDlgViewModel (made internal). +- [SIL.Archiving] Removed ArchivingPackage and AddSession from ArchivingDlgViewModel and RampArchivingDlgViewModel (where they threw NotImplementedExceptions) ### Fixed @@ -275,7 +337,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - [SIL.Core, SIL.Windows.Forms] `IErrorReporter` interface added a simpler overload of NotifyUserOfProblem method, which must be implemented by IErrorReporters. (It is acceptable for implementers to just fill some parameters then call the original method) `ConsoleErrorReporter` and `WinFormsErrorReporter` implement `IErrorReporter`'s new interface method -- [SIL.Core] Added override of SerializeToFileWithWriteThrough to simplify error handling. +- [SIL.Core] Added overload of SerializeToFileWithWriteThrough to simplify error handling. - [SIL.Windows.Forms] Added a CheckedComboBox control - [SIL.WritingSystems] Added several methods to IetfLanguageTag class to support getting language names. - [SIL.Windows.Forms.WritingSystems] Added extension method InitializeWithAvailableUILocales diff --git a/Palaso.sln b/Palaso.sln index c8cae8d01..e7e313c7c 100755 --- a/Palaso.sln +++ b/Palaso.sln @@ -25,9 +25,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Reporting.TestApp", "TestAp {DB44F49C-D8C6-434F-81ED-28EA5C9E8195} = {DB44F49C-D8C6-434F-81ED-28EA5C9E8195} EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SIL.Archiving", "SIL.Archiving\SIL.Archiving.csproj", "{BCE1F124-5479-4B23-90B1-B7A4EBE44FA3}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SIL.Archiving.Tests", "SIL.Archiving.Tests\SIL.Archiving.Tests.csproj", "{892C7F20-FBBB-4AB3-BAC2-E40A135567B6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SIL.Windows.Forms.Archiving", "SIL.Windows.Forms.Archiving\SIL.Windows.Forms.Archiving.csproj", "{BCE1F124-5479-4B23-90B1-B7A4EBE44FA3}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SIL.Scripture", "SIL.Scripture\SIL.Scripture.csproj", "{F71BA7B9-D9DC-4F8C-A307-87B503D0E05B}" EndProject @@ -118,7 +116,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SIL.TestUtilities.Tests", " EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClipboardTestApp", "TestApps\ClipboardTestApp\ClipboardTestApp.csproj", "{B1E0B522-FEDF-497E-BFCD-A572F67C03C1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommandLineRunnerTestApp", "TestApps\CommandLineRunnerTestApp\CommandLineRunnerTestApp.csproj", "{6407C2F9-F62D-4568-B694-7A79C72582C9}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommandLineRunnerTestApp", "TestApps\CommandLineRunnerTestApp\CommandLineRunnerTestApp.csproj", "{6407C2F9-F62D-4568-B694-7A79C72582C9}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SIL.Archiving", "SIL.Archiving\SIL.Archiving.csproj", "{01B31D97-63A4-4FC6-940F-D7278F4D0F65}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SIL.Archiving.Tests", "SIL.Archiving.Tests\SIL.Archiving.Tests.csproj", "{698FDB66-0ACE-46E0-843C-3AB70DC7B055}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ArchivingTestApp", "TestApps\ArchivingTestApp\ArchivingTestApp.csproj", "{321BB622-8185-4173-8770-742CD620BC18}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -162,10 +166,6 @@ Global {BCE1F124-5479-4B23-90B1-B7A4EBE44FA3}.Debug|Any CPU.Build.0 = Debug|Any CPU {BCE1F124-5479-4B23-90B1-B7A4EBE44FA3}.Release|Any CPU.ActiveCfg = Release|Any CPU {BCE1F124-5479-4B23-90B1-B7A4EBE44FA3}.Release|Any CPU.Build.0 = Release|Any CPU - {892C7F20-FBBB-4AB3-BAC2-E40A135567B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {892C7F20-FBBB-4AB3-BAC2-E40A135567B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {892C7F20-FBBB-4AB3-BAC2-E40A135567B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {892C7F20-FBBB-4AB3-BAC2-E40A135567B6}.Release|Any CPU.Build.0 = Release|Any CPU {F71BA7B9-D9DC-4F8C-A307-87B503D0E05B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F71BA7B9-D9DC-4F8C-A307-87B503D0E05B}.Debug|Any CPU.Build.0 = Debug|Any CPU {F71BA7B9-D9DC-4F8C-A307-87B503D0E05B}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -302,6 +302,18 @@ Global {6407C2F9-F62D-4568-B694-7A79C72582C9}.Debug|Any CPU.Build.0 = Debug|Any CPU {6407C2F9-F62D-4568-B694-7A79C72582C9}.Release|Any CPU.ActiveCfg = Release|Any CPU {6407C2F9-F62D-4568-B694-7A79C72582C9}.Release|Any CPU.Build.0 = Release|Any CPU + {01B31D97-63A4-4FC6-940F-D7278F4D0F65}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {01B31D97-63A4-4FC6-940F-D7278F4D0F65}.Debug|Any CPU.Build.0 = Debug|Any CPU + {01B31D97-63A4-4FC6-940F-D7278F4D0F65}.Release|Any CPU.ActiveCfg = Release|Any CPU + {01B31D97-63A4-4FC6-940F-D7278F4D0F65}.Release|Any CPU.Build.0 = Release|Any CPU + {698FDB66-0ACE-46E0-843C-3AB70DC7B055}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {698FDB66-0ACE-46E0-843C-3AB70DC7B055}.Debug|Any CPU.Build.0 = Debug|Any CPU + {698FDB66-0ACE-46E0-843C-3AB70DC7B055}.Release|Any CPU.ActiveCfg = Release|Any CPU + {698FDB66-0ACE-46E0-843C-3AB70DC7B055}.Release|Any CPU.Build.0 = Release|Any CPU + {321BB622-8185-4173-8770-742CD620BC18}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {321BB622-8185-4173-8770-742CD620BC18}.Debug|Any CPU.Build.0 = Debug|Any CPU + {321BB622-8185-4173-8770-742CD620BC18}.Release|Any CPU.ActiveCfg = Release|Any CPU + {321BB622-8185-4173-8770-742CD620BC18}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -314,6 +326,7 @@ Global {CA8F3657-3D85-4C97-81A6-5C212627B2F6} = {3C695301-9C39-4715-8EDD-817C63EF81E7} {B1E0B522-FEDF-497E-BFCD-A572F67C03C1} = {3C695301-9C39-4715-8EDD-817C63EF81E7} {6407C2F9-F62D-4568-B694-7A79C72582C9} = {3C695301-9C39-4715-8EDD-817C63EF81E7} + {321BB622-8185-4173-8770-742CD620BC18} = {3C695301-9C39-4715-8EDD-817C63EF81E7} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {0915B762-D4AE-4892-BF4F-AC428A687B02} diff --git a/Palaso.sln.DotSettings b/Palaso.sln.DotSettings index 8ee3e7046..9bd1aa386 100644 --- a/Palaso.sln.DotSettings +++ b/Palaso.sln.DotSettings @@ -20,10 +20,14 @@ Review (?<=\W|^)(?<TAG>REVIEW)(\W|$)(.*) Normal + True True True True True + True + True + True True True True @@ -34,6 +38,7 @@ True True True + True True True True @@ -41,10 +46,12 @@ True True True + True True True True True + True True True True @@ -52,39 +59,52 @@ True True True + True True True True True True + True True True True True + True True True + True True True True + True True True + True + True True True + True True + True True True True True + True + True True True True True + True True True True True True True + True True True True diff --git a/SIL.Archiving.Tests/ArchivingLanguageTests.cs b/SIL.Archiving.Tests/ArchivingLanguageTests.cs new file mode 100644 index 000000000..fd22f39e3 --- /dev/null +++ b/SIL.Archiving.Tests/ArchivingLanguageTests.cs @@ -0,0 +1,27 @@ +using NUnit.Framework; + +namespace SIL.Archiving.Tests +{ + [TestFixture] + public class ArchivingLanguageTests + { + [Test] + public void Compare_SameCodeAndLanguageName_ReturnsZero() + { + Assert.That(ArchivingLanguage.Compare(new ArchivingLanguage("eng", "English"), + new ArchivingLanguage("ENG", "English")), Is.EqualTo(0)); + } + + [TestCase("eng", "English", "frg", "Froggish", ExpectedResult = false)] + [TestCase("zzz", "Zyzzyvian", "zzz", "Scrabbilian", ExpectedResult = true)] + [TestCase("som", "Some Language", "unk", "Some Language", ExpectedResult = false)] + public bool Compare_DifferentCodeOrLanguageName_ReturnsNonZero(string code1, string name1, + string code2, string name2) + { + var val = ArchivingLanguage.Compare(new ArchivingLanguage(code1, name1), + new ArchivingLanguage(code2, name2)); + Assert.That(val, Is.Not.EqualTo(0)); + return val > 0; + } + } +} diff --git a/SIL.Archiving.Tests/IMDI30Tests.cs b/SIL.Archiving.Tests/IMDI30Tests.cs index 43b367b6d..238f21974 100644 --- a/SIL.Archiving.Tests/IMDI30Tests.cs +++ b/SIL.Archiving.Tests/IMDI30Tests.cs @@ -236,7 +236,8 @@ public void SessionAddActor_Anonymized_RemovesActorFiles() [Test] public void FindByISO3Code_InvalidIso3Code_MustBeInList_Throws() { - Assert.Throws(() => LanguageList.FindByISO3Code("xyz", true)); + var code = Assert.Throws(() => LanguageList.FindByISO3Code("xyz", true)).Code; + Assert.That(code, Is.EqualTo("xyz")); } [Test] diff --git a/SIL.Archiving.Tests/IMDIArchivingDlgViewModelTests.cs b/SIL.Archiving.Tests/IMDIArchivingDlgViewModelTests.cs index 6577854de..ff294e983 100644 --- a/SIL.Archiving.Tests/IMDIArchivingDlgViewModelTests.cs +++ b/SIL.Archiving.Tests/IMDIArchivingDlgViewModelTests.cs @@ -1,4 +1,6 @@ using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; using System.Xml; using NUnit.Framework; using SIL.Archiving.IMDI; @@ -14,7 +16,7 @@ internal class IMDIArchivingDlgViewModelTests { private class MessageData { - public string MsgText; + public readonly string MsgText; public ArchivingDlgViewModel.MessageType MsgType; public MessageData(string msg, ArchivingDlgViewModel.MessageType type) @@ -25,8 +27,9 @@ public MessageData(string msg, ArchivingDlgViewModel.MessageType type) } private IMDIArchivingDlgViewModel _model; - private TemporaryFolder _tmpFolder; - private List _messages; + private TestProgress m_progress; + private TemporaryFolder m_tmpFolder; + private List m_messages; private const string kAppName = "Tèst App Náme"; private const string kTitle = "Tèst Title"; private const string kArchiveId = "Tèst Corpus Náme"; // include some invalid characters for testing @@ -35,13 +38,17 @@ public MessageData(string msg, ArchivingDlgViewModel.MessageType type) /// ------------------------------------------------------------------------------------ [SetUp] - public void Setup() + public async Task Setup() { ErrorReport.IsOkToInteractWithUser = false; - _tmpFolder = new TemporaryFolder("IMDIArchiveHelperTestFolder"); - _model = new IMDIArchivingDlgViewModel(kAppName, kTitle, kArchiveId, null, true, dummyAction => { }, _tmpFolder.Path); - _messages = new List(); - _model.OnDisplayMessage += (msg, type) => { _messages.Add(new MessageData(msg, type)); }; + m_tmpFolder = new TemporaryFolder("IMDIArchiveHelperTestFolder"); + _model = new IMDIArchivingDlgViewModel(kAppName, kTitle, kArchiveId, true, (no, op) => { }, m_tmpFolder.Path); + m_progress = new TestProgress("IMDI"); + var cancel = new CancellationToken(); + await _model.Initialize(m_progress, cancel); + Assert.That(m_progress.Step, Is.EqualTo(1)); + m_messages = new List(); + _model.OnReportMessage += (msg, type) => { m_messages.Add(new MessageData(msg, type)); }; } /// ------------------------------------------------------------------------------------ @@ -49,7 +56,7 @@ public void Setup() public void TearDown() { _model.CleanUp(); - _tmpFolder.Dispose(); + m_tmpFolder.Dispose(); } #endregion @@ -79,10 +86,10 @@ public void CorpusDirectoryName_ValidNameForNewDirectory() [Test] public void PathIsAccessible_WritablePath_True() { - var dir = _tmpFolder.Path; + var dir = m_tmpFolder.Path; var writable = _model.IsPathWritable(dir); Assert.True(writable); - Assert.IsEmpty(_messages); + Assert.IsEmpty(m_messages); } [Test] @@ -91,9 +98,9 @@ public void PathIsAccessible_NonexistentPath_False() const string dir = "/one/two"; var writable = _model.IsPathWritable(dir); Assert.False(writable); - Assert.AreEqual(1, _messages.Count); - Assert.AreEqual("The path is not writable: /one/two", _messages[0].MsgText); - Assert.AreEqual(ArchivingDlgViewModel.MessageType.Warning, _messages[0].MsgType); + Assert.AreEqual(1, m_messages.Count); + Assert.AreEqual("Test implementation message for PathNotWritable", m_messages[0].MsgText); + Assert.AreEqual(ArchivingDlgViewModel.MessageType.Warning, m_messages[0].MsgType); } [Test] @@ -103,9 +110,9 @@ public void IsPathWritable_WindowsInvalidPath_False() const string dir = ":?"; var writable = _model.IsPathWritable(dir); Assert.False(writable); - Assert.AreEqual(1, _messages.Count); - Assert.AreEqual("The path is not of a legal form.", _messages[0].MsgText); - Assert.AreEqual(ArchivingDlgViewModel.MessageType.Warning, _messages[0].MsgType); + Assert.AreEqual(1, m_messages.Count); + Assert.AreEqual("The path is not of a legal form.", m_messages[0].MsgText); + Assert.AreEqual(ArchivingDlgViewModel.MessageType.Warning, m_messages[0].MsgType); } [Test] @@ -114,9 +121,9 @@ public void IsPathWritable_IllegalCharacterInPath_False() const string dir = "/\0"; var writable = _model.IsPathWritable(dir); Assert.False(writable); - Assert.AreEqual(1, _messages.Count); - Assert.AreEqual("Illegal characters in path.", _messages[0].MsgText); - Assert.AreEqual(ArchivingDlgViewModel.MessageType.Warning, _messages[0].MsgType); + Assert.AreEqual(1, m_messages.Count); + Assert.AreEqual("Illegal characters in path.", m_messages[0].MsgText); + Assert.AreEqual(ArchivingDlgViewModel.MessageType.Warning, m_messages[0].MsgType); } #endregion @@ -158,7 +165,6 @@ public void GetNameOfProgramToLaunch_ExeNameContainsFolderName_ReturnsFolderName [Test] public void SetAbstract_UnspecifiedLanguage_AddsDescriptionToCorpusImdiFile() { - _model.Initialize(); _model.SetAbstract("Story about a frog", string.Empty); XmlDocument doc = new XmlDocument(); doc.LoadXml(_model.GetMetadata()); @@ -190,7 +196,6 @@ public void SetAbstract_UnspecifiedLanguage_AddsDescriptionToCorpusImdiFile() [Test] public void SetAbstract_SingleLanguage_AddsDescriptionToCorpusImdiFile() { - _model.Initialize(); _model.SetAbstract("Story about a frog", "eng"); XmlDocument doc = new XmlDocument(); doc.LoadXml(_model.GetMetadata()); @@ -217,7 +222,6 @@ public void SetAbstract_SingleLanguage_AddsDescriptionToCorpusImdiFile() [Test] public void SetAbstract_MultipleLanguages_AddsDescriptionToCorpusImdiFile() { - _model.Initialize(); Dictionary descriptions = new Dictionary(); descriptions["eng"] = "Story about a frog"; descriptions["deu"] = "Geschichte über einen Frosch"; diff --git a/SIL.Archiving.Tests/JSONUtilsTests.cs b/SIL.Archiving.Tests/JSONUtilsTests.cs old mode 100755 new mode 100644 diff --git a/SIL.Archiving.Tests/LanguageListTests.cs b/SIL.Archiving.Tests/LanguageListTests.cs index 02e14549a..aa7d0d784 100644 --- a/SIL.Archiving.Tests/LanguageListTests.cs +++ b/SIL.Archiving.Tests/LanguageListTests.cs @@ -11,6 +11,7 @@ public class LanguageListTests { [TestCase("fra", "French")] [TestCase("tru", "Turoyo")] + [TestCase("eng", "English")] public void FindByISO3Code_ExistingLanguageWithNoOtherName_GetsExpectedNameCodeAndId(string iso3Code, string expectedEnglishName) { var result = LanguageList.FindByISO3Code(iso3Code); @@ -33,12 +34,14 @@ public void FindByISO3Code_NoneExistentCode_ResultHasIsoCodeButNoName() } [TestCase("French", "fra")] - [TestCase("français", "fra")] // Not really sure why this works + [TestCase("français", "fra")] // This works because even though "French" is the default (English) name associated with "fra", the Ethnologue data also contains "français" [TestCase("Turoyo", "tru")] - public void FindByEnglishName_ExistingLanguage_GetsExpectedNameCodeAndId(string name, string expectedIso3Code) + [TestCase("English", "eng")] + // [TestCase("Eng", "gss", "Greek Sign Language")] // because these tests use the offline Sldr and there is an outdated langtags.json in our resources, "Eng" does not resolve to gss. + public void FindByEnglishName_ExistingLanguage_GetsExpectedNameCodeAndId(string name, string expectedIso3Code, string defaultName = null) { var result = LanguageList.FindByEnglishName(name); - Assert.AreEqual(name, result.EnglishName); + Assert.AreEqual(defaultName ?? name, result.EnglishName); Assert.AreEqual(expectedIso3Code, result.Iso3Code); Assert.That(result.OtherName, Is.Null.Or.Empty); Assert.AreEqual($"ISO639-3:{expectedIso3Code}", result.Id); diff --git a/SIL.Archiving.Tests/RampArchivingDlgViewModelTests.cs b/SIL.Archiving.Tests/RampArchivingDlgViewModelTests.cs old mode 100755 new mode 100644 index 08f86a709..db5ad3f4f --- a/SIL.Archiving.Tests/RampArchivingDlgViewModelTests.cs +++ b/SIL.Archiving.Tests/RampArchivingDlgViewModelTests.cs @@ -3,12 +3,14 @@ using System.IO; using System.Linq; using System.Text; +using System.Threading; +using System.Threading.Tasks; using Ionic.Zip; using NUnit.Framework; +using SIL.Core.ClearShare; using SIL.IO; using SIL.Reporting; using SIL.TestUtilities; -using SIL.Windows.Forms.ClearShare; namespace SIL.Archiving.Tests { @@ -16,29 +18,33 @@ namespace SIL.Archiving.Tests [Category("Archiving")] public class RampArchivingDlgViewModelTests { - private RampArchivingDlgViewModel _helper; - private Dictionary, string>> _filesToAdd; - private bool? _isRampInstalled; + private RampArchivingDlgViewModel m_model; + private TestProgress m_progress; + private readonly Dictionary, string>> m_filesToAdd = + new Dictionary, string>>(); + private bool? m_isRampInstalled; /// ------------------------------------------------------------------------------------ [SetUp] - public void Setup() + public async Task Setup() { ErrorReport.IsOkToInteractWithUser = false; - _helper = new RampArchivingDlgViewModel("Test App", "Test Title", "tst", null, + m_model = new RampArchivingDlgViewModel("Test App", "Test Title", "tst", SetFilesToArchive, GetFileDescription); - _helper.AppSpecificFilenameNormalization = CustomFilenameNormalization; - _filesToAdd = new Dictionary, string>>(); + m_progress = new TestProgress("RAMP"); + m_model.AppSpecificFilenameNormalization = CustomFilenameNormalization; + var cancel = new CancellationToken(); + await m_model.Initialize(m_progress, cancel); } /// ------------------------------------------------------------------------------------ [TearDown] public void TearDown() { - _helper.CleanUp(); - - try { File.Delete(_helper.PackagePath); } + m_model.CleanUp(); + m_filesToAdd.Clear(); + try { File.Delete(m_model.PackagePath); } // ReSharper disable once EmptyGeneralCatchClause catch { } } @@ -47,7 +53,7 @@ public void TearDown() [Test] public void CreateMetsFile_CreatesFile() { - var metsPath = _helper.CreateMetsFile(); + var metsPath = m_model.CreateMetsFile(); Assert.IsNotNull(metsPath); Assert.IsTrue(File.Exists(metsPath)); } @@ -55,7 +61,7 @@ public void CreateMetsFile_CreatesFile() /// ------------------------------------------------------------------------------------ [Test] [Category("SkipOnTeamCity")] - public void CreateRampPackageWithSessionArchiveAndMetsFile_CreatesRampPackage() + public async Task CreateRampPackageWithSessionArchiveAndMetsFile_CreatesRampPackage() { TemporaryFolder tmpFolder = new TemporaryFolder("ArchiveHelperTestFolder"); try @@ -63,11 +69,12 @@ public void CreateRampPackageWithSessionArchiveAndMetsFile_CreatesRampPackage() string fileName = Path.Combine(tmpFolder.Path, "ddo.session"); File.CreateText(fileName).Close(); var fileList = new[] { Path.Combine(tmpFolder.Path, "ddo.session") }; - _filesToAdd.Add(string.Empty, new Tuple, string>(fileList, "Message to display.")); - _helper.Initialize(); - _helper.CreateMetsFile(); - Assert.IsTrue(_helper.CreateRampPackage()); - Assert.IsTrue(File.Exists(_helper.PackagePath)); + m_filesToAdd.Add(string.Empty, new Tuple, string>(fileList, "Message to display.")); + var cancel = new CancellationToken(); + await m_model.Initialize(new TestProgress(RampArchivingDlgViewModel.kRampProcessName), cancel); + m_model.CreateMetsFile(); + Assert.IsTrue(m_model.CreateRampPackage(new CancellationToken()).Result); + Assert.IsTrue(File.Exists(m_model.PackagePath)); } finally { @@ -79,14 +86,14 @@ public void CreateRampPackageWithSessionArchiveAndMetsFile_CreatesRampPackage() [Test] public void GetMode_NullList_ReturnsNull() { - Assert.IsNull(_helper.GetMode(null)); + Assert.IsNull(m_model.GetMode(null)); } /// ------------------------------------------------------------------------------------ [Test] public void GetMode_EmptyList_ReturnsNull() { - Assert.IsNull(_helper.GetMode(new string[0])); + Assert.IsNull(m_model.GetMode(Array.Empty())); } /// ------------------------------------------------------------------------------------ @@ -94,14 +101,14 @@ public void GetMode_EmptyList_ReturnsNull() public void GetMode_SingleTypeInList_ReturnsCorrectMetsList() { Assert.AreEqual("\"" + RampArchivingDlgViewModel.kFileTypeModeList + "\":[\"" + - RampArchivingDlgViewModel.kModeVideo + "\"]", _helper.GetMode(new[] { "blah.mpg" })); + RampArchivingDlgViewModel.kModeVideo + "\"]", m_model.GetMode(new[] { "blah.mpg" })); } /// ------------------------------------------------------------------------------------ [Test] public void GetMode_MultipleTypesInList_ReturnsCorrectMetsList() { - var mode = _helper.GetMode(new[] { "blah.mp3", "blah.doc", "blah.mov" }); + var mode = m_model.GetMode(new[] { "blah.mp3", "blah.doc", "blah.mov" }); Assert.AreEqual("\"" + RampArchivingDlgViewModel.kFileTypeModeList + "\":[\"" + RampArchivingDlgViewModel.kModeSpeech + "\",\"" + RampArchivingDlgViewModel.kModeText + "\",\"" + @@ -120,7 +127,7 @@ public void GetMode_ZipFileWithMultipleTypesInList_ReturnsCorrectMetsList() try { zipFile.Save(tempFile.Path); - var mode = _helper.GetMode(new[] { zipFile.Name }); + var mode = m_model.GetMode(new[] { zipFile.Name }); Assert.AreEqual("\"" + RampArchivingDlgViewModel.kFileTypeModeList + "\":[\"" + RampArchivingDlgViewModel.kModeSpeech + "\",\"" + RampArchivingDlgViewModel.kModeText + "\",\"" + @@ -135,7 +142,7 @@ public void GetMode_ZipFileWithMultipleTypesInList_ReturnsCorrectMetsList() /// ------------------------------------------------------------------------------------ [Test] - public void GetMode_FwbackupFileWithMultipleTypesInList_ReturnsCorrectMetsList() + public void GetMode_FwBackupFileWithMultipleTypesInList_ReturnsCorrectMetsList() { ZipFile zipFile = new ZipFile(); zipFile.AddEntry("blah.fwdata", "whatever"); @@ -145,7 +152,7 @@ public void GetMode_FwbackupFileWithMultipleTypesInList_ReturnsCorrectMetsList() try { zipFile.Save(tempFile.Path); - var mode = _helper.GetMode(new[] { zipFile.Name }); + var mode = m_model.GetMode(new[] { zipFile.Name }); Assert.AreEqual("\"" + RampArchivingDlgViewModel.kFileTypeModeList + "\":[\"" + RampArchivingDlgViewModel.kModeText + "\",\"" + RampArchivingDlgViewModel.kModeDataset + "\",\"" + @@ -163,7 +170,7 @@ public void GetMode_FwbackupFileWithMultipleTypesInList_ReturnsCorrectMetsList() [Test] public void GetMode_ListContainsMultiplesOfOneType_ReturnsOnlyOneTypeInList() { - var mode = _helper.GetMode(new[] { "blah.mp3", "blah.wma", "blah.wav" }); + var mode = m_model.GetMode(new[] { "blah.mp3", "blah.wma", "blah.wav" }); Assert.AreEqual("\"" + RampArchivingDlgViewModel.kFileTypeModeList + "\":[\"" + RampArchivingDlgViewModel.kModeSpeech + "\"]", mode); } @@ -173,81 +180,100 @@ public void GetMode_ListContainsMultiplesOfOneType_ReturnsOnlyOneTypeInList() [Test] public void GetSourceFilesForMetsData_ListContainsOnlySessionMetaFile_ReturnsCorrectMetsData() { - var fileLists = new Dictionary, string>>(); - fileLists[string.Empty] = new Tuple, string>(new[] { "blah.session" }, "Message to display."); + var fileLists = new Dictionary, string>> + { + [string.Empty] = new Tuple, string>( + new[] { "blah.session" }, "Message to display.") + }; var expected = "\"" + RampArchivingDlgViewModel.kDefaultKey + "\":\"blah.session\"" + RampArchivingDlgViewModel.kSeparator + "\"" + RampArchivingDlgViewModel.kFileDescription + "\":\"MyApp Session Metadata (XML)\"" + RampArchivingDlgViewModel.kSeparator + "\"" + RampArchivingDlgViewModel.kFileRelationship + "\":\"" + RampArchivingDlgViewModel.kRelationshipSource + "\""; - Assert.AreEqual(expected, _helper.GetSourceFilesForMetsData(fileLists).ElementAt(0)); + Assert.AreEqual(expected, m_model.GetSourceFilesForMetsData(fileLists).ElementAt(0)); } /// ------------------------------------------------------------------------------------ [Test] public void GetSourceFilesForMetsData_ListContainsOnlyPersonMetaFile_ReturnsCorrectMetsData() { - var fileLists = new Dictionary, string>>(); - fileLists[string.Empty] = new Tuple, string>(new[] { "blah.person" }, "Message to display."); + var fileLists = new Dictionary, string>> + { + [string.Empty] = new Tuple, string>( + new[] { "blah.person" }, "Message to display.") + }; var expected = "\"" + RampArchivingDlgViewModel.kDefaultKey + "\":\"blah.person\"" + RampArchivingDlgViewModel.kSeparator + "\"" + RampArchivingDlgViewModel.kFileDescription + "\":\"MyApp Contributor Metadata (XML)\"" + RampArchivingDlgViewModel.kSeparator + "\"" + RampArchivingDlgViewModel.kFileRelationship + "\":\"" + RampArchivingDlgViewModel.kRelationshipSource + "\""; - Assert.AreEqual(expected, _helper.GetSourceFilesForMetsData(fileLists).ElementAt(0)); + Assert.AreEqual(expected, m_model.GetSourceFilesForMetsData(fileLists).ElementAt(0)); } /// ------------------------------------------------------------------------------------ [Test] public void GetSourceFilesForMetsData_ListContainsOnlyMetaFile_ReturnsCorrectMetsData() { - var fileLists = new Dictionary, string>>(); - fileLists[string.Empty] = new Tuple, string>(new[] { "blah.meta" }, "Message to display."); + var fileLists = new Dictionary, string>> + { + [string.Empty] = new Tuple, string>( + new[] { "blah.meta" }, "Message to display.") + }; var expected = "\"" + RampArchivingDlgViewModel.kDefaultKey + "\":\"blah.meta\"" + RampArchivingDlgViewModel.kSeparator + "\"" + RampArchivingDlgViewModel.kFileDescription + "\":\"MyApp File Metadata (XML)\"" + RampArchivingDlgViewModel.kSeparator + "\"" + RampArchivingDlgViewModel.kFileRelationship + "\":\"" + RampArchivingDlgViewModel.kRelationshipSource + "\""; - Assert.AreEqual(expected, _helper.GetSourceFilesForMetsData(fileLists).ElementAt(0)); + Assert.AreEqual(expected, m_model.GetSourceFilesForMetsData(fileLists).ElementAt(0)); } /// ------------------------------------------------------------------------------------ [Test] public void GetSourceFilesForMetsData_ListContainsGenericSessionFile_ReturnsCorrectMetsData() { - var fileLists = new Dictionary, string>>(); - fileLists[string.Empty] = new Tuple, string>(new[] { "blah.wav" }, "Message to display."); + var fileLists = new Dictionary, string>> + { + [string.Empty] = new Tuple, string>( + new[] { "blah.wav" }, "Message to display.") + }; var expected = "\"" + RampArchivingDlgViewModel.kDefaultKey + "\":\"blah.wav\"" + RampArchivingDlgViewModel.kSeparator + "\"" + RampArchivingDlgViewModel.kFileDescription + "\":\"MyApp Session File\"" + RampArchivingDlgViewModel.kSeparator + "\"" + RampArchivingDlgViewModel.kFileRelationship + "\":\"" + RampArchivingDlgViewModel.kRelationshipSource + "\""; - Assert.AreEqual(expected, _helper.GetSourceFilesForMetsData(fileLists).ElementAt(0)); + Assert.AreEqual(expected, m_model.GetSourceFilesForMetsData(fileLists).ElementAt(0)); } /// ------------------------------------------------------------------------------------ [Test] public void GetSourceFilesForMetsData_ListContainsGenericPersonFile_ReturnsCorrectMetsData() { - var fileLists = new Dictionary, string>>(); - fileLists["Carmen"] = new Tuple, string>(new[] { "Carmen_blah.wav" }, "Message to display."); + var fileLists = new Dictionary, string>> + { + ["Carmen"] = new Tuple, string>( + new[] { "Carmen_blah.wav" }, "Message to display.") + }; var expected = "\"" + RampArchivingDlgViewModel.kDefaultKey + "\":\"__AppSpecific__Carmen_blah.wav\"" + RampArchivingDlgViewModel.kSeparator + "\"" + RampArchivingDlgViewModel.kFileDescription + "\":\"MyApp Contributor File\"" + RampArchivingDlgViewModel.kSeparator + "\"" + RampArchivingDlgViewModel.kFileRelationship + "\":\"" + RampArchivingDlgViewModel.kRelationshipSource + "\""; - Assert.AreEqual(expected, _helper.GetSourceFilesForMetsData(fileLists).ElementAt(0)); + Assert.AreEqual(expected, m_model.GetSourceFilesForMetsData(fileLists).ElementAt(0)); } /// ------------------------------------------------------------------------------------ [Test] public void GetSourceFilesForMetsData_ListMultipleFiles_ReturnsCorrectMetsData() { - var fileLists = new Dictionary, string>>(); - fileLists[string.Empty] = new Tuple, string>(new[] { "blah.session", "really cool.wav" }, "Message to display."); - fileLists["person id"] = new Tuple, string>(new[] { "person id_blah.person", "person id_baa.mpg", "person id_baa.mpg.meta" }, "Message to display."); + var fileLists = new Dictionary, string>> + { + [string.Empty] = new Tuple, string>( + new[] { "blah.session", "really cool.wav" }, "Message to display."), + ["person id"] = new Tuple, string>( + new[] { "person id_blah.person", "person id_baa.mpg", "person id_baa.mpg.meta" }, "Message to display.") + }; Assert.AreEqual("\"" + RampArchivingDlgViewModel.kDefaultKey + "\":\"blah.session\"" + RampArchivingDlgViewModel.kSeparator + "\"" + @@ -255,7 +281,7 @@ public void GetSourceFilesForMetsData_ListMultipleFiles_ReturnsCorrectMetsData() RampArchivingDlgViewModel.kSeparator + "\"" + RampArchivingDlgViewModel.kFileRelationship + "\":\"" + RampArchivingDlgViewModel.kRelationshipSource + "\"", - _helper.GetSourceFilesForMetsData(fileLists).ElementAt(0)); + m_model.GetSourceFilesForMetsData(fileLists).ElementAt(0)); Assert.AreEqual("\"" + RampArchivingDlgViewModel.kDefaultKey + "\":\"really-cool.wav\"" + RampArchivingDlgViewModel.kSeparator + "\"" + @@ -263,7 +289,7 @@ public void GetSourceFilesForMetsData_ListMultipleFiles_ReturnsCorrectMetsData() RampArchivingDlgViewModel.kSeparator + "\"" + RampArchivingDlgViewModel.kFileRelationship + "\":\"" + RampArchivingDlgViewModel.kRelationshipSource + "\"", - _helper.GetSourceFilesForMetsData(fileLists).ElementAt(1)); + m_model.GetSourceFilesForMetsData(fileLists).ElementAt(1)); Assert.AreEqual("\"" + RampArchivingDlgViewModel.kDefaultKey + "\":\"__AppSpecific__person-id_blah.person\"" + RampArchivingDlgViewModel.kSeparator + "\"" + @@ -271,7 +297,7 @@ public void GetSourceFilesForMetsData_ListMultipleFiles_ReturnsCorrectMetsData() RampArchivingDlgViewModel.kSeparator + "\"" + RampArchivingDlgViewModel.kFileRelationship + "\":\"" + RampArchivingDlgViewModel.kRelationshipSource + "\"", - _helper.GetSourceFilesForMetsData(fileLists).ElementAt(2)); + m_model.GetSourceFilesForMetsData(fileLists).ElementAt(2)); Assert.AreEqual("\"" + RampArchivingDlgViewModel.kDefaultKey + "\":\"__AppSpecific__person-id_baa.mpg\"" + RampArchivingDlgViewModel.kSeparator + "\"" + @@ -279,7 +305,7 @@ public void GetSourceFilesForMetsData_ListMultipleFiles_ReturnsCorrectMetsData() RampArchivingDlgViewModel.kSeparator + "\"" + RampArchivingDlgViewModel.kFileRelationship + "\":\"" + RampArchivingDlgViewModel.kRelationshipSource + "\"", - _helper.GetSourceFilesForMetsData(fileLists).ElementAt(3)); + m_model.GetSourceFilesForMetsData(fileLists).ElementAt(3)); Assert.AreEqual("\"" + RampArchivingDlgViewModel.kDefaultKey + "\":\"__AppSpecific__person-id_baa.mpg.meta\"" + RampArchivingDlgViewModel.kSeparator + "\"" + @@ -287,7 +313,7 @@ public void GetSourceFilesForMetsData_ListMultipleFiles_ReturnsCorrectMetsData() RampArchivingDlgViewModel.kSeparator + "\"" + RampArchivingDlgViewModel.kFileRelationship + "\":\"" + RampArchivingDlgViewModel.kRelationshipSource + "\"", - _helper.GetSourceFilesForMetsData(fileLists).ElementAt(4)); + m_model.GetSourceFilesForMetsData(fileLists).ElementAt(4)); } #endregion @@ -296,9 +322,9 @@ public void GetSourceFilesForMetsData_ListMultipleFiles_ReturnsCorrectMetsData() [Test] public void SetAudience_ChangeAudience_ThrowsInvalidOperationException() { - _helper.SetAudience(AudienceType.Vernacular); + m_model.SetAudience(AudienceType.Vernacular); Assert.Throws( - () => _helper.SetAudience(AudienceType.Training) + () => m_model.SetAudience(AudienceType.Training) ); } #endregion @@ -308,9 +334,9 @@ public void SetAudience_ChangeAudience_ThrowsInvalidOperationException() [Test] public void SetVernacularMaterialsAndContentType_IncompatibleWithAudience_ThrowsInvalidOperationException() { - _helper.SetAudience(AudienceType.Training); + m_model.SetAudience(AudienceType.Training); Assert.Throws( - () => _helper.SetVernacularMaterialsAndContentType(VernacularMaterialsType.BibleBackground) + () => m_model.SetVernacularMaterialsAndContentType(VernacularMaterialsType.BibleBackground) ); } @@ -318,9 +344,9 @@ public void SetVernacularMaterialsAndContentType_IncompatibleWithAudience_Throws [Test] public void SetVernacularMaterialsAndContentType_CompatibleWithAudience_IncludedInMetsData() { - _helper.SetAudience(AudienceType.Vernacular); - _helper.SetVernacularMaterialsAndContentType(VernacularMaterialsType.LiteracyEducation_Riddles); - var data = _helper.GetMetadata(); + m_model.SetAudience(AudienceType.Vernacular); + m_model.SetVernacularMaterialsAndContentType(VernacularMaterialsType.LiteracyEducation_Riddles); + var data = m_model.GetMetadata(); Assert.AreEqual("{\"dc.title\":\"Test Title\",\"" + RampArchivingDlgViewModel.kAudience + "\":\"" + RampArchivingDlgViewModel.kAudienceVernacular + "\",\"" + RampArchivingDlgViewModel.kVernacularMaterialsType + "\":\"" + RampArchivingDlgViewModel.kVernacularMaterialGeneral + "\",\"" + @@ -333,7 +359,7 @@ public void SetVernacularMaterialsAndContentType_CompatibleWithAudience_Included public void SetVernacularMaterialsAndContentType_MixOfScriptureAndOther_ThrowsArgumentException() { Assert.Throws( - () => _helper.SetVernacularMaterialsAndContentType(VernacularMaterialsType.BibleStory | VernacularMaterialsType.CommunityAndCulture_Calendar) + () => m_model.SetVernacularMaterialsAndContentType(VernacularMaterialsType.BibleStory | VernacularMaterialsType.CommunityAndCulture_Calendar) ); } #endregion @@ -343,8 +369,8 @@ public void SetVernacularMaterialsAndContentType_MixOfScriptureAndOther_ThrowsAr [Test] public void SetAbstract_SetSingleAbstractWithoutLanguage_IncludedInMetsData() { - _helper.SetAbstract("SayMore doesn't let the user specify the language explicitly.", string.Empty); - var data = _helper.GetMetadata(); + m_model.SetAbstract("SayMore doesn't let the user specify the language explicitly.", string.Empty); + var data = m_model.GetMetadata(); Assert.AreEqual("{\"dc.title\":\"Test Title\"," + "\"description.abstract.has\":\"Y\",\"dc.description.abstract\":{" + "\"0\":{\" \":\"SayMore doesn't let the user specify the language explicitly.\"}}}", @@ -360,12 +386,12 @@ public void SetAbstract_SetSingleAbstractWithoutLanguage_IncludedInMetsData() [Test] public void SetAbstract_SetTwice_ThrowsInvalidOperationException() { - _helper.SetAbstract("This is pretty abstract", "eng"); + m_model.SetAbstract("This is pretty abstract", "eng"); Dictionary foreignLanguageAbstracts = new Dictionary(); foreignLanguageAbstracts["fra"] = "C'est assez abstrait"; foreignLanguageAbstracts["spa"] = "Esto es bastante abstracto"; Assert.Throws( - () => _helper.SetAbstract(foreignLanguageAbstracts) + () => m_model.SetAbstract(foreignLanguageAbstracts) ); } @@ -373,7 +399,7 @@ public void SetAbstract_SetTwice_ThrowsInvalidOperationException() [Test] public void SetAbstract_Null_ThrowsArgumentNullException() { - Assert.Throws(() => _helper.SetAbstract(null)); + Assert.Throws(() => m_model.SetAbstract(null)); } /// ------------------------------------------------------------------------------------ @@ -384,8 +410,8 @@ public void SetAbstract_ThreeLanguages_IncludedInMetsData() abstracts["eng"] = "This is pretty abstract"; abstracts["fra"] = "C'est assez abstrait"; abstracts["spa"] = "Esto es bastante abstracto"; - _helper.SetAbstract(abstracts); - var data = _helper.GetMetadata(); + m_model.SetAbstract(abstracts); + var data = m_model.GetMetadata(); Assert.AreEqual("{\"dc.title\":\"Test Title\"," + "\"description.abstract.has\":\"Y\",\"dc.description.abstract\":{" + "\"0\":{\" \":\"This is pretty abstract\",\"lang\":\"eng\"}," + @@ -400,8 +426,8 @@ public void SetAbstract_ThreeLanguages_IncludedInMetsData() [Test] public void SetAudioVideoExtent_FreeFormString_IncludedInMetsData() { - _helper.SetAudioVideoExtent("6 and a half seconds"); - var data = _helper.GetMetadata(); + m_model.SetAudioVideoExtent("6 and a half seconds"); + var data = m_model.GetMetadata(); Assert.AreEqual("{\"dc.title\":\"Test Title\",\"" + RampArchivingDlgViewModel.kRecordingExtent + "\":\"6 and a half seconds\"}", data); @@ -412,8 +438,8 @@ public void SetAudioVideoExtent_FreeFormString_IncludedInMetsData() public void SetAudioVideoExtent_ValidTimeSpan_IncludedInMetsData() { TimeSpan duration = new TimeSpan(0, 2, 3, 4); - _helper.SetAudioVideoExtent(duration); - var data = _helper.GetMetadata(); + m_model.SetAudioVideoExtent(duration); + var data = m_model.GetMetadata(); Assert.AreEqual("{\"dc.title\":\"Test Title\",\"" + RampArchivingDlgViewModel.kRecordingExtent + "\":\"02:03:04\"}", data); @@ -423,9 +449,9 @@ public void SetAudioVideoExtent_ValidTimeSpan_IncludedInMetsData() [Test] public void SetAudioVideoExtent_SetTwice_ThrowsInvalidOperationException() { - _helper.SetAudioVideoExtent("twelve years or more"); + m_model.SetAudioVideoExtent("twelve years or more"); TimeSpan duration = new TimeSpan(0, 2, 3, 4); - Assert.Throws(() => _helper.SetAudioVideoExtent(duration)); + Assert.Throws(() => m_model.SetAudioVideoExtent(duration)); } #endregion @@ -440,8 +466,8 @@ public void SetContentLanguages_TwoLanguages_IncludedInMetsData() Assert.Ignore("This test is no longer valid because RAMP 3.0 does not have a languages file"); - _helper.SetContentLanguages("eng", "fra"); - var data = _helper.GetMetadata(); + m_model.SetContentLanguages("eng", "fra"); + var data = m_model.GetMetadata(); Assert.AreEqual("{\"dc.title\":\"Test Title\",\"" + RampArchivingDlgViewModel.kContentLanguages + "\":{\"0\":{\" \":\"eng:English\"},\"1\":{\" \":\"fra:French\"}}}", data); @@ -455,8 +481,8 @@ public void SetContentLanguages_SetTwice_ThrowsInvalidOperationException() { IgnoreTestIfRampIsNotInstalled(); - _helper.SetContentLanguages("eng", "fra"); - Assert.Throws(() => _helper.SetContentLanguages("spa", "fra")); + m_model.SetContentLanguages("eng", "fra"); + Assert.Throws(() => m_model.SetContentLanguages("spa", "fra")); } #endregion @@ -465,17 +491,17 @@ public void SetContentLanguages_SetTwice_ThrowsInvalidOperationException() [Test] public void SetContributors_Null_ThrowsArgumentNullException() { - Assert.Throws(() => _helper.SetContributors(null)); + Assert.Throws(() => m_model.SetContributors(null)); } /// ------------------------------------------------------------------------------------ [Test] public void SetContributors_Empty_NoChangeToMetsData() { - var dataBefore = _helper.GetMetadata(); + var dataBefore = m_model.GetMetadata(); var empty = new ContributionCollection(); - _helper.SetContributors(empty); - var dataAfter = _helper.GetMetadata(); + m_model.SetContributors(empty); + var dataAfter = m_model.GetMetadata(); Assert.AreEqual(dataBefore, dataAfter); } @@ -487,8 +513,8 @@ public void SetContributors_TwoContributors_IncludedInMetsData() OlacSystem olacSystem = new OlacSystem(); contributors.Add(new Contribution("Erkel", olacSystem.GetRoleByCodeOrThrow("author"))); contributors.Add(new Contribution("Sungfu", olacSystem.GetRoleByCodeOrThrow("recorder"))); - _helper.SetContributors(contributors); - var data = _helper.GetMetadata(); + m_model.SetContributors(contributors); + var data = m_model.GetMetadata(); Assert.AreEqual("{\"dc.title\":\"Test Title\",\"" + RampArchivingDlgViewModel.kContributor + "\":{\"0\":{\" \":\"Erkel\",\"role\":\"author\"},\"1\":{\" \":\"Sungfu\",\"role\":\"recorder\"}}}", data); @@ -503,8 +529,8 @@ public void SetContributors_SetTwice_ThrowsInvalidOperationException() Role role = olacSystem.GetRoleByCodeOrThrow("author"); var contrib = new Contribution("Erkel", role); contributors.Add(contrib); - _helper.SetContributors(contributors); - Assert.Throws(() => _helper.SetContributors(contributors)); + m_model.SetContributors(contributors); + Assert.Throws(() => m_model.SetContributors(contributors)); } #endregion @@ -513,8 +539,8 @@ public void SetContributors_SetTwice_ThrowsInvalidOperationException() [Test] public void SetCreationDate_FreeFormString_IncludedInMetsData() { - _helper.SetCreationDate("four years ago"); - var data = _helper.GetMetadata(); + m_model.SetCreationDate("four years ago"); + var data = m_model.GetMetadata(); Assert.AreEqual("{\"dc.title\":\"Test Title\",\"" + RampArchivingDlgViewModel.kDateCreated + "\":\"four years ago\"}", data); @@ -525,8 +551,8 @@ public void SetCreationDate_FreeFormString_IncludedInMetsData() public void SetCreationDate_ValidTimeSpan_IncludedInMetsData() { DateTime creationDate = new DateTime(2012, 4, 13); - _helper.SetCreationDate(creationDate); - var data = _helper.GetMetadata(); + m_model.SetCreationDate(creationDate); + var data = m_model.GetMetadata(); Assert.AreEqual("{\"dc.title\":\"Test Title\",\"" + RampArchivingDlgViewModel.kDateCreated + "\":\"2012-04-13\"}", data); @@ -536,8 +562,8 @@ public void SetCreationDate_ValidTimeSpan_IncludedInMetsData() [Test] public void SetCreationDate_SetTwice_ThrowsInvalidOperationException() { - _helper.SetCreationDate("tomorrow"); - Assert.Throws(() => _helper.SetCreationDate(new DateTime(2012, 4, 13))); + m_model.SetCreationDate("tomorrow"); + Assert.Throws(() => m_model.SetCreationDate(new DateTime(2012, 4, 13))); } #endregion @@ -546,8 +572,8 @@ public void SetCreationDate_SetTwice_ThrowsInvalidOperationException() [Test] public void SetDatasetExtent_FreeFormString_IncludedInMetsData() { - _helper.SetDatasetExtent("6 voice records and maybe an odd text file or two"); - var data = _helper.GetMetadata(); + m_model.SetDatasetExtent("6 voice records and maybe an odd text file or two"); + var data = m_model.GetMetadata(); Assert.AreEqual("{\"dc.title\":\"Test Title\",\"" + RampArchivingDlgViewModel.kDatasetExtent + "\":\"6 voice records and maybe an odd text file or two\"}", data); @@ -557,8 +583,8 @@ public void SetDatasetExtent_FreeFormString_IncludedInMetsData() [Test] public void SetDatasetExtent_SetTwice_ThrowsInvalidOperationException() { - _helper.SetDatasetExtent("practically nothing"); - Assert.Throws(() => _helper.SetDatasetExtent("lots of data")); + m_model.SetDatasetExtent("practically nothing"); + Assert.Throws(() => m_model.SetDatasetExtent("lots of data")); } #endregion @@ -567,16 +593,16 @@ public void SetDatasetExtent_SetTwice_ThrowsInvalidOperationException() [Test] public void SetDescription_Null_ThrowsArgumentNullException() { - Assert.Throws(() => _helper.SetDescription(null)); + Assert.Throws(() => m_model.SetDescription(null)); } /// ------------------------------------------------------------------------------------ [Test] public void SetDescription_Empty_NoChangeToMetsData() { - var dataBefore = _helper.GetMetadata(); - _helper.SetDescription(new Dictionary()); - var dataAfter = _helper.GetMetadata(); + var dataBefore = m_model.GetMetadata(); + m_model.SetDescription(new Dictionary()); + var dataAfter = m_model.GetMetadata(); Assert.AreEqual(dataBefore, dataAfter); } @@ -587,8 +613,8 @@ public void SetDescription_TwoLanguages_IncludedInMetsData() var descriptions = new Dictionary(); descriptions["eng"] = "General data"; descriptions["spa"] = "Datos generales"; - _helper.SetDescription(descriptions); - var data = _helper.GetMetadata(); + m_model.SetDescription(descriptions); + var data = m_model.GetMetadata(); Assert.AreEqual("{\"dc.title\":\"Test Title\",\"" + RampArchivingDlgViewModel.kFlagHasGeneralDescription + "\":\"Y\",\"" + RampArchivingDlgViewModel.kGeneralDescription + "\":{\"0\":{\" \":\"General data\",\"lang\":\"eng\"},\"1\":{\" \":\"Datos generales\",\"lang\":\"spa\"}}}", data); @@ -600,8 +626,8 @@ public void SetDescription_SetTwice_ThrowsInvalidOperationException() { var descriptions = new Dictionary(); descriptions["eng"] = "General data"; - _helper.SetDescription(descriptions); - Assert.Throws(() => _helper.SetDescription(descriptions)); + m_model.SetDescription(descriptions); + Assert.Throws(() => m_model.SetDescription(descriptions)); } #endregion @@ -626,7 +652,7 @@ public void GetLanguageName_English_ReturnsEnglish() Assert.Ignore("This test is no longer valid because RAMP 3.0 does not have a languages file"); - var langName = _helper.GetLanguageName("eng"); + var langName = m_model.GetLanguageName("eng"); Assert.AreEqual(langName, "English"); } @@ -638,7 +664,7 @@ public void GetLanguageName_Gibberish_ReturnsNull() { IgnoreTestIfRampIsNotInstalled(); - var langName = _helper.GetLanguageName("z23"); + var langName = m_model.GetLanguageName("z23"); Assert.IsNull(langName); } @@ -654,8 +680,8 @@ public void GetLanguageName_ArchivingLanguage_ReturnsCorrectName() // FieldWorks associates the name "Chinese" with the ISO3 Code "cmn" ArchivingLanguage lang = new ArchivingLanguage("cmn", "Chinese"); - // RAMP reqires the name "Chinese, Mandarin" - Assert.AreEqual("Chinese, Mandarin", _helper.GetLanguageName(lang.Iso3Code)); + // RAMP requires the name "Chinese, Mandarin" + Assert.AreEqual("Chinese, Mandarin", m_model.GetLanguageName(lang.Iso3Code)); } /// ------------------------------------------------------------------------------------ @@ -672,9 +698,9 @@ public void GetRAMPFileLocation_RAMPInstalled_ReturnsFileLocation() #region Private helper methods /// ------------------------------------------------------------------------------------ - private void SetFilesToArchive(ArchivingDlgViewModel model) + private void SetFilesToArchive(ArchivingDlgViewModel model, CancellationToken cancellationToken) { - foreach (var kvp in _filesToAdd) + foreach (var kvp in m_filesToAdd) model.AddFileGroup(kvp.Key, kvp.Value.Item1, kvp.Value.Item2); } @@ -702,14 +728,11 @@ private void CustomFilenameNormalization(string key, string file, StringBuilder private void IgnoreTestIfRampIsNotInstalled() { - if (!_isRampInstalled.HasValue) - { - // we remember the value so that we check only once. This won't change within - // a test run. - _isRampInstalled = !string.IsNullOrEmpty(RampArchivingDlgViewModel.GetExeFileLocation()); - } + // we remember the value so that we check only once. This won't change within + // a test run. + m_isRampInstalled ??= !string.IsNullOrEmpty(RampArchivingDlgViewModel.GetExeFileLocation()); - if (!_isRampInstalled.Value) + if (!m_isRampInstalled.Value) Assert.Ignore("This test requires RAMP"); } #endregion diff --git a/SIL.Archiving.Tests/SIL.Archiving.Tests.csproj b/SIL.Archiving.Tests/SIL.Archiving.Tests.csproj index 6a4af6349..918907f46 100644 --- a/SIL.Archiving.Tests/SIL.Archiving.Tests.csproj +++ b/SIL.Archiving.Tests/SIL.Archiving.Tests.csproj @@ -22,7 +22,6 @@ - \ No newline at end of file diff --git a/SIL.Archiving.Tests/SimpleObjectTests.cs b/SIL.Archiving.Tests/SimpleObjectTests.cs index 8ceca674c..bb9082c86 100644 --- a/SIL.Archiving.Tests/SimpleObjectTests.cs +++ b/SIL.Archiving.Tests/SimpleObjectTests.cs @@ -35,8 +35,8 @@ public void LanguageStringComparer_CorrectlyComparesLanguageStrings() [Test] public void ToLatinOnly_MultiplePeriod_OnePeriod() { - var result = "Langauge.wav.meta".ToLatinOnly("_", "+", "."); - Assert.AreEqual("Langauge_wav.meta", result); + var result = "Language.wav.meta".ToLatinOnly("_", "+", "."); + Assert.AreEqual("Language_wav.meta", result); } } } diff --git a/SIL.Archiving.Tests/TestProgress.cs b/SIL.Archiving.Tests/TestProgress.cs new file mode 100644 index 000000000..d94d32ffb --- /dev/null +++ b/SIL.Archiving.Tests/TestProgress.cs @@ -0,0 +1,23 @@ +namespace SIL.Archiving.Tests +{ + internal class TestProgress : IArchivingProgressDisplay + { + public int Step { get; private set; } = 0; + public string ArchiveTypeName { get; } + + internal TestProgress(string type) + { + ArchiveTypeName = $"{type} (Test)"; + } + + public void IncrementProgress() + { + Step++; + } + + public string GetMessage(ArchivingDlgViewModel.StringId msgId) + { + return $"Test implementation message for {msgId}"; + } + } +} diff --git a/SIL.Archiving/App.config b/SIL.Archiving/App.config new file mode 100644 index 000000000..4bfe07ada --- /dev/null +++ b/SIL.Archiving/App.config @@ -0,0 +1,21 @@ + + + + +
+ + + + + + http://www.mpi.nl/imdi/ + + + https://gateway.sil.org/display/RH/RAMP+Users%27+Manual + + + author;compiler;consultant;developer;editor;facilitator;illustrator;interviewer;photographer;recorder;researcher;signer;speaker;transcriber;translator + + + + \ No newline at end of file diff --git a/SIL.Archiving/ArchivingDlg.Designer.cs b/SIL.Archiving/ArchivingDlg.Designer.cs deleted file mode 100755 index 22d2888de..000000000 --- a/SIL.Archiving/ArchivingDlg.Designer.cs +++ /dev/null @@ -1,257 +0,0 @@ -using SIL.Windows.Forms.Progress; - -namespace SIL.Archiving -{ - partial class ArchivingDlg - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.components = new System.ComponentModel.Container(); - this._tableLayoutPanel = new System.Windows.Forms.TableLayoutPanel(); - this._buttonCreatePackage = new System.Windows.Forms.Button(); - this._linkOverview = new System.Windows.Forms.LinkLabel(); - this._progressBar = new System.Windows.Forms.ProgressBar(); - this._logBox = new SIL.Windows.Forms.Progress.LogBox(); - this._buttonLaunchRamp = new System.Windows.Forms.Button(); - this._buttonCancel = new System.Windows.Forms.Button(); - this._flowLayoutExtra = new System.Windows.Forms.FlowLayoutPanel(); - this.locExtender = new L10NSharp.UI.L10NSharpExtender(this.components); - this._chkMetadataOnly = new System.Windows.Forms.CheckBox(); - this._tableLayoutPanel.SuspendLayout(); - this._flowLayoutExtra.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.locExtender)).BeginInit(); - this.SuspendLayout(); - // - // _tableLayoutPanel - // - this._tableLayoutPanel.ColumnCount = 3; - this._tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); - this._tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); - this._tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); - this._tableLayoutPanel.Controls.Add(this._buttonCreatePackage, 0, 4); - this._tableLayoutPanel.Controls.Add(this._linkOverview, 0, 0); - this._tableLayoutPanel.Controls.Add(this._progressBar, 0, 2); - this._tableLayoutPanel.Controls.Add(this._logBox, 0, 1); - this._tableLayoutPanel.Controls.Add(this._buttonLaunchRamp, 1, 4); - this._tableLayoutPanel.Controls.Add(this._buttonCancel, 2, 4); - this._tableLayoutPanel.Controls.Add(this._flowLayoutExtra, 0, 3); - this._tableLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill; - this._tableLayoutPanel.Location = new System.Drawing.Point(12, 12); - this._tableLayoutPanel.Name = "_tableLayoutPanel"; - this._tableLayoutPanel.RowCount = 5; - this._tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle()); - this._tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); - this._tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle()); - this._tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle()); - this._tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle()); - this._tableLayoutPanel.Size = new System.Drawing.Size(355, 408); - this._tableLayoutPanel.TabIndex = 0; - // - // _buttonCreatePackage - // - this._buttonCreatePackage.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this._buttonCreatePackage.AutoSize = true; - this.locExtender.SetLocalizableToolTip(this._buttonCreatePackage, null); - this.locExtender.SetLocalizationComment(this._buttonCreatePackage, null); - this.locExtender.SetLocalizingId(this._buttonCreatePackage, "DialogBoxes.ArchivingDlg._buttonCreatePackage"); - this._buttonCreatePackage.Location = new System.Drawing.Point(58, 382); - this._buttonCreatePackage.Margin = new System.Windows.Forms.Padding(8, 0, 0, 0); - this._buttonCreatePackage.Name = "_buttonCreatePackage"; - this._buttonCreatePackage.Size = new System.Drawing.Size(106, 26); - this._buttonCreatePackage.TabIndex = 0; - this._buttonCreatePackage.Text = "&1) Create Package"; - this._buttonCreatePackage.UseVisualStyleBackColor = true; - this._buttonCreatePackage.Click += new System.EventHandler(this.CreatePackage); - - // - // _linkOverview - // - this._linkOverview.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this._linkOverview.AutoSize = true; - this._tableLayoutPanel.SetColumnSpan(this._linkOverview, 3); - this._linkOverview.LinkArea = new System.Windows.Forms.LinkArea(0, 0); - this.locExtender.SetLocalizableToolTip(this._linkOverview, null); - this.locExtender.SetLocalizationComment(this._linkOverview, ""); - this.locExtender.SetLocalizationPriority(this._linkOverview, L10NSharp.LocalizationPriority.NotLocalizable); - this.locExtender.SetLocalizingId(this._linkOverview, "DialogBoxes.ArchivingDlg.OverviewText"); - this._linkOverview.Location = new System.Drawing.Point(3, 0); - this._linkOverview.Name = "_linkOverview"; - this._linkOverview.Size = new System.Drawing.Size(349, 13); - this._linkOverview.TabIndex = 3; - this._linkOverview.Text = "#"; - this._linkOverview.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.HandleRampLinkClicked); - // - // _progressBar - // - this._progressBar.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this._tableLayoutPanel.SetColumnSpan(this._progressBar, 3); - this._progressBar.Location = new System.Drawing.Point(0, 326); - this._progressBar.Margin = new System.Windows.Forms.Padding(0, 0, 0, 12); - this._progressBar.Name = "_progressBar"; - this._progressBar.Size = new System.Drawing.Size(355, 15); - this._progressBar.TabIndex = 4; - // - // _logBox - // - this._logBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this._logBox.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; - this._logBox.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(171)))), ((int)(((byte)(173)))), ((int)(((byte)(179))))); - this._logBox.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; - this._logBox.CancelRequested = false; - this._tableLayoutPanel.SetColumnSpan(this._logBox, 3); - this._logBox.ErrorEncountered = false; - this._logBox.Font = new System.Drawing.Font("Segoe UI", 9F); - this._logBox.GetDiagnosticsMethod = null; - this.locExtender.SetLocalizableToolTip(this._logBox, null); - this.locExtender.SetLocalizationComment(this._logBox, null); - this.locExtender.SetLocalizingId(this._logBox, "LogBox"); - this._logBox.Location = new System.Drawing.Point(0, 18); - this._logBox.Margin = new System.Windows.Forms.Padding(0, 5, 0, 5); - this._logBox.Name = "_logBox"; - this._logBox.ProgressIndicator = null; - this._logBox.ShowCopyToClipboardMenuItem = false; - this._logBox.ShowDetailsMenuItem = false; - this._logBox.ShowDiagnosticsMenuItem = false; - this._logBox.ShowFontMenuItem = false; - this._logBox.ShowMenu = false; - this._logBox.Size = new System.Drawing.Size(355, 303); - this._logBox.TabIndex = 5; - this._logBox.TabStop = false; - this._logBox.ReportErrorLinkClicked += new System.EventHandler(this.HandleLogBoxReportErrorLinkClicked); - // - // _buttonLaunchRamp - // - this._buttonLaunchRamp.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this._buttonLaunchRamp.AutoSize = true; - this._buttonLaunchRamp.DialogResult = System.Windows.Forms.DialogResult.OK; - this.locExtender.SetLocalizableToolTip(this._buttonLaunchRamp, null); - this.locExtender.SetLocalizationComment(this._buttonLaunchRamp, null); - this.locExtender.SetLocalizingId(this._buttonLaunchRamp, "DialogBoxes.ArchivingDlg._buttonLaunchRamp"); - this._buttonLaunchRamp.Location = new System.Drawing.Point(172, 382); - this._buttonLaunchRamp.Margin = new System.Windows.Forms.Padding(8, 0, 0, 0); - this._buttonLaunchRamp.Name = "_buttonLaunchRamp"; - this._buttonLaunchRamp.Size = new System.Drawing.Size(100, 26); - this._buttonLaunchRamp.TabIndex = 1; - this._buttonLaunchRamp.Text = "&2) Launch {0}"; - this._buttonLaunchRamp.UseVisualStyleBackColor = true; - // - // _buttonCancel - // - this._buttonCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this._buttonCancel.AutoSize = true; - this._buttonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.locExtender.SetLocalizableToolTip(this._buttonCancel, null); - this.locExtender.SetLocalizationComment(this._buttonCancel, null); - this.locExtender.SetLocalizingId(this._buttonCancel, "DialogBoxes.ArchivingDlg._buttonCancel"); - this._buttonCancel.Location = new System.Drawing.Point(280, 382); - this._buttonCancel.Margin = new System.Windows.Forms.Padding(8, 0, 0, 0); - this._buttonCancel.Name = "_buttonCancel"; - this._buttonCancel.Size = new System.Drawing.Size(75, 26); - this._buttonCancel.TabIndex = 2; - this._buttonCancel.Text = "Cancel"; - this._buttonCancel.UseVisualStyleBackColor = true; - // - // _flowLayoutExtra - // - this._flowLayoutExtra.AutoSize = true; - this._flowLayoutExtra.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; - this._tableLayoutPanel.SetColumnSpan(this._flowLayoutExtra, 3); - this._flowLayoutExtra.Controls.Add(this._chkMetadataOnly); - this._flowLayoutExtra.Dock = System.Windows.Forms.DockStyle.Top; - this._flowLayoutExtra.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; - this._flowLayoutExtra.Location = new System.Drawing.Point(3, 356); - this._flowLayoutExtra.Name = "_flowLayoutExtra"; - this._flowLayoutExtra.Size = new System.Drawing.Size(349, 23); - this._flowLayoutExtra.TabIndex = 6; - // - // locExtender - // - this.locExtender.LocalizationManagerId = "SIL.Archiving"; - this.locExtender.PrefixForNewItems = null; - // - // _chkMetadataOnly - // - this._chkMetadataOnly.AutoSize = true; - this.locExtender.SetLocalizableToolTip(this._chkMetadataOnly, "Select this to prevent copying actual data files into archive"); - this.locExtender.SetLocalizationComment(this._chkMetadataOnly, null); - this.locExtender.SetLocalizingId(this._chkMetadataOnly, "ArchivingDlg._chkMetadataOnly"); - this._chkMetadataOnly.Location = new System.Drawing.Point(3, 3); - this._chkMetadataOnly.Name = "_chkMetadataOnly"; - this._chkMetadataOnly.Size = new System.Drawing.Size(93, 17); - this._chkMetadataOnly.TabIndex = 0; - this._chkMetadataOnly.Text = "Metadata only"; - this._chkMetadataOnly.UseVisualStyleBackColor = true; - this._chkMetadataOnly.Visible = false; - this._chkMetadataOnly.CheckedChanged += new System.EventHandler(this._chkMetadataOnly_CheckedChanged); - // - // ArchivingDlg - // - this.AcceptButton = this._buttonLaunchRamp; - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.CancelButton = this._buttonCancel; - this.ClientSize = new System.Drawing.Size(379, 432); - this.Controls.Add(this._tableLayoutPanel); - this.locExtender.SetLocalizableToolTip(this, null); - this.locExtender.SetLocalizationComment(this, "Parameter is application name"); - this.locExtender.SetLocalizingId(this, "DialogBoxes.ArchivingDlg.WindowTitle"); - this.MaximizeBox = false; - this.MinimizeBox = false; - this.MinimumSize = new System.Drawing.Size(355, 320); - this.Name = "ArchivingDlg"; - this.Padding = new System.Windows.Forms.Padding(12); - this.ShowIcon = false; - this.ShowInTaskbar = false; - this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; - this.Text = "{0}: Archive using {1}"; - this._tableLayoutPanel.ResumeLayout(false); - this._tableLayoutPanel.PerformLayout(); - this._flowLayoutExtra.ResumeLayout(false); - this._flowLayoutExtra.PerformLayout(); - ((System.ComponentModel.ISupportInitialize)(this.locExtender)).EndInit(); - this.ResumeLayout(false); - - } - - #endregion - - private System.Windows.Forms.LinkLabel _linkOverview; - private System.Windows.Forms.ProgressBar _progressBar; - private L10NSharp.UI.L10NSharpExtender locExtender; - private LogBox _logBox; - protected System.Windows.Forms.TableLayoutPanel _tableLayoutPanel; - protected System.Windows.Forms.FlowLayoutPanel _flowLayoutExtra; - protected System.Windows.Forms.Button _buttonLaunchRamp; - protected System.Windows.Forms.Button _buttonCreatePackage; - protected System.Windows.Forms.Button _buttonCancel; - private System.Windows.Forms.CheckBox _chkMetadataOnly; - } -} \ No newline at end of file diff --git a/SIL.Archiving/ArchivingDlg.cs b/SIL.Archiving/ArchivingDlg.cs deleted file mode 100644 index 5ef1fce1e..000000000 --- a/SIL.Archiving/ArchivingDlg.cs +++ /dev/null @@ -1,237 +0,0 @@ -using System; -using System.Drawing; -using System.Windows.Forms; -using SIL.Windows.Forms; -using SIL.Windows.Forms.Miscellaneous; -using SIL.Windows.Forms.PortableSettingsProvider; - -namespace SIL.Archiving -{ - /// ---------------------------------------------------------------------------------------- - public partial class ArchivingDlg : Form - { - private readonly FormSettings _settings; - protected readonly ArchivingDlgViewModel _viewModel; - protected readonly string _launchButtonTextFormat; - - /// ------------------------------------------------------------------------------------ - /// Caller can use this to retrieve and persist form settings (typically - /// after form is closed). - /// ------------------------------------------------------------------------------------ - public FormSettings FormSettings - { - get { return _settings; } - } - - /// ------------------------------------------------------------------------------------ - /// View model - /// The ID of the localization manager for the - /// calling application. - /// Application can set this to ensure a consistent look - /// in the UI (especially useful for when a localization requires a particular font). - /// Location, size, and state where the client would like the - /// dialog box to appear (can be null) - /// ------------------------------------------------------------------------------------ - public ArchivingDlg(ArchivingDlgViewModel model, string localizationManagerId, - Font programDialogFont, FormSettings settings) - { - _settings = settings ?? FormSettings.Create(this); - - _viewModel = model; - - InitializeComponent(); - - if (!string.IsNullOrEmpty(localizationManagerId)) - locExtender.LocalizationManagerId = localizationManagerId; - - Text = string.Format(Text, model.AppName, model.ArchiveType); - _progressBar.Visible = false; - - // remember this because we will need it later in a derived class - _launchButtonTextFormat = _buttonLaunchRamp.Text; - - UpdateLaunchButtonText(); - _buttonLaunchRamp.Enabled = false; //!string.IsNullOrEmpty(model.PathToProgramToLaunch); - _chkMetadataOnly.Visible = _viewModel is ISupportMetadataOnly; - - _linkOverview.Text = model.InformativeText; - _linkOverview.Links.Clear(); - - if (!string.IsNullOrEmpty(model.ArchiveInfoUrl) && !string.IsNullOrEmpty(model.ArchiveInfoHyperlinkText)) - { - int i = _linkOverview.Text.IndexOf(model.ArchiveInfoHyperlinkText, StringComparison.InvariantCulture); - if (i >= 0) - _linkOverview.Links.Add(i, model.ArchiveInfoHyperlinkText.Length, model.ArchiveInfoUrl); - } - - // this is for a display problem in mono - _linkOverview.SizeToContents(); - _logBox.Tag = false; - - model.OnDisplayMessage += DisplayMessage; - model.OnDisplayError += new ArchivingDlgViewModel.DisplayErrorEventHandler(model_DisplayError); - - if (programDialogFont != null) - { - _linkOverview.Font = programDialogFont; - _logBox.Font = FontHelper.MakeFont(programDialogFont, FontStyle.Bold); - _buttonCancel.Font = programDialogFont; - _buttonCreatePackage.Font = programDialogFont; - _buttonLaunchRamp.Font = programDialogFont; - Font = programDialogFont; - } - - _buttonLaunchRamp.Click += (s, e) => model.LaunchArchivingProgram(); - - _buttonCancel.MouseLeave += delegate - { - if (model.IsBusy) - WaitCursor.Show(); - }; - - _buttonCancel.MouseEnter += delegate - { - if (model.IsBusy) - WaitCursor.Hide(); - }; - - _buttonCancel.Click += delegate - { - model.Cancel(); - WaitCursor.Hide(); - }; - } - - private void CreatePackage(object sender, EventArgs eventArgs) - { - Focus(); - DisableControlsDuringPackageCreation(); - _progressBar.Visible = true; - WaitCursor.Show(); - _logBox.Clear(); - _buttonLaunchRamp.Enabled = _viewModel.CreatePackage(); - _progressBar.Visible = false; - WaitCursor.Hide(); - } - - protected virtual void DisableControlsDuringPackageCreation() - { - _buttonCreatePackage.Enabled = false; - _chkMetadataOnly.Enabled = false; - } - - /// ------------------------------------------------------------------------------------ - protected void UpdateLaunchButtonText() - { - _buttonLaunchRamp.Text = string.Format(_launchButtonTextFormat, _viewModel.NameOfProgramToLaunch); - } - - /// ------------------------------------------------------------------------------------ - protected void UpdateOverviewText() - { - _linkOverview.Text = _viewModel.InformativeText; - } - - /// ------------------------------------------------------------------------------------ - void DisplayMessage(string msg, ArchivingDlgViewModel.MessageType type) - { - if ((bool) _logBox.Tag) - { - _logBox.Clear(); - _logBox.Tag = false; - } - switch (type) - { - case ArchivingDlgViewModel.MessageType.Normal: - _logBox.WriteMessage(msg); - break; - case ArchivingDlgViewModel.MessageType.Indented: - _logBox.WriteMessage(Environment.NewLine + " " + msg); - break; - case ArchivingDlgViewModel.MessageType.Detail: - _logBox.WriteMessageWithFontStyle(FontStyle.Regular, "\t" + msg); - break; - case ArchivingDlgViewModel.MessageType.Bullet: - _logBox.WriteMessageWithFontStyle(FontStyle.Regular, " \u00B7 {0}", msg); - break; - case ArchivingDlgViewModel.MessageType.Progress: - _logBox.WriteMessage(Environment.NewLine + msg); - break; - case ArchivingDlgViewModel.MessageType.Warning: - _logBox.WriteWarning(msg); - break; - case ArchivingDlgViewModel.MessageType.Error: - _logBox.WriteMessageWithColor("Red", msg + Environment.NewLine); - break; - case ArchivingDlgViewModel.MessageType.Success: - _logBox.WriteMessageWithColor(Color.DarkGreen, Environment.NewLine + msg); - break; - case ArchivingDlgViewModel.MessageType.Volatile: - _logBox.WriteMessage(msg); - _logBox.Tag = true; - break; - } - } - - /// ------------------------------------------------------------------------------------ - void model_DisplayError(string msg, string packageTitle, Exception e) - { - if (_logBox.IsHandleCreated) - { - WaitCursor.Hide(); - _logBox.WriteError(msg, packageTitle); - if (e != null) - _logBox.WriteException(e); - } - - } - - /// ------------------------------------------------------------------------------------ - protected override void OnLoad(EventArgs e) - { - _settings.InitializeForm(this); - base.OnLoad(e); - } - - /// ------------------------------------------------------------------------------------ - protected override void OnShown(EventArgs e) - { - base.OnShown(e); - - try - { - WaitCursor.Show(); - _viewModel.IncrementProgressBarAction = () => _progressBar.Increment(1); - _buttonCreatePackage.Enabled = _viewModel.Initialize(); - _logBox.ScrollToTop(); - _progressBar.Maximum = _viewModel.CalculateMaxProgressBarValue(); - WaitCursor.Hide(); - } - catch - { - WaitCursor.Hide(); - throw; - } - } - - /// ------------------------------------------------------------------------------------ - private void HandleRampLinkClicked(object sender, LinkLabelLinkClickedEventArgs e) - { - var tgt = e.Link.LinkData as string; - - if (!string.IsNullOrEmpty(tgt)) - System.Diagnostics.Process.Start(tgt); - } - - /// ------------------------------------------------------------------------------------ - private void HandleLogBoxReportErrorLinkClicked(object sender, EventArgs e) - { - Close(); - } - - private void _chkMetadataOnly_CheckedChanged(object sender, EventArgs e) - { - ((ISupportMetadataOnly)_viewModel).MetadataOnly = _chkMetadataOnly.Checked; - } - } -} diff --git a/SIL.Archiving/ArchivingDlgViewModel.cs b/SIL.Archiving/ArchivingDlgViewModel.cs index b9a388f03..3c50a99a4 100644 --- a/SIL.Archiving/ArchivingDlgViewModel.cs +++ b/SIL.Archiving/ArchivingDlgViewModel.cs @@ -1,22 +1,31 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; -using System.Windows.Forms; +using System.Threading; +using System.Threading.Tasks; using JetBrains.Annotations; -using L10NSharp; -using SIL.Archiving.Generic; +using SIL.Archiving.IMDI.Lists; using SIL.Code; +using SIL.EventsAndDelegates; using SIL.IO; +using static System.String; +// ReSharper disable InconsistentNaming namespace SIL.Archiving { /// ------------------------------------------------------------------------------------ - public abstract class ArchivingDlgViewModel + public abstract class ArchivingDlgViewModel : IDisposable { + public enum Standard + { + REAP, + IMDI, + Other, + } + [Flags] protected enum MetadataProperties { @@ -41,14 +50,12 @@ protected enum MetadataProperties } #region Data members - protected readonly string _id; // ID/Name of the top-level element being archived (can be either a session or a project) - protected readonly string _appSpecificArchivalProcessInfo; - protected readonly Dictionary _titles = new Dictionary(); //Titles of elements being archived (keyed by element id) + private readonly string _id; // ID/Name of the top-level element being archived (can be either a session or a project) + private readonly Dictionary _titles = new Dictionary(); //Titles of elements being archived (keyed by element id) private readonly Dictionary _propertiesSet = new Dictionary(); // Metadata properties that have been set (keyed by element id) - private readonly Action _setFilesToArchive; + private readonly Action _setFilesToArchive; + private readonly Dictionary, string>> _fileLists = new Dictionary, string>>(); - protected bool _cancelProcess; - protected readonly Dictionary _progressMessages = new Dictionary(); /// /// Keyed and grouped according to whatever logical grouping makes sense in the /// calling application. The key for each group will be supplied back to the calling app @@ -56,11 +63,31 @@ protected enum MetadataProperties /// enumerable list of files to include, and Item2 contains a progress message to be /// displayed when that group of files is being processed. /// - protected IDictionary, string>> _fileLists = new Dictionary, string>>(); - protected BackgroundWorker _worker; + protected IDictionary, string>> FileLists => _fileLists; #endregion #region Delegates and Events + /// ------------------------------------------------------------------------------------ + public enum StringId + { + PreArchivingStatus, + SearchingForArchiveUploadingProgram, + ArchiveUploadingProgramNotFound, + IMDIPackageInvalid, + ErrorStartingArchivalProgram, + PreparingFiles, + SavingFilesInPackage, + FileExcludedFromPackage, + PathNotWritable, + ReadyToCallRampMsg, + ErrorCreatingArchive, + IMDIActorsGroup, + CopyingFiles, + FailedToMakePackage, + ErrorCreatingMetsFile, + RampPackageRemoved + } + /// ------------------------------------------------------------------------------------ public enum MessageType { @@ -84,40 +111,48 @@ public enum MessageType Volatile, } - /// Delegate for OnDisplayMessage event + /// Delegate for event /// Message to display - /// Type of message (which handler can use to determine appropriate color, style, indentation, etc. - public delegate void DisplayMessageEventHandler(string msg, MessageType type); + /// Type of message (which handler can use to determine appropriate + /// presentation formatting to use). + public delegate void MessageEventHandler(string msg, MessageType type); /// - /// Notifiers subscribers of a message to display. + /// Notifies subscribers of a message to display. /// - public event DisplayMessageEventHandler OnDisplayMessage; + public event MessageEventHandler OnReportMessage; - /// Delegate for DisplayError event - /// Message to display + /// Delegate for event + /// Error message to display /// Title of package being created /// Exception (can be null) - public delegate void DisplayErrorEventHandler(string msg, string packageTitle, Exception e); + public delegate void ErrorEventHandler(string msg, string packageTitle, Exception e); /// /// Notifiers subscribers of an error message to report. /// - public event DisplayErrorEventHandler OnDisplayError; + public event ErrorEventHandler OnError; + + /// Delegate for event + /// Event args that hold the exception that occurred + public delegate void ExceptionHandler(EventArgs args); + + /// + /// Notifies subscribers of an exception thrown during launch of archive uploader program. + /// + public event ExceptionHandler OnExceptionDuringLaunch; - /// Action raised when progress happens - public Action IncrementProgressBarAction { protected get; set; } + protected IArchivingProgressDisplay Progress { get; private set; } #endregion #region properties + protected string PackageId => _id; + protected string PackageTitle => _titles[_id]; + /// ------------------------------------------------------------------------------------ - /// - /// Short name/description of the archiving program or standard used for this type of - /// archiving. (Should fit in the frame "Archive using ___".) - /// - /// ------------------------------------------------------------------------------------ - internal abstract string ArchiveType { get; } + public abstract Standard ArchiveType { get; } + /// ------------------------------------------------------------------------------------ /// /// Short name of the archiving program to launch once package is created. @@ -125,16 +160,6 @@ public enum MessageType /// ------------------------------------------------------------------------------------ public abstract string NameOfProgramToLaunch { get; } - /// ------------------------------------------------------------------------------------ - public abstract string InformativeText { get; } - - /// ------------------------------------------------------------------------------------ - /// - /// Implement ArchiveInfoHyperlinkText to define text (first occurrence only) in the - /// InformativeText that will be marked as a hyperlink to ArchiveInfoUrl. - /// - /// ------------------------------------------------------------------------------------ - public abstract string ArchiveInfoHyperlinkText { get; } /// ------------------------------------------------------------------------------------ public abstract string ArchiveInfoUrl { get; } @@ -145,11 +170,12 @@ public enum MessageType /// ------------------------------------------------------------------------------------ /// Path to the generated package + /// This is also returned by CreatePackage if successful. /// ------------------------------------------------------------------------------------ public string PackagePath { get; protected set; } /// ------------------------------------------------------------------------------------ - public string AppName { get; private set; } + public string AppName { get; } /// ------------------------------------------------------------------------------------ public bool IsBusy { get; protected set; } @@ -188,7 +214,7 @@ public enum MessageType /// the application implements this, then the default summary display will be suppressed. /// /// ------------------------------------------------------------------------------------ - public Action, string>>> OverrideDisplayInitialSummary { private get; set; } + public Action, string>>, CancellationToken> OverrideDisplayInitialSummary { private get; set; } #endregion #region construction and initialization @@ -197,54 +223,42 @@ public enum MessageType /// The application name /// Title of the submission /// Identifier (used as filename) for the package being created - /// Application can use this to pass - /// additional information that will be displayed to the user in the dialog to explain - /// any application-specific details about the archival process. /// Delegate to request client to call methods to set - /// which files should be archived (this is deferred to allow display of progress message) + /// which files should be archived (this is deferred to allow display of progress + /// message). Clients will normally do this by calling AddFileGroup one or more times. + /// /// ------------------------------------------------------------------------------------ protected ArchivingDlgViewModel(string appName, string title, string id, - string appSpecificArchivalProcessInfo, Action setFilesToArchive) + Action setFilesToArchive) { - if (appName == null) - throw new ArgumentNullException("appName"); - AppName = appName; - if (title == null) - throw new ArgumentNullException("title"); - if (id == null) - throw new ArgumentNullException("id"); - _id = id; - _appSpecificArchivalProcessInfo = appSpecificArchivalProcessInfo; + AppName = appName ?? throw new ArgumentNullException(nameof(appName)); + _id = id ?? throw new ArgumentNullException(nameof(id)); _setFilesToArchive = setFilesToArchive; - _titles[id] = title; + _titles[id] = title ?? throw new ArgumentNullException(nameof(title)); _propertiesSet[id] = MetadataProperties.Title; AdditionalMessages = new Dictionary(); } /// ------------------------------------------------------------------------------------ - public bool Initialize() + public async Task Initialize(IArchivingProgressDisplay progress, CancellationToken cancellationToken) { - IsBusy = true; + Progress = progress ?? throw new ArgumentNullException(nameof(progress)); + + if (!DoArchiveSpecificInitialization()) + return false; - try - { - if (!DoArchiveSpecificInitialization()) - return false; + await SetFilesToArchive(cancellationToken); + DisplayInitialSummary(cancellationToken); - _setFilesToArchive(this); - foreach (var fileList in _fileLists.Where(fileList => fileList.Value.Item1.Any())) - { - string normalizedName = NormalizeFilename(fileList.Key, Path.GetFileName(fileList.Value.Item1.First())); - _progressMessages[normalizedName] = fileList.Value.Item2; - } - DisplayInitialSummary(); + return true; + } - return true; - } - finally + protected virtual async Task SetFilesToArchive(CancellationToken cancellationToken) + { + await Task.Run(() => { - IsBusy = false; - } + _setFilesToArchive(this, cancellationToken); + }, cancellationToken); } /// ------------------------------------------------------------------------------------ @@ -257,29 +271,31 @@ public bool Initialize() public virtual void AddFileGroup(string groupId, IEnumerable files, string progressMessage) { Guard.AgainstNull(groupId, nameof(groupId)); - if (_fileLists.ContainsKey(groupId)) + if (FileLists.ContainsKey(groupId)) throw new ArgumentException("Duplicate file group ID: " + groupId, nameof(groupId)); - _fileLists[groupId] = Tuple.Create(files, progressMessage); + FileLists[groupId] = Tuple.Create(files, progressMessage); } /// ------------------------------------------------------------------------------------ - private void DisplayInitialSummary() + private void DisplayInitialSummary(CancellationToken cancellationToken) { if (OverrideDisplayInitialSummary != null) - OverrideDisplayInitialSummary(_fileLists); - else if (OnDisplayMessage != null) + OverrideDisplayInitialSummary(FileLists, cancellationToken); + else { - OnDisplayMessage(LocalizationManager.GetString("DialogBoxes.ArchivingDlg.PrearchivingStatusMsg", - "The following files will be added to the archive:"), MessageType.Normal); + ReportProgress(Progress.GetMessage(StringId.PreArchivingStatus), MessageType.Normal, cancellationToken); - foreach (var kvp in _fileLists) + if (OnReportMessage != null) { - string msg = FileGroupDisplayMessage(kvp.Key); - if (msg != string.Empty) - OnDisplayMessage(msg, MessageType.Indented); + foreach (var kvp in FileLists) + { + string msg = FileGroupDisplayMessage(kvp.Key); + if (msg != Empty) + OnReportMessage(msg, MessageType.Indented); - foreach (var file in kvp.Value.Item1) - OnDisplayMessage(Path.GetFileName(file), MessageType.Bullet); + foreach (var file in kvp.Value.Item1) + OnReportMessage(Path.GetFileName(file), MessageType.Bullet); + } } } } @@ -318,7 +334,7 @@ protected void PreventDuplicateMetadataProperty(string elementId, MetadataProper if (_propertiesSet.TryGetValue(elementId, out var propertiesSet)) { if (propertiesSet.HasFlag(property)) - throw new InvalidOperationException(string.Format("{0} has already been set", property.ToString())); + throw new InvalidOperationException($"{property} has already been set"); _propertiesSet[elementId] |= property; } else @@ -400,15 +416,16 @@ public void SetAbstract(string description, string language) public void SetAbstract(IDictionary descriptions) { if (descriptions == null) - throw new ArgumentNullException("descriptions"); + throw new ArgumentNullException(nameof(descriptions)); if (descriptions.Count == 0) return; if (descriptions.Count > 1) { - if (descriptions.Keys.Any(k => k.Length != 3)) - throw new ArgumentException(); + var invalidLanguageCode = descriptions.Keys.FirstOrDefault(k => k.Length != 3); + if (invalidLanguageCode != null) + throw new InvalidLanguageCodeException(invalidLanguageCode); } PreventDuplicateMetadataProperty(MetadataProperties.AbstractDescription); SetAbstract_Impl(descriptions); @@ -423,49 +440,107 @@ public void SetAbstract(IDictionary descriptions) #endregion /// ------------------------------------------------------------------------------------ - public void DisplayMessage(string msg, MessageType type) + protected void DisplayMessage(StringId msgId, MessageType type, params object[] fmtParams) { - if (OnDisplayMessage != null) - OnDisplayMessage(msg, type); + if (OnReportMessage != null) + { + var msg = Progress.GetMessage(msgId); + if (fmtParams?.Length > 0) + msg = Format(msg, fmtParams); + if (msg != null) + OnReportMessage(msg, type); + } } /// ------------------------------------------------------------------------------------ - [PublicAPI] - public abstract string GetMetadata(); + protected void DisplayMessage(string msg, MessageType type) + { + OnReportMessage?.Invoke(msg, type); + } /// ------------------------------------------------------------------------------------ - internal abstract void LaunchArchivingProgram(); + [PublicAPI] + public abstract string GetMetadata(); /// ------------------------------------------------------------------------------------ - protected void LaunchArchivingProgram(Action HandleInvalidOperation) + public virtual void LaunchArchivingProgram() { - if (string.IsNullOrEmpty(PathToProgramToLaunch) || !File.Exists(PathToProgramToLaunch)) + if (IsNullOrEmpty(PathToProgramToLaunch) || !File.Exists(PathToProgramToLaunch)) return; + var prs = new Process(); + prs.StartInfo.FileName = PathToProgramToLaunch; + if (!IsNullOrEmpty(PackagePath)) + prs.StartInfo.Arguments = "\"" + PackagePath + "\""; + LaunchArchivingProgram(prs); + } + + /// ------------------------------------------------------------------------------------ + protected void LaunchArchivingProgram(Process prs) + { try { - var prs = new Process(); - prs.StartInfo.FileName = PathToProgramToLaunch; - if (!string.IsNullOrEmpty(PackagePath)) - prs.StartInfo.Arguments = "\"" + PackagePath + "\""; prs.Start(); } catch (Exception e) { - if (e is InvalidOperationException && HandleInvalidOperation != null) - HandleInvalidOperation(); - else - { - ReportError(e, string.Format(LocalizationManager.GetString( - "DialogBoxes.ArchivingDlg.StartingRampErrorMsg", - "There was an error attempting to open the archive package in {0}."), - PathToProgramToLaunch)); - } + OnExceptionDuringLaunch?.Invoke(new EventArgs(e)); + + ReportError(e, Progress.GetMessage(StringId.ErrorStartingArchivalProgram)); } } /// ------------------------------------------------------------------------------------ - public abstract bool CreatePackage(); + /// Creates the archiving package asynchronously + /// A task, which when completed, provides the "result" (typically a file path); + /// or null if package creation failed. + /// + /// ------------------------------------------------------------------------------------ + public abstract Task CreatePackage(CancellationToken cancellationToken); + + /// ------------------------------------------------------------------------------------ + /// Report archiving progress at a major (success) point and check to see if + /// user has requested cancellation + /// ------------------------------------------------------------------------------------ + protected void ReportMajorProgressPoint(StringId id, CancellationToken cancellationToken, + bool isAtCancelablePoint = true) + { + ReportProgress(Progress.GetMessage(id), MessageType.Success, cancellationToken, + isAtCancelablePoint); + } + + /// ------------------------------------------------------------------------------------ + /// Report archiving progress and check to see if user has requested + /// cancellation + /// ------------------------------------------------------------------------------------ + protected void ReportProgress(string message, MessageType type, + CancellationToken cancellationToken, bool isAtCancelablePoint = true) + { + if (cancellationToken.IsCancellationRequested) + { + if (!isAtCancelablePoint) + return; + CleanUp(); + throw new OperationCanceledException(); + } + + DisplayMessage(message, type); + Progress.IncrementProgress(); + } + + public virtual void Dispose() + { + CleanUp(); + } + + /// ------------------------------------------------------------------------------------ + /// Performs any needed clean-up after creating a package or when creation is + /// canceled + /// ------------------------------------------------------------------------------------ + protected internal virtual void CleanUp() + { + // delete temp files, etc. + } /// ------------------------------------------------------------------------------------ protected virtual StringBuilder DoArchiveSpecificFilenameNormalization(string key, string fileName) @@ -483,7 +558,7 @@ public virtual string NormalizeFilename(string key, string fileName) } /// ------------------------------------------------------------------------------------ - const int CopyBufferSize = 64 * 1024; + private const int kCopyBufferSize = 64 * 1024; /// ------------------------------------------------------------------------------------ protected static void CopyFile(string src, string dest) { @@ -491,9 +566,9 @@ protected static void CopyFile(string src, string dest) { using (var inputFile = File.OpenRead(src)) { - var buffer = new byte[CopyBufferSize]; + var buffer = new byte[kCopyBufferSize]; int bytesRead; - while ((bytesRead = inputFile.Read(buffer, 0, CopyBufferSize)) != 0) + while ((bytesRead = inputFile.Read(buffer, 0, kCopyBufferSize)) != 0) { outputFile.Write(buffer, 0, bytesRead); } @@ -501,55 +576,19 @@ protected static void CopyFile(string src, string dest) } } - /// ------------------------------------------------------------------------------------ - public virtual void Cancel() - { - if (_cancelProcess) - return; - - _cancelProcess = true; - - if (_worker != null) - { - DisplayMessage(Environment.NewLine + LocalizationManager.GetString( - "DialogBoxes.ArchivingDlg.CancellingMsg", "Canceling..."), MessageType.Error); - - _worker.CancelAsync(); - while (_worker.IsBusy) - Application.DoEvents(); - } - } - /// ------------------------------------------------------------------------------------ protected void ReportError(Exception e, string msg) { - if (OnDisplayError != null) - OnDisplayError(msg, _titles[_id], e); + if (OnError != null) + OnError(msg, _titles[_id], e); else if (e != null) throw e; - if (HandleNonFatalError != null) - HandleNonFatalError(e, msg); + HandleNonFatalError?.Invoke(e, msg); } - protected string PreparingFilesMsg => LocalizationManager.GetString( - "DialogBoxes.ArchivingDlg.PreparingFilesMsg", "Analyzing component files"); - - protected string GetSavingFilesMsg(string type) - { - return string.Format(LocalizationManager.GetString( - "DialogBoxes.ArchivingDlg.SavingFilesInPackageMsg", - "Saving files in {0} package", - "Parameter is the type of archive (e.g., RAMP/IMDI)"), type); - } - - protected string GetFileExcludedMsg(string type, string file) - { - return string.Format(LocalizationManager.GetString( - "DialogBoxes.ArchivingDlg.FileExcludedFromPackage", - "File excluded from {0} package: ", - "Parameter is the type of archive (e.g., RAMP/IMDI)"), type) + file; - } + protected string GetFileExcludedMsg(string file) => + Progress.GetMessage(StringId.FileExcludedFromPackage) + file; /// ------------------------------------------------------------------------------------ /// @@ -558,17 +597,8 @@ protected string GetFileExcludedMsg(string type, string file) /// ------------------------------------------------------------------------------------ public static bool IsMono => (Type.GetType("Mono.Runtime") != null); - /// - /// - [PublicAPI] - public abstract IArchivingSession AddSession(string sessionId); - - /// - [PublicAPI] - public abstract IArchivingPackage ArchivingPackage { get; } - /// - public Dictionary AdditionalMessages { get; private set; } + public Dictionary AdditionalMessages { get; } public bool IsPathWritable(string directory) { @@ -579,15 +609,11 @@ public bool IsPathWritable(string directory) } catch (Exception e) { - DisplayMessage(e.Message, MessageType.Warning); + OnReportMessage?.Invoke(e.Message, MessageType.Warning); return false; } - var msg = LocalizationManager.GetString( - "DialogBoxes.ArchivingDlg.PathNotWritableMsg", - "The path is not writable: {0}"); - - DisplayMessage(string.Format(msg, directory), MessageType.Warning); + DisplayMessage(StringId.PathNotWritable, MessageType.Warning, directory); return false; } diff --git a/SIL.Archiving/ArchivingEnumerations.cs b/SIL.Archiving/ArchivingEnumerations.cs old mode 100755 new mode 100644 index ba5da6715..948fa25e6 --- a/SIL.Archiving/ArchivingEnumerations.cs +++ b/SIL.Archiving/ArchivingEnumerations.cs @@ -1,4 +1,4 @@ -using System; +using System; // ReSharper disable CSharpWarnings::CS1591 // ReSharper disable InconsistentNaming @@ -48,7 +48,7 @@ public enum AudienceType /// the Scripture Part (specific testaments/books included). /// [Flags] - public enum VernacularMaterialsType : ulong + public enum VernacularMaterialsType : long { // First two bits determine the overall vernacular materials type. Only one of these may be set. Remaining // bits indicate specific content. @@ -225,7 +225,7 @@ public enum ScholarlyWorkType /// Domains to which a resource relates http://purl.org/net/sword-types/SIL/metadata/dc/subject/silDomain /// [Flags] - public enum SilDomain : ulong + public enum SilDomain : long { // Academic domains diff --git a/SIL.Archiving/ArchivingLanguage.cs b/SIL.Archiving/ArchivingLanguage.cs old mode 100755 new mode 100644 index 4ffafd6ae..f51780029 --- a/SIL.Archiving/ArchivingLanguage.cs +++ b/SIL.Archiving/ArchivingLanguage.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; +using JetBrains.Annotations; using SIL.WritingSystems; namespace SIL.Archiving @@ -9,8 +10,8 @@ namespace SIL.Archiving /// public class ArchivingLanguage : IComparable { - protected string _iso3Code; // ex. "eng" - protected string _englishName; // ex. "English" + private string m_iso3Code; // ex. "eng" + private string m_englishName; // ex. "English" /// public ArchivingLanguage(string iso3Code) @@ -26,6 +27,7 @@ public ArchivingLanguage(string iso3Code, string languageName) } /// + [PublicAPI] public ArchivingLanguage(string iso3Code, string languageName, string englishName) { LanguageName = languageName; @@ -39,8 +41,8 @@ public ArchivingLanguage(string iso3Code, string languageName, string englishNam /// public string Iso3Code { - get { return _iso3Code; } - set { _iso3Code = value.ToLower(); } + get => m_iso3Code; + set => m_iso3Code = value.ToLower(); } /// @@ -48,13 +50,13 @@ public string EnglishName { get { - if (string.IsNullOrEmpty(_englishName)) + if (string.IsNullOrEmpty(m_englishName)) { var returnVal = string.Empty; // look for the language name in culture info var culture = CultureInfo.GetCultures(CultureTypes.AllCultures).FirstOrDefault( - c => c.ThreeLetterISOLanguageName == _iso3Code); + c => c.ThreeLetterISOLanguageName == m_iso3Code); if (culture != null) { @@ -62,7 +64,7 @@ public string EnglishName // The name returned first might be something like "English (United States)," so // search the parent cultures for the base culture which will return "English." - while ((culture.Parent.ThreeLetterISOLanguageName == _iso3Code) + while ((culture.Parent.ThreeLetterISOLanguageName == m_iso3Code) && (culture.Parent.ThreeLetterISOLanguageName != "ivl")) { culture = culture.Parent; @@ -70,26 +72,30 @@ public string EnglishName } } - _englishName = returnVal; + m_englishName = returnVal; } - if (string.IsNullOrEmpty(_englishName)) + if (string.IsNullOrEmpty(m_englishName)) { // Not very efficient, but this is not very performance-critical. // And it's undesirable to crash if someone uses a language that's not windows-standard. // It's not guaranteed that DesiredName is an English name, but it's the best we can do AFAIK. var lookup = new LanguageLookup(); - var lang = lookup.GetLanguageFromCode(_iso3Code); - _englishName = lang?.DesiredName; + var lang = lookup.GetLanguageFromCode(m_iso3Code); + m_englishName = lang?.DesiredName; } // throw an exception if no name is found - if (string.IsNullOrEmpty(_englishName)) - throw new CultureNotFoundException(string.Format("Could not determine the language for ISO3 code \"{0}.\" Perhaps you need to set the EnglishName value explicitly.", _iso3Code)); + if (string.IsNullOrEmpty(m_englishName)) + { + throw new CultureNotFoundException( + $"Could not determine the language for ISO3 code \"{m_iso3Code}.\" " + + $"Perhaps you need to set the {nameof(EnglishName)} value explicitly."); + } - return _englishName; + return m_englishName; } - set { _englishName = value; } + set => m_englishName = value; } /// The script used for the Subject Language. Ex. "Latn:Latin" @@ -98,26 +104,26 @@ public string EnglishName /// The dialect used for the language. Ex. "03035:Standard; deu" public string Dialect { get; set; } - /// Compare 2 ArchivingLanguage objects. They are identical if they have the same Iso3Code and LanguageName + /// Compare an ArchivingLanguage to an object. They are identical if the object + /// is an ArchivingLanguage with the same Iso3Code and LanguageName public int CompareTo(object obj) { if (obj == null) return 1; - ArchivingLanguage other = obj as ArchivingLanguage; - - if (other == null) + if (!(obj is ArchivingLanguage other)) throw new ArgumentException(); // first compare the Iso3Code - int result = String.Compare(Iso3Code, other.Iso3Code, StringComparison.Ordinal); + int result = string.Compare(Iso3Code, other.Iso3Code, StringComparison.Ordinal); if (result != 0) return result; // if the same Iso3Code, compare the LanguageName return String.Compare(LanguageName, other.LanguageName, StringComparison.Ordinal); } - /// Compare 2 LanguageString objects. They are identical if they have the same Iso3LanguageId + /// Compare 2 ArchivingLanguage objects. They are identical if they have the same + /// Iso3Code and LanguageName public static int Compare(ArchivingLanguage langA, ArchivingLanguage langB) { return langA.CompareTo(langB); diff --git a/SIL.Archiving/ArchivingPrograms.cs b/SIL.Archiving/ArchivingPrograms.cs index fd444bcfa..5e6b60089 100644 --- a/SIL.Archiving/ArchivingPrograms.cs +++ b/SIL.Archiving/ArchivingPrograms.cs @@ -1,5 +1,6 @@ -using System.IO; +using System.IO; using System.Linq; +using JetBrains.Annotations; using SIL.IO; namespace SIL.Archiving @@ -8,15 +9,17 @@ namespace SIL.Archiving public static class ArchivingPrograms { /// Is RAMP installed on this computer + [PublicAPI] public static bool RampIsInstalled() { - return (GetRampExeFileLocation() != null); + return GetRampExeFileLocation() != null; } /// Is Arbil installed on this computer + [PublicAPI] public static bool ArbilIsInstalled() { - return (GetArbilExeFileLocation() != null); + return GetArbilExeFileLocation() != null; } /// Get the path and file name of the RAMP executable file @@ -74,9 +77,6 @@ private static string FindArbilJarFileMono() } /// Command line parameters use to launch Arbil in Linux - public static string ArbilCommandLineArgs - { - get { return "-Xms256m -Xmx1024m -jar \"{0}\""; } - } + public static string ArbilCommandLineArgs => "-Xms256m -Xmx1024m -jar \"{0}\""; } } diff --git a/SIL.Archiving/Extensions.cs b/SIL.Archiving/Extensions.cs old mode 100755 new mode 100644 index f0a1e2305..ae4b86775 --- a/SIL.Archiving/Extensions.cs +++ b/SIL.Archiving/Extensions.cs @@ -1,7 +1,6 @@ using System; using System.Globalization; using System.Text; -using System.Windows.Forms; namespace SIL.Archiving { @@ -10,79 +9,7 @@ public static class Extensions { /// ------------------------------------------------------------------------------------ /// - /// Used to size a link label in mono because it is not working at all - /// - /// - /// ------------------------------------------------------------------------------------ - public static void SizeToContents(this LinkLabel linkLabel) - { - var w = linkLabel.ClientSize.Width; - - using (var g = linkLabel.CreateGraphics()) - { - - if (ArchivingDlgViewModel.IsMono) - { - // split at the existing like breaks - var segments = linkLabel.Text.Replace("\r", "").Split(new[] {'\n'}, StringSplitOptions.None); - var newText = new StringBuilder(); - - foreach (var segment in segments) - { - var thisSegment = segment.Trim(); - - while (MeasureText(linkLabel, g, thisSegment, linkLabel.Font).Width > w) - { - var line = string.Empty; - var lastSpace = 0; - - for (var i = 0; i < thisSegment.Length; i++) - { - if (char.IsWhiteSpace(thisSegment[i])) - { - if (MeasureText(linkLabel, g, line, linkLabel.Font).Width > w) - { - newText.AppendLine(thisSegment.Substring(0, lastSpace)); - thisSegment = thisSegment.Substring(lastSpace + 1); - break; - } - - lastSpace = i; - } - line += thisSegment[i]; - } - } - - // check for left-overs - if (thisSegment.Length > 0) - newText.AppendLine(thisSegment); - } - - linkLabel.Text = newText.ToString(); - } - - var size = MeasureText(linkLabel, g, linkLabel.Text, linkLabel.Font, new System.Drawing.Size(w, Int32.MaxValue)); - linkLabel.Height = size.Height; - } - } - private static System.Drawing.Size MeasureText(this LinkLabel linkLabel, System.Drawing.Graphics g, string text, System.Drawing.Font font) - { - if (linkLabel.UseCompatibleTextRendering) - return g.MeasureString(text, font).ToSize(); - else - return TextRenderer.MeasureText(g, text, font); - } - private static System.Drawing.Size MeasureText(this LinkLabel linkLabel, System.Drawing.Graphics g, string text, System.Drawing.Font font, System.Drawing.Size proposedSize) - { - if (linkLabel.UseCompatibleTextRendering) - return g.MeasureString(text, font, proposedSize.Width).ToSize(); - else - return TextRenderer.MeasureText(g, text, font, proposedSize, TextFormatFlags.WordBreak); - } - - /// ------------------------------------------------------------------------------------ - /// - /// Combines the functionality fo StringBuilder.AppendFormat and + /// Combines the functionality of StringBuilder.AppendFormat and /// StringBuilder.AppendLine. Also allows for the delimiter to be specified. If the /// delimiter is null, Environment.NewLine will be used. /// @@ -93,8 +20,9 @@ private static System.Drawing.Size MeasureText(this LinkLabel linkLabel, System. /// ------------------------------------------------------------------------------------ public static void AppendLineFormat(this StringBuilder sb, string format, object[] args, string delimiter) { - if (delimiter == null) delimiter = Environment.NewLine; - if (sb.Length != 0) sb.Append(delimiter); + delimiter ??= Environment.NewLine; + if (sb.Length != 0) + sb.Append(delimiter); sb.AppendFormat(format, args); } @@ -108,8 +36,9 @@ public static void AppendLineFormat(this StringBuilder sb, string format, object /// ------------------------------------------------------------------------------------ public static void AppendDelimiter(this StringBuilder sb, string value, string delimiter) { - if (delimiter == null) delimiter = Environment.NewLine; - if (sb.Length != 0) sb.Append(delimiter); + delimiter ??= Environment.NewLine; + if (sb.Length != 0) + sb.Append(delimiter); sb.Append(value); } diff --git a/SIL.Archiving/Generic/AccessProtocol/AccessProtocolList.cs b/SIL.Archiving/Generic/AccessProtocol/AccessProtocolList.cs index cc1f5778a..3ed2fb3a8 100644 --- a/SIL.Archiving/Generic/AccessProtocol/AccessProtocolList.cs +++ b/SIL.Archiving/Generic/AccessProtocol/AccessProtocolList.cs @@ -1,4 +1,3 @@ - using System; using System.Collections.Generic; using System.IO; @@ -7,7 +6,7 @@ using System.Runtime.Serialization.Json; using System.Text; using JetBrains.Annotations; -using SIL.Archiving.Properties; +using static SIL.Archiving.Resources.Resources; namespace SIL.Archiving.Generic.AccessProtocol { @@ -35,13 +34,13 @@ protected AccessProtocols(IEnumerable items) /// public static AccessProtocols Load(string programDirectory) => - _instance ??= LoadFromFile(kProtocolFileName, Resources.AccessProtocols, programDirectory); + _instance ??= LoadFromFile(kProtocolFileName, GetResource(Name.AccessProtocols_json), programDirectory); /// public static AccessProtocols LoadCustom() { return _customInstance ??= - LoadFromFile(kCustomProtocolFileName, Resources.CustomAccessProtocols, null); + LoadFromFile(kCustomProtocolFileName, GetResource(Name.CustomAccessProtocols_json), null); } private static AccessProtocols LoadFromFile(string protocolFileName, string resourceName, string programDirectory) @@ -206,8 +205,8 @@ public string GetDocumentationUri(string programDirectory) if (!File.Exists(fileName)) { var pos = DocumentationFile.LastIndexOf('.'); - var resourceName = (pos > -1) ? DocumentationFile.Substring(0, pos) : DocumentationFile; - var resourceString = Resources.ResourceManager.GetString(resourceName); + var resourceName = pos > -1 ? DocumentationFile.Substring(0, pos) : DocumentationFile; + var resourceString = GetResource(resourceName); if (!string.IsNullOrEmpty(resourceString)) File.WriteAllText(fileName, resourceString); } diff --git a/SIL.Archiving/Generic/ArchivingFile.cs b/SIL.Archiving/Generic/ArchivingFile.cs index 7418f6517..b98017a04 100644 --- a/SIL.Archiving/Generic/ArchivingFile.cs +++ b/SIL.Archiving/Generic/ArchivingFile.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using SIL.Archiving.Generic.AccessProtocol; @@ -7,12 +7,12 @@ namespace SIL.Archiving.Generic /// A file to add to the archive public class ArchivingFile { - protected readonly string _fullName; - protected string _fileName; - protected string _fileSize; // in KB - protected string _mimeType; - protected LanguageStringCollection _descriptions; - protected ArchiveAccessProtocol _accessProtocol; + private readonly string _fullName; + private string _fileName; + private string _fileSize; // in KB + private string _mimeType; + private LanguageStringCollection _descriptions; + private ArchiveAccessProtocol _accessProtocol; /// If this file contains information about another file, put the name of the other file here public string DescribesAnotherFile; diff --git a/SIL.Archiving/Generic/ArchivingPackage.cs b/SIL.Archiving/Generic/ArchivingPackage.cs index dc5db1144..56a2611da 100644 --- a/SIL.Archiving/Generic/ArchivingPackage.cs +++ b/SIL.Archiving/Generic/ArchivingPackage.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using SIL.Archiving.Generic.AccessProtocol; @@ -47,7 +47,7 @@ public interface IArchivingPackage : IArchivingGenericObject /// Collects the data needed to produce an archive package to upload public abstract class ArchivingPackage : IArchivingPackage { - protected readonly List> _keys; + private readonly List> _keys; public ArchivingLanguageCollection MetadataIso3Languages { get; set; } @@ -71,6 +71,8 @@ protected ArchivingPackage() } + protected IEnumerable> Keys => _keys; + /// public string Title { get; set; } diff --git a/SIL.Archiving/Generic/ArchivingSession.cs b/SIL.Archiving/Generic/ArchivingSession.cs index a711c4f28..7c215c277 100644 --- a/SIL.Archiving/Generic/ArchivingSession.cs +++ b/SIL.Archiving/Generic/ArchivingSession.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using JetBrains.Annotations; namespace SIL.Archiving.Generic { @@ -10,10 +11,11 @@ public interface IArchivingSession : IArchivingGenericObject void AddFile(ArchivingFile file); /// + [PublicAPI] void AddFileAccess(string fullFileName, ArchivingPackage package); - /// - List Files { get; } + /// The paths of all resource files in the session. + IReadOnlyList Files { get; } /// Set session date with DateTime object void SetDate(DateTime date); @@ -25,36 +27,47 @@ public interface IArchivingSession : IArchivingGenericObject void SetDate(int year); /// + [PublicAPI] void AddContentLanguage(ArchivingLanguage language, LanguageString description); /// + [PublicAPI] void AddActor(ArchivingActor actor); /// + [PublicAPI] void AddGroupKeyValuePair(string key, string value); /// + [PublicAPI] void AddContentKeyValuePair(string key, string value); /// + [PublicAPI] void AddFileKeyValuePair(string fullFileName, string key, string value); /// + [PublicAPI] void AddContentDescription(LanguageString description); /// + [PublicAPI] void AddActorDescription(ArchivingActor actor, LanguageString description); /// + [PublicAPI] void AddFileDescription(string fullFileName, LanguageString description); /// + [PublicAPI] void AddActorContact(ArchivingActor actor, ArchivingContact contact); /// + [PublicAPI] void AddMediaFileTimes(string fullFileName, string start, string stop); /// + [PublicAPI] void AddProject(ArchivingPackage package); /// @@ -66,10 +79,11 @@ public interface IArchivingSession : IArchivingGenericObject /// string Interactivity { get; set; } - /// + /// Indicates in how far the researcher was involved in the linguistic event + [PublicAPI] string Involvement { get; set; } - /// + /// Degree of planning of the event string PlanningType { get; set; } /// diff --git a/SIL.Archiving/IArchivingMessageProvider.cs b/SIL.Archiving/IArchivingMessageProvider.cs new file mode 100644 index 000000000..49d94de08 --- /dev/null +++ b/SIL.Archiving/IArchivingMessageProvider.cs @@ -0,0 +1,16 @@ +namespace SIL.Archiving +{ + public interface IArchivingProgressDisplay + { + void IncrementProgress(); + string GetMessage(ArchivingDlgViewModel.StringId msgId); + /// ------------------------------------------------------------------------------------ + /// + /// Short name/description of the archiving program or standard used for this type of + /// archiving. (Should fit in the frame "Archive using ___".) Typically not localized, + /// but can potentially be. + /// + /// ------------------------------------------------------------------------------------ + string ArchiveTypeName { get; } + } +} diff --git a/SIL.Archiving/IMDI/IMDIArchivingDlgViewModel.cs b/SIL.Archiving/IMDI/IMDIArchivingDlgViewModel.cs index 02740a8a5..fb0abaab0 100644 --- a/SIL.Archiving/IMDI/IMDIArchivingDlgViewModel.cs +++ b/SIL.Archiving/IMDI/IMDIArchivingDlgViewModel.cs @@ -1,16 +1,17 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using System.Threading; -using L10NSharp; +using System.Threading.Tasks; +using JetBrains.Annotations; using SIL.Archiving.Generic; using SIL.Archiving.IMDI.Schema; -using System.Windows.Forms; using SIL.Extensions; +using SIL.WritingSystems; +using static System.String; namespace SIL.Archiving.IMDI { @@ -19,22 +20,16 @@ public class IMDIArchivingDlgViewModel : ArchivingDlgViewModel, ISupportMetadata { private readonly IMDIPackage _imdiData; private string _corpusDirectoryName; - private bool _workerException; private string _programPreset; private string _otherProgramPath; private readonly string _configFileName = Path.Combine(ArchivingFileSystem.SilCommonArchivingDataFolder, "IMDIProgram.config"); private string _outputFolder; + public event EventHandler InitializationFailed; + #region Properties - internal override string ArchiveType - { - get - { - return LocalizationManager.GetString("DialogBoxes.ArchivingDlg.IMDIArchiveType", "IMDI", - "This is the abbreviation for Isle Metadata Initiative (https://www.mpi.nl/imdi/). " + - "Typically this probably does not need to be localized."); - } - } + + public override Standard ArchiveType => Standard.IMDI; public override string NameOfProgramToLaunch { @@ -63,42 +58,6 @@ public override string NameOfProgramToLaunch } } - /// ------------------------------------------------------------------------------------ - public override string InformativeText - { - get - { - string programInfo = string.IsNullOrEmpty(NameOfProgramToLaunch) ? - string.Format(LocalizationManager.GetString("DialogBoxes.ArchivingDlg.NoIMDIProgramInfoText", - "The {0} package will be created in {1}.", - "Parameter 0 is 'IMDI'; " + - "Parameter 1 is the path where the package is created."), - ArchiveType, PackagePath) - : - string.Format(LocalizationManager.GetString("DialogBoxes.ArchivingDlg.IMDIProgramInfoText", - "This tool will help you use {0} to archive your {1} data. When the {1} package has been " + - "created, you can launch {0} and enter any additional information before doing the actual submission.", - "Parameter 0 is the name of the program that will be launched to further prepare the IMDI data for submission; " + - "Parameter 1 is the name of the calling (host) program (SayMore, FLEx, etc.)"), NameOfProgramToLaunch, AppName); - return string.Format(LocalizationManager.GetString("DialogBoxes.ArchivingDlg.IMDIOverviewText", - "{0} ({1}) is a metadata standard to describe multi-media and multi-modal language " + - "resources. The standard provides interoperability for browsable and searchable " + - "corpus structures and resource descriptions.", - "Parameter 0 is 'Isle Metadata Initiative' (the first occurrence will be turned into a hyperlink); " + - "Parameter 1 is 'IMDI'"), - ArchiveInfoHyperlinkText, ArchiveType) + - " " + _appSpecificArchivalProcessInfo + - " " + programInfo; - } - } - - /// - public override string ArchiveInfoHyperlinkText - { - get { return LocalizationManager.GetString("DialogBoxes.ArchivingDlg.IsleMetadataInitiative", - "Isle Metadata Initiative", "Typically this probably does not need to be localized."); } - } - /// ------------------------------------------------------------------------------------ public override string ArchiveInfoUrl => Properties.Settings.Default.IMDIWebSite; @@ -109,50 +68,52 @@ public override string ArchiveInfoHyperlinkText /// Constructor /// The application name /// Title of the submission. - /// Identifier for the package being created. Used as the CORPUS name. - /// Application can use this to pass - /// additional information that will be displayed to the user in the dialog to explain - /// any application-specific details about the archival process. + /// Identifier for the package being created. Used as the CORPUS name. + /// /// Indicates whether this is for an entire project corpus or a /// single session /// Delegate to request client to call methods to set - /// which files should be archived (this is deferred to allow display of progress message) - /// Base folder where IMDI file structure is to be created + /// which files should be archived (this is deferred to allow display of progress message). + /// Clients will normally do this by calling AddFileGroup one or more times. + /// Base folder where IMDI file structure is to be created + /// /// ------------------------------------------------------------------------------------ - public IMDIArchivingDlgViewModel(string appName, string title, string id, - string appSpecificArchivalProcessInfo, bool corpus, - Action setFilesToArchive, string outputFolder) - : base(appName, title, id, appSpecificArchivalProcessInfo, setFilesToArchive) + public IMDIArchivingDlgViewModel(string appName, string title, string id, bool corpus, + Action setFilesToArchive, string outputFolder) + : base(appName, title, id, setFilesToArchive) { OutputFolder = outputFolder; _imdiData = new IMDIPackage(corpus, PackagePath) { - Title = _titles[_id], - Name = _id + Title = PackageTitle, + Name = PackageId }; } /// ------------------------------------------------------------------------------------ protected override bool DoArchiveSpecificInitialization() { - // no-op - return true; + if (_imdiData.IsValid) + return true; + + DisplayMessage(Progress.GetMessage(StringId.IMDIPackageInvalid), MessageType.Error); + InitializationFailed?.Invoke(this, EventArgs.Empty); + return false; } /// ------------------------------------------------------------------------------------ public override int CalculateMaxProgressBarValue() { // One for processing each list and one for copying each file - return _fileLists.Count + _fileLists.SelectMany(kvp => kvp.Value.Item1).Count(); + return FileLists.Count + FileLists.SelectMany(kvp => kvp.Value.Item1).Count(); } /// ------------------------------------------------------------------------------------ protected override string FileGroupDisplayMessage(string groupKey) { - if (groupKey == string.Empty) - return LocalizationManager.GetString("DialogBoxes.ArchivingDlg.IMDIActorsGroup", "Actors", - "This is the heading displayed in the Archive Using IMDI dialog box for the files for the actors/participants"); + if (groupKey == Empty) + Progress.GetMessage(StringId.IMDIActorsGroup); return base.FileGroupDisplayMessage(groupKey); } @@ -164,33 +125,26 @@ protected override string FileGroupDisplayMessage(string groupKey) /// The abstract description /// ISO 639-3 3-letter language code /// ------------------------------------------------------------------------------------ + [PublicAPI] public void SetSessionDescription(string sessionId, string description, string iso3LanguageCode) { if (description == null) - throw new ArgumentNullException("description"); - if (iso3LanguageCode == null) - throw new ArgumentNullException("iso3LanguageCode"); - if (iso3LanguageCode.Length != 3) - { - var msg = LocalizationManager.GetString("DialogBoxes.ArchivingDlg.ISO3CodeRequired", - "ISO 639-3 3-letter language code required.", - "Message displayed when an invalid language code is given."); - throw new ArgumentException(msg, "iso3LanguageCode"); - } + throw new ArgumentNullException(nameof(description)); + // This will throw an appropriate exception if the language code is not valid: + IetfLanguageTag.Create(iso3LanguageCode, null, null, null); _imdiData.AddDescription(sessionId, new LanguageString { Value = description, Iso3LanguageId = iso3LanguageCode }); } - /// - /// + /// ------------------------------------------------------------------------------------ protected override void SetAbstract_Impl(IDictionary descriptions) { foreach (var desc in descriptions) _imdiData.AddDescription(new LanguageString(desc.Value, desc.Key)); } - /// - /// + + /// ------------------------------------------------------------------------------------ public override string GetMetadata() { return _imdiData.BaseImdiFile.ToString(); @@ -198,166 +152,66 @@ public override string GetMetadata() /// ------------------------------------------------------------------------------------ /// Launch Arbil or Lamus or whatever - /// need custom launcher here because Arbil is a java program, with no executable on linux + /// Need custom launcher here because Arbil is a java program, with no + /// executable on linux /// ------------------------------------------------------------------------------------ - internal override void LaunchArchivingProgram() + public override void LaunchArchivingProgram() { - if (string.IsNullOrEmpty(PathToProgramToLaunch) || !File.Exists(PathToProgramToLaunch)) + if (IsNullOrEmpty(PathToProgramToLaunch) || !File.Exists(PathToProgramToLaunch)) return; // if it is a .jar file, open with java - var exePath = (PathToProgramToLaunch.EndsWith(".jar")) ? "java" : PathToProgramToLaunch; - var args = string.Empty; + var exePath = PathToProgramToLaunch.EndsWith(".jar") ? "java" : PathToProgramToLaunch; + var args = Empty; if (exePath == "java") { // are there additional command line parameters for this program? - if (PathToProgramToLaunch.ToLower().Contains("arbil")) - args = string.Format(ArchivingPrograms.ArbilCommandLineArgs, PathToProgramToLaunch); - else - args = PathToProgramToLaunch; + args = PathToProgramToLaunch.ToLower().Contains("arbil") ? + Format(ArchivingPrograms.ArbilCommandLineArgs, PathToProgramToLaunch) : + PathToProgramToLaunch; } - try - { - var prs = new Process { StartInfo = { FileName = exePath, Arguments = args } }; - prs.Start(); - } - catch (Exception e) - { - ReportError(e, string.Format(LocalizationManager.GetString("DialogBoxes.ArchivingDlg.StartingIMDIErrorMsg", - "There was an error attempting to open the archive package in {0}."), PathToProgramToLaunch)); - } + var prs = new Process { StartInfo = { FileName = exePath, Arguments = args } }; + + LaunchArchivingProgram(prs); } -#region Create IMDI package in worker thread +#region Create IMDI package asynchronously - /// - public override bool CreatePackage() + /// ------------------------------------------------------------------------------------ + public override async Task CreatePackage(CancellationToken cancellationToken) { - IsBusy = true; - // check for missing data that is required by Arbil var success = _imdiData.SetMissingInformation(); // write the xml files if (success) - success = _imdiData.CreateIMDIPackage(); + success = await _imdiData.CreateIMDIPackage(cancellationToken); // copy the content files if (success && !MetadataOnly) - success = CreateIMDIPackage(); + success = await CreateIMDIPackageAsync(cancellationToken); CleanUp(); - if (success) - { - // copy the path to the imdi file to the clipboard - - // SP-818: Crash in IMDI export when dialog tries to put string on clipboard - // 18 FEB 2014, Phil Hopper: I found this possible solution using retries on StackOverflow - // https://stackoverflow.com/questions/5707990/requested-clipboard-operation-did-not-succeed - //Clipboard.SetData(DataFormats.Text, _imdiData.MainExportFile); - Clipboard.SetDataObject(_imdiData.MainExportFile, true, 3, 500); - - var successMsg = LocalizationManager.GetString("DialogBoxes.ArchivingDlg.ReadyToCallIMDIMsg", - "Exported to {0}. This path is now on your clipboard. If you are using Arbil, go to File, Import, then paste this path in."); - DisplayMessage(string.Format(successMsg, _imdiData.MainExportFile), MessageType.Success); - } - - IsBusy = false; - return success; + return success ? _imdiData.MainExportFile : null; } - /// - public bool CreateIMDIPackage() - { - try - { - using (_worker = new BackgroundWorker()) - { - _cancelProcess = false; - _workerException = false; - _worker.ProgressChanged += HandleBackgroundWorkerProgressChanged; - _worker.WorkerReportsProgress = true; - _worker.WorkerSupportsCancellation = true; - _worker.DoWork += CreateIMDIPackageInWorkerThread; - _worker.RunWorkerAsync(); - - while (_worker.IsBusy) - Application.DoEvents(); - } - } - catch (Exception e) - { - ReportError(e, LocalizationManager.GetString( - "DialogBoxes.ArchivingDlg.CreatingIMDIPackageErrorMsg", - "There was a problem starting process to create IMDI package.")); - - return false; - } - finally - { - _worker = null; - } - - return !_cancelProcess && !_workerException; - } - - public override void Cancel() - { - base.Cancel(); - - CleanUp(); - } - - /// - void HandleBackgroundWorkerProgressChanged(object sender, ProgressChangedEventArgs e) - { - if (e.UserState == null || _cancelProcess) - return; - - if (e.UserState is KeyValuePair) - { - var kvp = (KeyValuePair)e.UserState; - ReportError(kvp.Key, kvp.Value); - return; - } - - if (!string.IsNullOrEmpty(e.UserState as string)) - { - if (e.ProgressPercentage == 0) - { - DisplayMessage(e.UserState.ToString(), MessageType.Success); - return; - } - - DisplayMessage(e.UserState.ToString(), MessageType.Detail); - } - - if (IncrementProgressBarAction != null) - IncrementProgressBarAction(); - } - - private void CreateIMDIPackageInWorkerThread(object sender, DoWorkEventArgs e) + /// ------------------------------------------------------------------------------------ + private async Task CreateIMDIPackageAsync(CancellationToken cancellationToken) { try { var outputDirectory = Path.Combine(_imdiData.PackagePath, NormalizeDirectoryName(_imdiData.Name)); - if (Thread.CurrentThread.Name == null) - Thread.CurrentThread.Name = "CreateIMDIPackageInWorkerThread"; - - _worker.ReportProgress(0, PreparingFilesMsg); + ReportMajorProgressPoint(StringId.PreparingFiles, cancellationToken); var filesToCopy = new Dictionary(); // get files from each session - foreach (var sess in _imdiData.Sessions) + foreach (var session in _imdiData.Sessions.OfType()) { - Session session = (Session) sess; - - _worker.ReportProgress(1 /* actual value ignored, progress just increments */, - session.Name); + ReportProgress(session.Name, MessageType.Detail, cancellationToken); // get files to copy foreach (var file in session.Resources.MediaFile) @@ -366,7 +220,7 @@ private void CreateIMDIPackageInWorkerThread(object sender, DoWorkEventArgs e) var fullSessionDirName = Path.Combine(outputDirectory, NormalizeDirectoryName(file.OutputDirectory)); Directory.CreateDirectory(fullSessionDirName); - var newFileName = NormalizeFilename(string.Empty, Path.GetFileName(file.FullPathAndFileName)); + var newFileName = NormalizeFilename(Empty, Path.GetFileName(file.FullPathAndFileName)); filesToCopy[file.FullPathAndFileName] = Path.Combine(fullSessionDirName, newFileName); } @@ -376,24 +230,18 @@ private void CreateIMDIPackageInWorkerThread(object sender, DoWorkEventArgs e) var fullSessionDirName = Path.Combine(outputDirectory, NormalizeDirectoryName(file.OutputDirectory)); Directory.CreateDirectory(fullSessionDirName); - var newFileName = NormalizeFilename(string.Empty, Path.GetFileName(file.FullPathAndFileName)); + var newFileName = NormalizeFilename(Empty, Path.GetFileName(file.FullPathAndFileName)); filesToCopy[file.FullPathAndFileName] = Path.Combine(fullSessionDirName, newFileName); } - - if (_cancelProcess) - return; } - _worker.ReportProgress(0, LocalizationManager.GetString("DialogBoxes.ArchivingDlg.CopyingFilesMsg", - "Copying files")); + ReportMajorProgressPoint(StringId.CopyingFiles, cancellationToken); // copy the files now foreach (var fileToCopy in filesToCopy) { - if (_cancelProcess) - return; - _worker.ReportProgress(1 /* actual value ignored, progress just increments */, - Path.GetFileName(fileToCopy.Key)); + ReportProgress(Path.GetFileName(fileToCopy.Key), MessageType.Detail, cancellationToken); + if (FileCopyOverride != null) { try @@ -407,23 +255,26 @@ private void CreateIMDIPackageInWorkerThread(object sender, DoWorkEventArgs e) } catch (Exception error) { - var msg = GetFileExcludedMsg(ArchiveType, fileToCopy.Value); + var msg = GetFileExcludedMsg(fileToCopy.Value); ReportError(error, msg); } } // Don't use File.Copy because it's asynchronous. - CopyFile(fileToCopy.Key, fileToCopy.Value); + await Task.Run(() => CopyFile(fileToCopy.Key, fileToCopy.Value), cancellationToken); } - _worker.ReportProgress(0, GetSavingFilesMsg(ArchiveType)); + ReportMajorProgressPoint(StringId.SavingFilesInPackage, cancellationToken, false); + + return true; + } + catch (OperationCanceledException) + { + return false; } catch (Exception exception) { - _worker.ReportProgress(0, new KeyValuePair(exception, - string.Format(LocalizationManager.GetString("DialogBoxes.ArchivingDlg.CreatingArchiveErrorMsg", - "There was an error attempting to create the {0} package.", "Parameter is the type of archive (e.g., IMDI)"), ArchiveType))); - - _workerException = true; + ReportError(exception, Progress.GetMessage(StringId.ErrorCreatingArchive)); + return false; } } @@ -447,29 +298,17 @@ internal static string NormalizeDirectoryName(string dirName) return dirName.ToLatinOnly("_", "_", ".-"); } - /// Performs clean-up for the class - public void CleanUp() - { - // delete temp files, etc - } - /// Returns the normalized name to use for the output corpus folder. A sub-directory of OutputFolder public string CorpusDirectoryName { get { // create the output base directory if it doesn't already exist - if (!Directory.Exists(OutputFolder)) - { - Directory.CreateDirectory(OutputFolder); - - if (!Directory.Exists(OutputFolder)) - throw new DirectoryNotFoundException(string.Format("The path {0} was not found.", OutputFolder)); - } + Directory.CreateDirectory(OutputFolder); - if (string.IsNullOrEmpty(_corpusDirectoryName)) + if (IsNullOrEmpty(_corpusDirectoryName)) { - var baseName = NormalizeDirectoryName(_titles[_id] + " " + DateTime.Today.ToISO8601TimeFormatDateOnlyString()); + var baseName = NormalizeDirectoryName(PackageTitle + " " + DateTime.Today.ToISO8601TimeFormatDateOnlyString()); var test = baseName; var i = 1; @@ -484,23 +323,33 @@ public string CorpusDirectoryName } } - /// Adds a new session and returns it - /// - public override IArchivingSession AddSession(string sessionId) + /// ------------------------------------------------------------------------------------ + /// Adds a "session" or "resource bundle". This usually corresponds to a + /// meaningful unit of analysis, e.g., to a piece of data having the same overall + /// content, the same set of actors, and the same location and time (e.g., one + /// elicitation session on topic X, or one folktale, or one ‘matching game’, or one + /// conversation between several speakers). + /// Unique Identifier for this session. + /// The added session, or the existing one if a session with the given sessionId + /// already exists. + /// ------------------------------------------------------------------------------------ + public IArchivingSession AddSession(string sessionId) { // look for existing session - foreach (var sess in _imdiData.Sessions.Where(sess => sess.Name == sessionId)) - return sess; + var session = _imdiData.Sessions.FirstOrDefault(s => s.Name == sessionId); + + if (session != null) + return session; // if not found, add a new session - Session session = new Session {Name = sessionId}; + session = new Session {Name = sessionId}; _imdiData.Sessions.Add(session); return session; } - public override IArchivingPackage ArchivingPackage { get { return _imdiData; } } + public IArchivingPackage ArchivingPackage => _imdiData; /// public new string PathToProgramToLaunch @@ -516,11 +365,7 @@ public override IArchivingSession AddSession(string sessionId) return OtherProgramPath; } } - set - { - // this is just for compatibility - _otherProgramPath = value; - } + set => _otherProgramPath = value; // this is just for compatibility } /// @@ -528,7 +373,7 @@ public string ProgramPreset { get { - if (string.IsNullOrEmpty(_programPreset)) + if (IsNullOrEmpty(_programPreset)) GetSavedValues(); return _programPreset; @@ -545,7 +390,7 @@ public string OtherProgramPath { get { - if (string.IsNullOrEmpty(_programPreset)) + if (IsNullOrEmpty(_programPreset)) GetSavedValues(); return _otherProgramPath; @@ -557,7 +402,7 @@ public string OtherProgramPath } } - private void GetSavedValues() + public void GetSavedValues() { if (File.Exists(_configFileName)) @@ -584,11 +429,10 @@ private void GetSavedValues() } // default to Arbil - if (string.IsNullOrEmpty(_programPreset)) + if (IsNullOrEmpty(_programPreset)) _programPreset = "Arbil"; - if (_otherProgramPath == null) - _otherProgramPath = string.Empty; + _otherProgramPath ??= Empty; } private void SaveProgramValues() @@ -605,11 +449,11 @@ private void SaveProgramValues() /// public string OutputFolder { - get { return _outputFolder; } + get => _outputFolder; set { _outputFolder = value; - PackagePath = !string.IsNullOrEmpty(value)? + PackagePath = !IsNullOrEmpty(value)? Path.Combine(value, CorpusDirectoryName): CorpusDirectoryName; if (_imdiData != null) diff --git a/SIL.Archiving/IMDI/IMDIPackage.cs b/SIL.Archiving/IMDI/IMDIPackage.cs index 31549bdb2..951f36479 100644 --- a/SIL.Archiving/IMDI/IMDIPackage.cs +++ b/SIL.Archiving/IMDI/IMDIPackage.cs @@ -1,8 +1,9 @@ - using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading.Tasks; +using System.Threading; using SIL.Archiving.Generic; using SIL.Archiving.IMDI.Lists; using SIL.Archiving.IMDI.Schema; @@ -14,7 +15,7 @@ namespace SIL.Archiving.IMDI public class IMDIPackage : ArchivingPackage { /// - public MetaTranscript BaseImdiFile { get; private set; } + public MetaTranscript BaseImdiFile { get; } /// The file to import into Arbil public string MainExportFile { get; private set; } @@ -40,15 +41,12 @@ public IMDIPackage(bool corpus, string packagePath) } #region Properties - private IIMDIMajorObject BaseMajorObject - { - get { return (IIMDIMajorObject)BaseImdiFile.Items[0]; } - } + private IIMDIMajorObject BaseMajorObject => (IIMDIMajorObject)BaseImdiFile.Items[0]; /// The path where the corpus imdi file and corpus directory will be created public string PackagePath { - get { return _packagePath; } + get => _packagePath; set { if (_creationStarted) @@ -56,6 +54,12 @@ public string PackagePath _packagePath = value; } } + + /// Generally an IMDI package should have at least one session. (This is not + /// strictly required for a corpus package, though it would be strange to want to + /// archive a corpus with no sessions.) + public bool IsValid => _corpus || Sessions.Any(); + #endregion // **** Corpus Layout **** @@ -65,25 +69,33 @@ public string PackagePath // Test_Corpus\Test_Corpus_Catalog.imdi (catalogue of information) // Test_Corpus\Test_Session (directory) // Test_Corpus\Test_Session.imdi (session meta data file) - // Test_Corpus\Test_Session\Contributors (directory - contains files pertaining to contributers/actors) + // Test_Corpus\Test_Session\Contributors (directory - contains files pertaining to contributors/actors) // Test_Corpus\Test_Session\Files*.* (session files) // Test_Corpus\Contributors\Files*.* (contributor/actor files) - /// Creates the corpus directory structure, meta data files, and copies content files + /// Creates the corpus directory structure, meta data files, and copies content + /// files, checking for cancellation before each IO operation. /// - public bool CreateIMDIPackage() + public async Task CreateIMDIPackage(CancellationToken cancellationToken) { + if (!IsValid) + return false; + _creationStarted = true; // list of session files for the corpus List sessionFiles = new List(); // create the session directories -// ReSharper disable once LoopCanBeConvertedToQuery foreach (var session in Sessions) { - var sessImdi = new MetaTranscript { Items = new object[] { session }, Type = MetatranscriptValueType.SESSION }; - sessionFiles.Add(sessImdi.WriteImdiFile(PackagePath, Name)); + cancellationToken.ThrowIfCancellationRequested(); + + var sessionImdi = new MetaTranscript { + Items = new object[] { session }, + Type = MetatranscriptValueType.SESSION + }; + sessionFiles.Add(await sessionImdi.WriteImdiFile(PackagePath, Name)); } if (!_corpus) @@ -101,16 +113,20 @@ public bool CreateIMDIPackage() foreach (var fileName in sessionFiles) corpus.CorpusLink.Add(new CorpusLinkType { Value = fileName.Replace("\\", "/"), Name = string.Empty }); + cancellationToken.ThrowIfCancellationRequested(); + // create the catalogue - corpus.CatalogueLink = CreateCorpusCatalogue(); + corpus.CatalogueLink = await CreateCorpusCatalogue(); + + cancellationToken.ThrowIfCancellationRequested(); // Create the corpus imdi file - MainExportFile = BaseImdiFile.WriteImdiFile(PackagePath, Name); + MainExportFile = await BaseImdiFile.WriteImdiFile(PackagePath, Name); return true; } - private string CreateCorpusCatalogue() + private async Task CreateCorpusCatalogue() { // Create the package catalogue imdi file var catalogue = new Catalogue @@ -159,7 +175,7 @@ private string CreateCorpusCatalogue() catalogue.Publisher.Add(Publisher); // keys - foreach (var kvp in _keys) + foreach (var kvp in Keys) catalogue.Keys.Key.Add(new KeyType { Name = kvp.Key, Value = kvp.Value }); // access @@ -184,7 +200,7 @@ private string CreateCorpusCatalogue() // write the xml file var catImdi = new MetaTranscript { Items = new object[] { catalogue }, Type = MetatranscriptValueType.CATALOGUE }; - return catImdi.WriteImdiFile(PackagePath, Name).Replace("\\", "/"); + return (await catImdi.WriteImdiFile(PackagePath, Name)).Replace("\\", "/"); } /// Add a description of the package/corpus @@ -195,7 +211,8 @@ private string CreateCorpusCatalogue() foreach (var itm in BaseMajorObject.Description) { if (itm.LanguageId == description.Iso3LanguageId) - throw new InvalidOperationException(string.Format("A description for language {0} has already been set", itm.LanguageId)); + throw new InvalidOperationException( + $"A description for language {itm.LanguageId} has already been set"); } BaseMajorObject.Description.Add(description); @@ -209,10 +226,8 @@ public void AddDescription(string sessionId, LanguageString description) // prevent duplicate description if (_corpus) { - foreach (var sess in Sessions.Where(sess => sess.Name == sessionId)) - { - sess.AddDescription(description); - } + foreach (var session in Sessions.Where(s => s.Name == sessionId)) + session.AddDescription(description); } else { diff --git a/SIL.Archiving/IMDI/Lists/InvalidLanguageCodeException.cs b/SIL.Archiving/IMDI/Lists/InvalidLanguageCodeException.cs new file mode 100644 index 000000000..7101a26f6 --- /dev/null +++ b/SIL.Archiving/IMDI/Lists/InvalidLanguageCodeException.cs @@ -0,0 +1,15 @@ +using System; + +namespace SIL.Archiving.IMDI.Lists +{ + public class InvalidLanguageCodeException : ArgumentException + { + public string Code { get; } + + public InvalidLanguageCodeException(string code, string paramName = null, Exception innerException = null) : + base ("Invalid language code", paramName, innerException) + { + Code = code; + } + } +} diff --git a/SIL.Archiving/IMDI/Lists/ItemListBase.cs b/SIL.Archiving/IMDI/Lists/ItemListBase.cs index 701f46082..5c670dbb7 100644 --- a/SIL.Archiving/IMDI/Lists/ItemListBase.cs +++ b/SIL.Archiving/IMDI/Lists/ItemListBase.cs @@ -19,7 +19,7 @@ public class IMDIListItem : IComparable public string Text { get; protected set; } /// Value suitable for storing in metadata files - public string Value { get; private set; } + public string Value { get; } /// Description to the user (often the same as the text) public string Definition { get; private set; } diff --git a/SIL.Archiving/IMDI/Lists/LanguageList.cs b/SIL.Archiving/IMDI/Lists/LanguageList.cs index 567d0d275..11365d0eb 100644 --- a/SIL.Archiving/IMDI/Lists/LanguageList.cs +++ b/SIL.Archiving/IMDI/Lists/LanguageList.cs @@ -1,7 +1,6 @@ - using System; using System.Linq; -using L10NSharp; +using JetBrains.Annotations; using SIL.Archiving.IMDI.Schema; namespace SIL.Archiving.IMDI.Lists @@ -23,19 +22,14 @@ internal LanguageItem(string englishName, string imdiCode, string otherName) : t } /// This is provided because the XSD uses the term "LanguageId" - public string Id { get { return Value; } } + public string Id => Value; /// public string OtherName { get; set; } /// - public string DisplayName - { - get - { - return string.IsNullOrEmpty(OtherName) ? EnglishName : OtherName; - } - } + [PublicAPI] + public string DisplayName => string.IsNullOrEmpty(OtherName) ? EnglishName : OtherName; /// Convert to a LanguageType object public LanguageType ToLanguageType() @@ -43,7 +37,7 @@ public LanguageType ToLanguageType() var langName = Text; // check for "und" code - if ((Id.EndsWith("und")) && (!string.IsNullOrEmpty(OtherName))) + if (Id.EndsWith("und") && !string.IsNullOrEmpty(OtherName)) langName = OtherName; return new LanguageType @@ -76,15 +70,12 @@ public SubjectLanguageType ToSubjectLanguageType() /// public string EnglishName { - get { return Text; } - set { Text = value; } + get => Text; + set => Text = value; } /// - public string Iso3Code - { - get { return Value.Substring(Value.Length - 3); } - } + public string Iso3Code => Value.Substring(Value.Length - 3); } /// ------------------------------------------------------------------------------------------- @@ -96,9 +87,18 @@ public class LanguageList : IMDIItemList /// private static LanguageList GetList() { - return _instance ?? (_instance = new LanguageList()); + return _instance ??= new LanguageList(); } + /// --------------------------------------------------------------------------------------- + /// + /// Gets a language list based on the Ethnologue + /// + /// Even though this ostensibly claims to use the MPI Languages, this file is + /// woefully incomplete (only ~345 languages, less than 5% of the total). A comment inside + /// it even says "When a language name and identifier that you need is not in this list, + /// please look it up under www.ethnologue.com/web.asp.". So we use the information from + /// SIL.WritingSystems, which is based on the complete Ethnologue data. /// --------------------------------------------------------------------------------------- protected LanguageList() : base(ListType.MPILanguages, false) { @@ -145,14 +145,16 @@ public static LanguageItem FindByISO3Code(string iso3Code, bool mustBeInList) // if not found, and not limited to list, just return the code passed in if (!mustBeInList) return new LanguageItem(string.Empty, "ISO639-3:" + iso3Code.ToLower()); - // if not found, throw exception - var msg = LocalizationManager.GetString("DialogBoxes.ArchivingDlg.InvalidLanguageCode", - "Invalid ISO 639-3 code: {0}"); - throw new ArgumentException(string.Format(msg, iso3Code), "iso3Code"); + throw new InvalidLanguageCodeException(iso3Code, nameof(iso3Code)); } /// ------------------------------------------------------------------------------------------- - /// This finds either English name or localised name + /// + /// Finds information about the language, given its English name (or possibly the localised + /// name or some other variant). + /// + /// Given its actual behavior, this is kind of poorly named. + /// ------------------------------------------------------------------------------------------- public static LanguageItem FindByEnglishName(string englishName) { if (string.IsNullOrEmpty(englishName)) @@ -161,10 +163,8 @@ public static LanguageItem FindByEnglishName(string englishName) var item = GetList().FindByText(englishName); // if not on list, return "und" - if (item == null) - return new LanguageItem(englishName, "ISO639-3:und", englishName); - - return (LanguageItem)(GetList().FindByText(englishName)); + return item == null ? new LanguageItem(englishName, "ISO639-3:und", englishName) + : (LanguageItem)item; } /// ------------------------------------------------------------------------------------------- diff --git a/SIL.Archiving/IMDI/Lists/ListConstructor.cs b/SIL.Archiving/IMDI/Lists/ListConstructor.cs index 2ab14e2fd..4f1724769 100644 --- a/SIL.Archiving/IMDI/Lists/ListConstructor.cs +++ b/SIL.Archiving/IMDI/Lists/ListConstructor.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; namespace SIL.Archiving.IMDI.Lists @@ -34,7 +34,8 @@ public enum RemoveUnknown /// Delegate to use for getting localized versions of the Text and /// Definition of list items. The parameters are: 1) the list name; 2) list item value; /// 3) "Definition" or "Text"; 4) default (English) value. - public static IMDIItemList GetList(string listName, bool uppercaseFirstCharacter, Func localize) + public static IMDIItemList GetList(string listName, bool uppercaseFirstCharacter, + Func localize) { return GetList(listName, uppercaseFirstCharacter, localize, RemoveUnknown.RemoveNone); } @@ -57,9 +58,7 @@ public static IMDIItemList GetList(string listName, bool uppercaseFirstCharacter listName = ListNameWithXmlExtension(listName); var listKey = listName + removeUnknown; - IMDIItemList list; - - if (!_loadedLists.TryGetValue(listKey, out list)) + if (!_loadedLists.TryGetValue(listKey, out var list)) { list = new IMDIItemList(listName, uppercaseFirstCharacter, removeUnknown); diff --git a/SIL.Archiving/IMDI/Schema/IMDIExtensions.cs b/SIL.Archiving/IMDI/Schema/IMDIExtensions.cs index 70c0f6091..f656fc3c3 100644 --- a/SIL.Archiving/IMDI/Schema/IMDIExtensions.cs +++ b/SIL.Archiving/IMDI/Schema/IMDIExtensions.cs @@ -1,4 +1,4 @@ -using SIL.Archiving.Generic; +using SIL.Archiving.Generic; using SIL.Archiving.IMDI.Lists; namespace SIL.Archiving.IMDI.Schema @@ -15,8 +15,7 @@ public static void SetValue(this VocabularyType vocabularyType, string value, bo { if (value == null) return; - if (vocabularyType == null) - vocabularyType = new VocabularyType(); + vocabularyType ??= new VocabularyType(); vocabularyType.Value = value; vocabularyType.Type = isClosedVocabulary diff --git a/SIL.Archiving/IMDI/Schema/IMDI_3_0.cs b/SIL.Archiving/IMDI/Schema/IMDI_3_0.cs index 12f392313..9b904611f 100644 --- a/SIL.Archiving/IMDI/Schema/IMDI_3_0.cs +++ b/SIL.Archiving/IMDI/Schema/IMDI_3_0.cs @@ -8,10 +8,13 @@ using System.Xml.Schema; using System.Xml.Serialization; using System.Diagnostics; +using JetBrains.Annotations; using SIL.Archiving.Generic; using SIL.Archiving.Generic.AccessProtocol; using SIL.Archiving.IMDI.Lists; using SIL.Extensions; +using static SIL.Archiving.IMDI.Lists.ListType; +using System.Threading.Tasks; namespace SIL.Archiving.IMDI.Schema { @@ -29,10 +32,10 @@ public interface IIMDIMajorObject } /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlType(Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] - [XmlRootAttribute("METATRANSCRIPT", Namespace="http://www.mpi.nl/IMDI/Schema/IMDI", IsNullable=false)] + [Serializable] + [DebuggerStepThrough] + [XmlType(Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] + [XmlRoot("METATRANSCRIPT", Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd", IsNullable=false)] public class MetaTranscript { /// @@ -73,9 +76,9 @@ public MetaTranscript(MetatranscriptValueType type) public string SchemaLocation { get; set; } /// - [XmlElementAttribute("Catalogue", typeof(Catalogue))] - [XmlElementAttribute("Corpus", typeof(Corpus))] - [XmlElementAttribute("Session", typeof(Session))] + [XmlElement("Catalogue", typeof(Catalogue))] + [XmlElement("Corpus", typeof(Corpus))] + [XmlElement("Session", typeof(Session))] public object[] Items { get; set; } /// @@ -95,7 +98,7 @@ public MetaTranscript(MetatranscriptValueType type) public string FormatId { get; set; } /// - [XmlAttributeAttribute] + [XmlAttribute] public MetatranscriptValueType Type { get; set; } /// @@ -120,7 +123,6 @@ public override string ToString() var serializer = new XmlSerializer(GetType()); serializer.Serialize(xmlWriter, this); - //return strWriter.ToString(); return Encoding.UTF8.GetString(memStream.ToArray()); } } @@ -132,26 +134,26 @@ public override string ToString() /// /// /// IMDI file name - public string WriteImdiFile(string outputDirectoryName, string corpusName) + public async Task WriteImdiFile(string outputDirectoryName, string corpusName) { string corpusDirectoryName = IMDIArchivingDlgViewModel.NormalizeDirectoryName(corpusName); switch (Type) { case MetatranscriptValueType.CORPUS: - return WriteCorpusImdiFile(outputDirectoryName, corpusDirectoryName); + return await WriteCorpusImdiFile(outputDirectoryName, corpusDirectoryName); case MetatranscriptValueType.CATALOGUE: - return WriteCatalogueImdiFile(outputDirectoryName, corpusDirectoryName); + return await WriteCatalogueImdiFile(outputDirectoryName, corpusDirectoryName); case MetatranscriptValueType.SESSION: - return WriteSessionImdiFile(outputDirectoryName, corpusDirectoryName); + return await WriteSessionImdiFile(outputDirectoryName, corpusDirectoryName); } return null; } - private string WriteCorpusImdiFile(string outputDirectoryName, string corpusDirectoryName) + private async Task WriteCorpusImdiFile(string outputDirectoryName, string corpusDirectoryName) { // create the corpus directory Directory.CreateDirectory(Path.Combine(outputDirectoryName, corpusDirectoryName)); @@ -163,13 +165,13 @@ private string WriteCorpusImdiFile(string outputDirectoryName, string corpusDire var imdiFile = Path.Combine(outputDirectoryName, corpusDirectoryName + ".imdi"); TextWriter writer = new StreamWriter(imdiFile); - writer.Write(ToString()); + await writer.WriteAsync(ToString()); writer.Close(); return imdiFile; } - private string WriteCatalogueImdiFile(string outputDirectoryName, string corpusDirectoryName) + private async Task WriteCatalogueImdiFile(string outputDirectoryName, string corpusDirectoryName) { // create the corpus directory Directory.CreateDirectory(Path.Combine(outputDirectoryName, corpusDirectoryName)); @@ -181,13 +183,13 @@ private string WriteCatalogueImdiFile(string outputDirectoryName, string corpusD var imdiFile = Path.Combine(outputDirectoryName, corpusDirectoryName, corpusDirectoryName + "_Catalogue.imdi"); TextWriter writer = new StreamWriter(imdiFile); - writer.Write(ToString()); + await writer.WriteAsync(ToString()); writer.Close(); return Path.Combine(corpusDirectoryName, corpusDirectoryName + "_Catalogue.imdi"); } - private string WriteSessionImdiFile(string outputDirectoryName, string corpusDirectoryName) + private async Task WriteSessionImdiFile(string outputDirectoryName, string corpusDirectoryName) { // session object Session s = (Session)Items[0]; @@ -207,7 +209,7 @@ private string WriteSessionImdiFile(string outputDirectoryName, string corpusDir var imdiFile = Path.Combine(outputDirectoryName, corpusDirectoryName, sessionDirectoryName + ".imdi"); TextWriter writer = new StreamWriter(imdiFile); - writer.Write(ToString()); + await writer.WriteAsync(ToString()); writer.Close(); return Path.Combine(corpusDirectoryName, sessionDirectoryName + ".imdi"); @@ -255,48 +257,41 @@ private void ArbilCheckSession(Session session) foreach (var actor in session.MDGroup.Actors.Actor) { - if (actor.Role == null) - actor.Role = string.Empty.ToVocabularyType(false, ListType.Link(ListType.ActorRole)); + actor.Role ??= string.Empty.ToVocabularyType(false, Link(ActorRole)); - if (actor.FamilySocialRole == null) - actor.FamilySocialRole = string.Empty.ToVocabularyType(false, ListType.Link(ListType.ActorFamilySocialRole)); + actor.FamilySocialRole ??= string.Empty.ToVocabularyType(false, + Link(ActorFamilySocialRole)); - if (actor.Anonymized == null) - actor.Anonymized = new BooleanType { Link = ListType.Link(ListType.Boolean) }; + actor.Anonymized ??= new BooleanType { Link = Link(ListType.Boolean) }; - if (actor.Sex == null) - actor.Sex = new VocabularyType { Type = VocabularyTypeValueType.ClosedVocabulary, Link = ListType.Link(ListType.ActorSex) }; + actor.Sex ??= new VocabularyType + { Type = VocabularyTypeValueType.ClosedVocabulary, Link = Link(ActorSex) }; } foreach (var file in session.Resources.WrittenResource) { - if (file.SubType == null) - file.SubType = new VocabularyType { Link = ListType.Link(ListType.WrittenResourceSubType) }; + file.SubType ??= new VocabularyType { Link = Link(WrittenResourceSubType) }; - if (file.Validation == null) - file.Validation = new ValidationType - { - Type = new VocabularyType{ Type = VocabularyTypeValueType.ClosedVocabulary, Link = ListType.Link(ListType.ValidationType) }, - Methodology = new VocabularyType { Type = VocabularyTypeValueType.ClosedVocabulary, Link = ListType.Link(ListType.ValidationMethodology) }, - Level = new IntegerType { Value = "Unspecified" }, - Description = new DescriptionTypeCollection { new LanguageString() } - }; + file.Validation ??= new ValidationType + { + Type = new VocabularyType + { Type = VocabularyTypeValueType.ClosedVocabulary, Link = Link(ListType.ValidationType) }, + Methodology = new VocabularyType + { Type = VocabularyTypeValueType.ClosedVocabulary, Link = Link(ValidationMethodology) }, + Level = new IntegerType { Value = "Unspecified" }, + Description = new DescriptionTypeCollection { new LanguageString() } + }; - if (file.Derivation == null) - file.Derivation = new VocabularyType { Type = VocabularyTypeValueType.ClosedVocabulary, Link = ListType.Link(ListType.WrittenResourceDerivation) }; + file.Derivation ??= new VocabularyType + { Type = VocabularyTypeValueType.ClosedVocabulary, Link = Link(WrittenResourceDerivation) }; - if (file.Anonymized == null) - file.Anonymized = new BooleanType { Link = ListType.Link(ListType.Boolean) }; + file.Anonymized ??= new BooleanType { Link = Link(ListType.Boolean) }; - if (file.Access == null) - file.Access = new AccessType(); + file.Access ??= new AccessType(); } foreach (var file in session.Resources.MediaFile) - { - if (file.Access == null) - file.Access = new AccessType(); - } + file.Access ??= new AccessType(); } /// Set the access code on session files if not set already. @@ -306,8 +301,7 @@ private void SetFileAccessCode(Session session) foreach (var file in session.Resources.MediaFile) { - if (file.Access == null) - file.Access = new AccessType(); + file.Access ??= new AccessType(); if (string.IsNullOrEmpty(file.Access.Availability)) file.Access.Availability = session.AccessCode; @@ -315,8 +309,7 @@ private void SetFileAccessCode(Session session) foreach (var file in session.Resources.WrittenResource) { - if (file.Access == null) - file.Access = new AccessType(); + file.Access ??= new AccessType(); if (string.IsNullOrEmpty(file.Access.Availability)) file.Access.Availability = session.AccessCode; @@ -325,9 +318,9 @@ private void SetFileAccessCode(Session session) } /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [DebuggerStepThrough] + [XmlType(Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class CommaSeparatedStringType { /// @@ -336,10 +329,10 @@ public class CommaSeparatedStringType } /// - [XmlIncludeAttribute(typeof(SubjectLanguageType))] - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [XmlInclude(typeof(SubjectLanguageType))] + [Serializable] + [DebuggerStepThrough] + [XmlType(Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class SimpleLanguageType { /// @@ -350,19 +343,19 @@ public class SimpleLanguageType } /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [DebuggerStepThrough] + [XmlType(Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class LanguageNameType : VocabularyType { } /// - [XmlIncludeAttribute(typeof(LanguageNameType))] - [XmlIncludeAttribute(typeof(KeyType))] - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [XmlInclude(typeof(LanguageNameType))] + [XmlInclude(typeof(KeyType))] + [Serializable] + [DebuggerStepThrough] + [XmlType(Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class VocabularyType { /// @@ -388,27 +381,27 @@ public VocabularyType() { } /// - [SerializableAttribute] - [XmlTypeAttribute(Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [XmlType(Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public enum VocabularyTypeValueType { /// ClosedVocabulary, - /// + [PublicAPI] ClosedVocabularyList, /// OpenVocabulary, - /// + [PublicAPI] OpenVocabularyList, } /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [DebuggerStepThrough] + [XmlType(Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class KeyType { /// @@ -421,35 +414,43 @@ public class KeyType } /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [DebuggerStepThrough] + [XmlType(Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class SubjectLanguageType : SimpleLanguageType { private DescriptionTypeCollection _descriptionField; - /// + /// + /// Is it the most frequently used language in the document. Only applicable if used in the context of the resource's language + /// + [PublicAPI] public BooleanType Dominant { get; set; } - /// + /// + /// Source language of translation. Only applicable in case it is the context of a lexicon resource + /// public BooleanType SourceLanguage { get; set; } - /// + /// + /// Target language of translation. Only applicable in case it is the context of a lexicon resource + /// + [PublicAPI] public BooleanType TargetLanguage { get; set; } /// - [XmlElementAttribute("Description")] + [XmlElement("Description")] public DescriptionTypeCollection Description { - get { return _descriptionField ?? (_descriptionField = new DescriptionTypeCollection()); } - set { _descriptionField = value; } + get => _descriptionField ??= new DescriptionTypeCollection(); + set => _descriptionField = value; } } /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [DebuggerStepThrough] + [XmlType(Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class BooleanType { /// @@ -475,9 +476,9 @@ public BooleanType() { } /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [DebuggerStepThrough] + [XmlType(Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public partial class DescriptionType { /// @@ -502,9 +503,9 @@ public partial class DescriptionType } /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [DebuggerStepThrough] + [XmlType(Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class Catalogue : IIMDIMajorObject { /// @@ -617,17 +618,17 @@ public Catalogue() } /// - /// I think this is the DocumentLanguages subelement of a Catalogue element - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(AnonymousType=true, Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + /// I think this is the DocumentLanguages sub-element of a Catalogue element + [Serializable] + [DebuggerStepThrough] + [XmlType(AnonymousType=true, Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class CatalogueDocumentLanguages : IMDIDescription { [XmlElement("Description")] public DescriptionTypeCollection Description { - get { return DescriptionInternal; } - set { DescriptionInternal = value; } + get => DescriptionInternal; + set => DescriptionInternal = value; } /// @@ -642,16 +643,16 @@ public CatalogueDocumentLanguages() } /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(AnonymousType=true, Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [DebuggerStepThrough] + [XmlType(AnonymousType=true, Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class CatalogueSubjectLanguages : IMDIDescription { [XmlElement("Description")] public DescriptionTypeCollection Description { - get { return DescriptionInternal; } - set { DescriptionInternal = value; } + get => DescriptionInternal; + set => DescriptionInternal = value; } /// @@ -666,16 +667,16 @@ public CatalogueSubjectLanguages() } /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [DebuggerStepThrough] + [XmlType(Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class LocationType { /// public LocationType() { - Continent = new VocabularyType { Type = VocabularyTypeValueType.ClosedVocabulary, Link = ListType.Link(ListType.Continents) }; - Country = new VocabularyType { Link = ListType.Link(ListType.Countries) }; + Continent = new VocabularyType { Type = VocabularyTypeValueType.ClosedVocabulary, Link = Link(Continents) }; + Country = new VocabularyType { Link = Link(Countries) }; Region = new List(); } @@ -701,8 +702,8 @@ public LocationType(ArchivingLocation location) : this() /// Closed vocabulary public void SetContinent(string continent) { - var continentList = ListConstructor.GetClosedList(ListType.Continents, false, ListConstructor.RemoveUnknown.RemoveNone); - Continent = continentList.FindByValue(continent).ToVocabularyType(VocabularyTypeValueType.ClosedVocabulary, ListType.Link(ListType.Continents)); + var continentList = ListConstructor.GetClosedList(Continents, false, ListConstructor.RemoveUnknown.RemoveNone); + Continent = continentList.FindByValue(continent).ToVocabularyType(VocabularyTypeValueType.ClosedVocabulary, Link(Continents)); } /// Open vocabulary @@ -711,11 +712,11 @@ public void SetContinent(string continent) /// public void SetCountry(string country) { - Country = IMDISchemaHelper.SetVocabulary(country, false, ListType.Link(ListType.Countries)); + Country = IMDISchemaHelper.SetVocabulary(country, false, Link(Countries)); } /// - [XmlElementAttribute("Region")] + [XmlElement("Region")] public List Region { get; set; } /// @@ -741,9 +742,9 @@ public ArchivingLocation ToArchivingLocation() } /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(AnonymousType=true, Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [DebuggerStepThrough] + [XmlType(AnonymousType=true, Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class CatalogueFormat { /// @@ -760,9 +761,9 @@ public class CatalogueFormat } /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(AnonymousType=true, Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [DebuggerStepThrough] + [XmlType(AnonymousType=true, Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class CatalogueQuality { /// @@ -779,9 +780,9 @@ public class CatalogueQuality } /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [DebuggerStepThrough] + [XmlType(Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class Project : IIMDIMajorObject { /// @@ -842,9 +843,9 @@ public Project(IMDIPackage package) } /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [DebuggerStepThrough] + [XmlType(Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class ContactType { /// @@ -861,9 +862,9 @@ public class ContactType } /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [DebuggerStepThrough] + [XmlType(Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class AccessType : IMDIDescription { /// initialize for Arbil @@ -908,15 +909,15 @@ public AccessType(IArchivingPackage package) [XmlElement("Description")] public DescriptionTypeCollection Description { - get { return DescriptionInternal; } - set { DescriptionInternal = value; } + get => DescriptionInternal; + set => DescriptionInternal = value; } } /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [DebuggerStepThrough] + [XmlType(Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class KeysType { private List _keyField; @@ -925,15 +926,15 @@ public class KeysType [XmlElement("Key")] public List Key { - get { return _keyField ?? (_keyField = new List()); } - set { _keyField = value; } + get => _keyField ??= new List(); + set => _keyField = value; } } /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [DebuggerStepThrough] + [XmlType(Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class Corpus : IIMDIMajorObject { /// @@ -982,9 +983,9 @@ public Corpus() } /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [DebuggerStepThrough] + [XmlType(Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class MDGroupType { /// @@ -1015,9 +1016,9 @@ public MDGroupType() } /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [DebuggerStepThrough] + [XmlType(Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class ContentType { /// @@ -1079,87 +1080,82 @@ public void AddLanguage(ArchivingLanguage language, BooleanEnum dominantLanguage /// public void SetInteractivity(string interactivity) { - IMDIItemList list = ListConstructor.GetClosedList(ListType.ContentInteractivity); - CommunicationContext.Interactivity = list.FindByValue(interactivity).ToVocabularyType(VocabularyTypeValueType.ClosedVocabulary, ListType.Link(ListType.ContentInteractivity)); + IMDIItemList list = ListConstructor.GetClosedList(ContentInteractivity); + CommunicationContext.Interactivity = list.FindByValue(interactivity).ToVocabularyType(VocabularyTypeValueType.ClosedVocabulary, Link(ContentInteractivity)); } /// public void SetPlanningType(string planningType) { - IMDIItemList list = ListConstructor.GetClosedList(ListType.ContentPlanningType, DontRequireUppercaseFirstCharacter); - CommunicationContext.PlanningType = list.FindByValue(planningType).ToVocabularyType(VocabularyTypeValueType.ClosedVocabulary, ListType.Link(ListType.ContentPlanningType)); + IMDIItemList list = ListConstructor.GetClosedList(ContentPlanningType, DontRequireUppercaseFirstCharacter); + CommunicationContext.PlanningType = list.FindByValue(planningType).ToVocabularyType(VocabularyTypeValueType.ClosedVocabulary, Link(ContentPlanningType)); } /// public void SetInvolvement(string involvement) { - IMDIItemList list = ListConstructor.GetClosedList(ListType.ContentInvolvement, DontRequireUppercaseFirstCharacter); - CommunicationContext.Involvement = list.FindByValue(involvement).ToVocabularyType(VocabularyTypeValueType.ClosedVocabulary, ListType.Link(ListType.ContentInvolvement)); + IMDIItemList list = ListConstructor.GetClosedList(ContentInvolvement, DontRequireUppercaseFirstCharacter); + CommunicationContext.Involvement = list.FindByValue(involvement).ToVocabularyType(VocabularyTypeValueType.ClosedVocabulary, Link(ContentInvolvement)); } /// public void SetSocialContext(string socialContext) { - IMDIItemList list = ListConstructor.GetClosedList(ListType.ContentSocialContext); - CommunicationContext.SocialContext = list.FindByValue(socialContext).ToVocabularyType(VocabularyTypeValueType.ClosedVocabulary, ListType.Link(ListType.ContentSocialContext)); + IMDIItemList list = ListConstructor.GetClosedList(ContentSocialContext); + CommunicationContext.SocialContext = list.FindByValue(socialContext).ToVocabularyType(VocabularyTypeValueType.ClosedVocabulary, Link(ContentSocialContext)); } /// public void SetEventStructure(string eventStructure) { - IMDIItemList list = ListConstructor.GetClosedList(ListType.ContentEventStructure); - CommunicationContext.EventStructure = list.FindByValue(eventStructure).ToVocabularyType(VocabularyTypeValueType.ClosedVocabulary, ListType.Link(ListType.ContentEventStructure)); + IMDIItemList list = ListConstructor.GetClosedList(ContentEventStructure); + CommunicationContext.EventStructure = list.FindByValue(eventStructure).ToVocabularyType(VocabularyTypeValueType.ClosedVocabulary, Link(ContentEventStructure)); } /// public void SetChannel(string channel) { - IMDIItemList list = ListConstructor.GetClosedList(ListType.ContentChannel); - CommunicationContext.Channel = list.FindByValue(channel).ToVocabularyType(VocabularyTypeValueType.ClosedVocabulary, ListType.Link(ListType.ContentChannel)); + IMDIItemList list = ListConstructor.GetClosedList(ContentChannel); + CommunicationContext.Channel = list.FindByValue(channel).ToVocabularyType(VocabularyTypeValueType.ClosedVocabulary, Link(ContentChannel)); } /// public void CheckRequiredFields() { - if (Genre == null) - Genre = new VocabularyType { Link = ListType.Link(ListType.ContentGenre) }; + Genre ??= new VocabularyType { Link = Link(ContentGenre) }; - if (SubGenre == null) - SubGenre = new VocabularyType { Link = ListType.Link(ListType.ContentSubGenre) }; + SubGenre ??= new VocabularyType { Link = Link(ContentSubGenre) }; - if (Task == null) - Task = new VocabularyType { Link = ListType.Link(ListType.ContentTask) }; + Task ??= new VocabularyType { Link = Link(ContentTask) }; - if (Modalities == null) - Modalities = new VocabularyType { Link = ListType.Link(ListType.ContentModalities) }; + Modalities ??= new VocabularyType { Link = Link(ContentModalities) }; - if (Subject == null) - Subject = new ContentTypeSubject { Link = ListType.Link(ListType.ContentSubject) }; + Subject ??= new ContentTypeSubject { Link = Link(ContentSubject) }; - if (CommunicationContext.Interactivity == null) - CommunicationContext.Interactivity = new VocabularyType { Type = VocabularyTypeValueType.ClosedVocabulary, Link = ListType.Link(ListType.ContentInteractivity) }; + CommunicationContext.Interactivity ??= new VocabularyType + { Type = VocabularyTypeValueType.ClosedVocabulary, Link = Link(ContentInteractivity) }; - if (CommunicationContext.PlanningType == null) - CommunicationContext.PlanningType = new VocabularyType { Type = VocabularyTypeValueType.ClosedVocabulary, Link = ListType.Link(ListType.ContentPlanningType) }; + CommunicationContext.PlanningType ??= new VocabularyType + { Type = VocabularyTypeValueType.ClosedVocabulary, Link = Link(ContentPlanningType) }; - if (CommunicationContext.Involvement == null) - CommunicationContext.Involvement = new VocabularyType { Type = VocabularyTypeValueType.ClosedVocabulary, Link = ListType.Link(ListType.ContentInvolvement) }; + CommunicationContext.Involvement ??= new VocabularyType + { Type = VocabularyTypeValueType.ClosedVocabulary, Link = Link(ContentInvolvement) }; - if (CommunicationContext.SocialContext == null) - CommunicationContext.SocialContext = new VocabularyType { Type = VocabularyTypeValueType.ClosedVocabulary, Link = ListType.Link(ListType.ContentSocialContext) }; + CommunicationContext.SocialContext ??= new VocabularyType + { Type = VocabularyTypeValueType.ClosedVocabulary, Link = Link(ContentSocialContext) }; - if (CommunicationContext.EventStructure == null) - CommunicationContext.EventStructure = new VocabularyType { Type = VocabularyTypeValueType.ClosedVocabulary, Link = ListType.Link(ListType.ContentEventStructure) }; + CommunicationContext.EventStructure ??= new VocabularyType + { Type = VocabularyTypeValueType.ClosedVocabulary, Link = Link(ContentEventStructure) }; - if (CommunicationContext.Channel == null) - CommunicationContext.Channel = new VocabularyType { Type = VocabularyTypeValueType.ClosedVocabulary, Link = ListType.Link(ListType.ContentChannel) }; + CommunicationContext.Channel ??= new VocabularyType + { Type = VocabularyTypeValueType.ClosedVocabulary, Link = Link(ContentChannel) }; } } /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(AnonymousType = true, Namespace = "http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [DebuggerStepThrough] + [XmlType(AnonymousType = true, Namespace = "https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class ContentTypeCommunicationContext { /// Closed vocabulary, Content-Interactivity.xml @@ -1182,9 +1178,9 @@ public class ContentTypeCommunicationContext } /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(AnonymousType=true, Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [DebuggerStepThrough] + [XmlType(AnonymousType=true, Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class ContentTypeSubject : VocabularyType { /// @@ -1193,9 +1189,9 @@ public class ContentTypeSubject : VocabularyType } /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [DebuggerStepThrough] + [XmlType(Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class LanguagesType : IMDIDescription { private List _languageField; @@ -1208,10 +1204,10 @@ public DescriptionTypeCollection Description } /// - [XmlElementAttribute("Language")] + [XmlElement("Language")] public List Language { - get { return _languageField ?? (_languageField = new List()); } - set { _languageField = value; } + get => _languageField ??= new List(); + set => _languageField = value; } /// @@ -1223,9 +1219,9 @@ public static LanguageType GetLanguage(string iso3CodeOrEnglishName) } /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [DebuggerStepThrough] + [XmlType(Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class LanguageType : IMDIDescription { /// @@ -1253,8 +1249,8 @@ public class LanguageType : IMDIDescription [XmlElement("Description")] public DescriptionTypeCollection Description { - get { return DescriptionInternal; } - set { DescriptionInternal = value; } + get => DescriptionInternal; + set => DescriptionInternal = value; } /// @@ -1263,9 +1259,9 @@ public DescriptionTypeCollection Description } /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [DebuggerStepThrough] + [XmlType(Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class ActorsType : IMDIDescription { private List _actorField; @@ -1273,22 +1269,22 @@ public class ActorsType : IMDIDescription [XmlElement("Description")] public DescriptionTypeCollection Description { - get { return DescriptionInternal; } - set { DescriptionInternal = value; } + get => DescriptionInternal; + set => DescriptionInternal = value; } /// - [XmlElementAttribute("Actor")] + [XmlElement("Actor")] public List Actor { - get { return _actorField ?? (_actorField = new List()); } - set { _actorField = value; } + get => _actorField ??= new List(); + set => _actorField = value; } } /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [DebuggerStepThrough] + [XmlType(Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class ActorType { private LanguagesType _languagesField; @@ -1352,7 +1348,7 @@ public ActorType(ArchivingActor actor) : this() // Role if (!string.IsNullOrEmpty(actor.Role)) - Role = actor.Role.ToVocabularyType(false, ListType.Link(ListType.ActorRole)); ; + Role = actor.Role.ToVocabularyType(false, Link(ActorRole)); ; // Occupation if (!string.IsNullOrEmpty(actor.Occupation)) @@ -1371,7 +1367,7 @@ public ActorType(ArchivingActor actor) : this() public VocabularyType Role { get; set; } /// - [XmlElementAttribute("Name")] + [XmlElement("Name")] public string[] Name { get; set; } /// @@ -1387,8 +1383,8 @@ public ActorType(ArchivingActor actor) : this() [XmlElement("Languages")] public LanguagesType Languages { - get { return _languagesField ?? (_languagesField = new LanguagesType()); } - set { _languagesField = value; } + get => _languagesField ??= new LanguagesType(); + set => _languagesField = value; } /// @@ -1418,8 +1414,8 @@ public void SetBirthDate(string birthDate) /// public void SetSex(string gender) { - IMDIItemList genderList = ListConstructor.GetClosedList(ListType.ActorSex); - Sex = genderList.FindByValue(gender).ToVocabularyType(VocabularyTypeValueType.ClosedVocabulary, ListType.Link(ListType.ActorSex)); + IMDIItemList genderList = ListConstructor.GetClosedList(ActorSex); + Sex = genderList.FindByValue(gender).ToVocabularyType(VocabularyTypeValueType.ClosedVocabulary, Link(ActorSex)); } /// @@ -1435,11 +1431,11 @@ public void AddLanguage(ArchivingLanguage language, BooleanEnum primaryLanguage, if (primaryLanguage == BooleanEnum.Unspecified) imdiLanguage.PrimaryLanguage = null; else - imdiLanguage.PrimaryLanguage = new BooleanType { Value = primaryLanguage, Link = ListType.Link(ListType.Boolean) }; + imdiLanguage.PrimaryLanguage = new BooleanType { Value = primaryLanguage, Link = Link(ListType.Boolean) }; if (motherTongue == BooleanEnum.Unspecified) imdiLanguage.MotherTongue = null; else - imdiLanguage.MotherTongue = new BooleanType { Value = motherTongue, Link = ListType.Link(ListType.Boolean) }; + imdiLanguage.MotherTongue = new BooleanType { Value = motherTongue, Link = Link(ListType.Boolean) }; Languages.Language.Add(imdiLanguage); } @@ -1458,14 +1454,14 @@ public void AddLanguage(ArchivingLanguage language, BooleanEnum primaryLanguage, public DescriptionTypeCollection Description { get; set; } /// - [XmlAttributeAttribute] + [XmlAttribute] public string ResourceRef { get; set; } // TODO: Do we need this method? /// public ArchivingActor ToArchivingActor() { - ArchivingActor actr = new ArchivingActor + ArchivingActor actor = new ArchivingActor { Age = Age, Education = Education, @@ -1473,38 +1469,38 @@ public ArchivingActor ToArchivingActor() }; if (Sex != null) - actr.Gender = Sex.Value; + actor.Gender = Sex.Value; if (Name.Length > 0) - actr.Name = Name[0]; + actor.Name = Name[0]; if (!string.IsNullOrEmpty(Role.Value)) - actr.Role = Role.Value; + actor.Role = Role.Value; if (!string.IsNullOrEmpty(BirthDate)) - actr.BirthDate = BirthDate; + actor.BirthDate = BirthDate; foreach (LanguageType lang in Languages.Language) { var iso3 = lang.Id.Substring(lang.Id.Length - 3); var archLanguage = new ArchivingLanguage(iso3, lang.Name[0].Value); - actr.Iso3Languages.Add(archLanguage); + actor.Iso3Languages.Add(archLanguage); if (lang.PrimaryLanguage.Value == BooleanEnum.@true) - actr.PrimaryLanguage = archLanguage; + actor.PrimaryLanguage = archLanguage; if (lang.MotherTongue.Value == BooleanEnum.@true) - actr.MotherTongueLanguage = archLanguage; + actor.MotherTongueLanguage = archLanguage; } - return actr; + return actor; } } /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [DebuggerStepThrough] + [XmlType(Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class CorpusLinkType : ResourceLinkType { /// @@ -1513,10 +1509,10 @@ public class CorpusLinkType : ResourceLinkType } /// - [XmlIncludeAttribute(typeof(CorpusLinkType))] - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [XmlInclude(typeof(CorpusLinkType))] + [Serializable] + [DebuggerStepThrough] + [XmlType(Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class ResourceLinkType { /// @@ -1528,13 +1524,16 @@ public class ResourceLinkType public string Value { get; set; } } - /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + /// Groups data about name conversions for persons who are anonymised> + [Serializable] + [DebuggerStepThrough] + [XmlType(Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class AnonymsType { - /// + /// + /// URL to information to convert pseudo named to real-names + /// + [PublicAPI] public ResourceLinkType ResourceLink { get; set; } /// @@ -1542,9 +1541,9 @@ public class AnonymsType } /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [DebuggerStepThrough] + [XmlType(Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class CounterPositionType { /// @@ -1555,9 +1554,9 @@ public class CounterPositionType } /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [DebuggerStepThrough] + [XmlType(Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class IntegerType { /// @@ -1566,9 +1565,9 @@ public class IntegerType } /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [DebuggerStepThrough] + [XmlType(Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class SourceType : IMDIDescription { /// @@ -1580,7 +1579,10 @@ public class SourceType : IMDIDescription /// public QualityType Quality { get; set; } - /// + /// + /// Position (start (+end) ) on a old fashioned tape without time indication + /// + [PublicAPI] public CounterPositionType CounterPosition { get; set; } /// @@ -1589,8 +1591,8 @@ public class SourceType : IMDIDescription [XmlElement("Description")] public DescriptionTypeCollection Description { - get { return DescriptionInternal; } - set { DescriptionInternal = value; } + get => DescriptionInternal; + set => DescriptionInternal = value; } /// @@ -1602,9 +1604,9 @@ public DescriptionTypeCollection Description } /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [DebuggerStepThrough] + [XmlType(Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class QualityType { /// @@ -1624,9 +1626,9 @@ public QualityType() {} } /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [DebuggerStepThrough] + [XmlType(Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class ValidationType { /// @@ -1650,9 +1652,9 @@ public ValidationType() } /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [DebuggerStepThrough] + [XmlType(Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class WrittenResourceType : IIMDISessionFile { /// @@ -1735,9 +1737,9 @@ public void AddDescription(LanguageString description) } /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [DebuggerStepThrough] + [XmlType(Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class MediaFileType : IIMDISessionFile { /// @@ -1799,10 +1801,15 @@ public void AddDescription(LanguageString description) public string OutputDirectory { get; set; } } - /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + /// + /// A "session" (aka, "resource bundle") usually corresponds to a meaningful unit of analysis, + /// e.g., to a piece of data having the same overall content, the same set of actors, and the + /// same location and time (e.g., one elicitation session on topic X, or one folktale, or one + /// ‘matching game’, or one conversation between several speakers). + /// + [Serializable] + [DebuggerStepThrough] + [XmlType(Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class Session : IIMDIMajorObject, IArchivingSession { private static int _anonymizedCounter = 1; @@ -1816,7 +1823,7 @@ public Session() References = new SessionTypeReferences(); } - /// Name of object + /// Name of session [XmlElement("Name")] public string Name { get; set; } @@ -1863,7 +1870,7 @@ public void SetDate(int year) [XmlIgnore] public ArchivingLocation Location { - get { return MDGroup.Location.ToArchivingLocation(); } + get => MDGroup.Location.ToArchivingLocation(); set { var loc = MDGroup.Location; @@ -1904,8 +1911,7 @@ public void AddProject(ArchivingPackage package) } }; var imdiPackage = package as IMDIPackage; - var corpus = imdiPackage?.BaseImdiFile?.Items?.FirstOrDefault() as Corpus; - if (corpus != null) + if (imdiPackage?.BaseImdiFile?.Items?.FirstOrDefault() is Corpus corpus) project.Description.Add(corpus.Description.FirstOrDefault()); MDGroup.Project = new List{ project }; } @@ -1985,27 +1991,23 @@ public void AddContentKeyValuePair(string key, string value) public void AddFileKeyValuePair(string fullFileName, string key, string value) { var sessionFile = GetFile(fullFileName); - if (sessionFile == null) return; - if (sessionFile is MediaFileType) - { - var audioOrVisualFile = sessionFile as MediaFileType; + if (sessionFile == null) + return; + if (sessionFile is MediaFileType audioOrVisualFile) audioOrVisualFile.Keys.Key.Add(new KeyType { Name = key, Value = value }); - } - else if (sessionFile is WrittenResourceType) - { - var writtenResourceFile = sessionFile as WrittenResourceType; + else if (sessionFile is WrittenResourceType writtenResourceFile) writtenResourceFile.Keys.Key.Add(new KeyType { Name = key, Value = value }); - } } public void AddFileDescription(string fullFileName, LanguageString description) { var sessionFile = GetFile(fullFileName); - if (sessionFile == null) return; + if (sessionFile == null) + return; // IMDIFile.cs line 160 adds a default value which will block the adding of the first entry if (sessionFile.Description.Count == 1 && - sessionFile.Description.FirstOrDefault().Value == null) + sessionFile.Description.FirstOrDefault()?.Value == null) sessionFile.Description.Remove(sessionFile.Description.FirstOrDefault()); sessionFile.Description.Add(description); @@ -2014,7 +2016,8 @@ public void AddFileDescription(string fullFileName, LanguageString description) public void AddActorContact(ArchivingActor actor, ArchivingContact contact) { var actorType = GetActor(actor.FullName); - if (actorType == null) return; + if (actorType == null) + return; actorType.Contact = new ContactType { Name = contact.Name, @@ -2027,8 +2030,8 @@ public void AddActorContact(ArchivingActor actor, ArchivingContact contact) public void AddMediaFileTimes(string fullFileName, string start, string stop) { var sessionFile = GetFile(fullFileName); - if (!(sessionFile is MediaFileType)) return; - var audioOrVisualFile = sessionFile as MediaFileType; + if (!(sessionFile is MediaFileType audioOrVisualFile)) + return; audioOrVisualFile.TimePosition.Start = start; audioOrVisualFile.TimePosition.End = stop; } @@ -2068,22 +2071,13 @@ private ActorType GetActor(string fullName) /// private IIMDISessionFile GetFile(string fileName) { - foreach (var fileType in Resources.MediaFile) - { - if (fileType.FullPathAndFileName == fileName) - return fileType; - } - foreach (var writtenResourceType in Resources.WrittenResource) - { - if (writtenResourceType.FullPathAndFileName == fileName) - return writtenResourceType; - } - return null; + return Resources.MediaFile.FirstOrDefault(fileType => fileType.FullPathAndFileName == fileName) as IIMDISessionFile ?? + Resources.WrittenResource.FirstOrDefault(writtenResourceType => writtenResourceType.FullPathAndFileName == fileName); } /// [XmlIgnore] - public List Files + public IReadOnlyList Files { get { @@ -2099,126 +2093,85 @@ public List Files [XmlIgnore] public string Genre { - get - { - return MDGroup.Content.Genre == null ? null : MDGroup.Content.Genre.Value; - } - set - { - MDGroup.Content.Genre = value?.Replace("<","")?.Replace(">","").ToVocabularyType(false, ListType.Link(ListType.ContentGenre)); - } + get => MDGroup.Content.Genre?.Value; + set => MDGroup.Content.Genre = value?.Replace("<","")?.Replace(">","") + .ToVocabularyType(false, Link(ContentGenre)); } /// [XmlIgnore] public string SubGenre { - get - { - return MDGroup.Content.SubGenre == null ? null : MDGroup.Content.SubGenre.Value; - } - set - { - MDGroup.Content.SubGenre = value.ToVocabularyType(false, ListType.Link(ListType.ContentSubGenre)); - } + get => MDGroup.Content.SubGenre?.Value; + set => MDGroup.Content.SubGenre = value.ToVocabularyType(false, Link(ContentSubGenre)); } /// [XmlIgnore] public string Interactivity { - get - { - return MDGroup.Content.CommunicationContext.Interactivity == null ? null : MDGroup.Content.CommunicationContext.Interactivity.Value; - } - set - { - MDGroup.Content.SetInteractivity(value); - } + get => MDGroup.Content.CommunicationContext.Interactivity?.Value; + set => MDGroup.Content.SetInteractivity(value); } /// [XmlIgnore] public string Involvement { - get - { - return MDGroup.Content.CommunicationContext.Involvement == null ? null : MDGroup.Content.CommunicationContext.Involvement.Value; - } - set - { - MDGroup.Content.SetInvolvement(value); - } + get => MDGroup.Content.CommunicationContext.Involvement?.Value; + set => MDGroup.Content.SetInvolvement(value); } /// [XmlIgnore] public string PlanningType { - get - { - return MDGroup.Content.CommunicationContext.PlanningType == null ? null : MDGroup.Content.CommunicationContext.PlanningType.Value; - } - set - { - MDGroup.Content.SetPlanningType(value); - } + get => MDGroup.Content.CommunicationContext.PlanningType?.Value; + set => MDGroup.Content.SetPlanningType(value); } /// [XmlIgnore] public string SocialContext { - get - { - return MDGroup.Content.CommunicationContext.SocialContext == null ? null : MDGroup.Content.CommunicationContext.SocialContext.Value; - } - set - { - MDGroup.Content.SetSocialContext(value); - } + get => MDGroup.Content.CommunicationContext.SocialContext?.Value; + set => MDGroup.Content.SetSocialContext(value); } /// [XmlIgnore] public string Task { - get - { - return MDGroup.Content.Task == null ? null : MDGroup.Content.Task.Value; - } - set - { - MDGroup.Content.Task = value.ToVocabularyType(false, ListType.Link(ListType.ContentTask)); - } + get => MDGroup.Content.Task?.Value; + set => MDGroup.Content.Task = value.ToVocabularyType(false, Link(ContentTask)); } } /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(AnonymousType=true, Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [DebuggerStepThrough] + [XmlType(AnonymousType=true, Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class SessionResources { private List _mediaFileField; private List _writtenResourceField; /// - [XmlElementAttribute("MediaFile")] + [XmlElement("MediaFile")] public List MediaFile { - get { return _mediaFileField ?? (_mediaFileField = new List()); } - set { _mediaFileField = value; } + get => _mediaFileField ??= new List(); + set => _mediaFileField = value; } /// - [XmlElementAttribute("WrittenResource")] + [XmlElement("WrittenResource")] public List WrittenResource { - get { return _writtenResourceField ?? (_writtenResourceField = new List()); } - set { _writtenResourceField = value; } + get { return _writtenResourceField ??= new List(); } + set => _writtenResourceField = value; } /// - [XmlElementAttribute("Source")] + [XmlElement("Source")] public SourceType[] Source { get; set; } /// @@ -2226,22 +2179,22 @@ public List WrittenResource { } /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(AnonymousType=true, Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [DebuggerStepThrough] + [XmlType(AnonymousType=true, Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class SessionReferences : IMDIDescription { [XmlElement("Description")] public DescriptionTypeCollection Description { - get { return DescriptionInternal; } - set { DescriptionInternal = value; } + get => DescriptionInternal; + set => DescriptionInternal = value; } } /// - [SerializableAttribute] - [XmlTypeAttribute(Namespace="http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [XmlType(Namespace="https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public enum MetatranscriptValueType { // ReSharper disable InconsistentNaming @@ -2249,36 +2202,37 @@ public enum MetatranscriptValueType SESSION, /// - [XmlEnumAttribute("SESSION.Profile")] + [XmlEnum("SESSION.Profile")] SESSIONProfile, /// + [PublicAPI] LEXICON_RESOURCE_BUNDLE, /// - [XmlEnumAttribute("LEXICON_RESOURCE_BUNDLE.Profile")] + [XmlEnum("LEXICON_RESOURCE_BUNDLE.Profile")] LEXICON_RESOURCE_BUNDLEProfile, /// CATALOGUE, /// - [XmlEnumAttribute("CATALOGUE.Profile")] + [XmlEnum("CATALOGUE.Profile")] CATALOGUEProfile, /// CORPUS, /// - [XmlEnumAttribute("CORPUS.Profile")] + [XmlEnum("CORPUS.Profile")] CORPUSProfile, // ReSharper restore InconsistentNaming } /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(Namespace = "http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [DebuggerStepThrough] + [XmlType(Namespace = "https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class TimePositionRangeType { /// @@ -2296,9 +2250,9 @@ public TimePositionRangeType() } /// - [SerializableAttribute] - [DebuggerStepThroughAttribute] - [XmlTypeAttribute(AnonymousType = true, Namespace = "http://www.mpi.nl/IMDI/Schema/IMDI")] + [Serializable] + [DebuggerStepThrough] + [XmlType(AnonymousType = true, Namespace = "https://www.mpi.nl/IMDI/Schema/IMDI_3.0.xsd")] public class SessionTypeReferences { /// diff --git a/SIL.Archiving/IMDI/Schema/IMDI_3_0_Fix.py b/SIL.Archiving/IMDI/Schema/IMDI_3_0_Fix.py index bdc66d7ac..318336d79 100644 --- a/SIL.Archiving/IMDI/Schema/IMDI_3_0_Fix.py +++ b/SIL.Archiving/IMDI/Schema/IMDI_3_0_Fix.py @@ -79,7 +79,7 @@ using System.Collections.Generic; using System.Xml.Schema; -namespace SIL.Archiving.IMDI.Schema +namespace SIL.Windows.Forms.Archiving.IMDI.Schema { ''' diff --git a/SIL.Archiving/IMDI/xsd/GenerateClasses.bat b/SIL.Archiving/IMDI/xsd/GenerateClasses.bat index 529dc7389..c1ddd0e91 100644 --- a/SIL.Archiving/IMDI/xsd/GenerateClasses.bat +++ b/SIL.Archiving/IMDI/xsd/GenerateClasses.bat @@ -2,11 +2,11 @@ rem for /f "tokens=2-8 delims=.:/ " %%a in ("%date% %time%") do set DateNtime=%%c-%%a-%%b_%%d-%%e-%%f.%%g for /f "tokens=2-8 delims=.:/ " %%a in ("%date% %time%") do set DateNtime=%%c%%a%%b%%d%%e%%f echo Renaming existing file... -rename "C:\Projects\Palaso\SIL.Archiving\IMDI\Schema\IMDI_3_0.cs" "IMDI_3_0.cs.%DateNtime%.bak" +rename "C:\Projects\Palaso\SIL.Windows.Forms.Archiving\IMDI\Schema\IMDI_3_0.cs" "IMDI_3_0.cs.%DateNtime%.bak" echo. -"C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\x64\xsd.exe" -c -l:c# -n:IMDI.Schema "C:\Projects\Palaso\SIL.Archiving\IMDI\xsd\IMDI_3.0.xsd" -o:"C:\Projects\Palaso\SIL.Archiving\IMDI\Schema" +"C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\x64\xsd.exe" -c -l:c# -n:IMDI.Schema "C:\Projects\Palaso\SIL.Windows.Forms.Archiving\IMDI\xsd\IMDI_3.0.xsd" -o:"C:\Projects\Palaso\SIL.Windows.Forms.Archiving\IMDI\Schema" echo. echo Fixing output file... -cd C:\Projects\Palaso\SIL.Archiving\IMDI\Schema +cd C:\Projects\Palaso\SIL.Windows.Forms.Archiving\IMDI\Schema c:\python27\python imdi_3_0_fix.py pause \ No newline at end of file diff --git a/SIL.Archiving/JSONUtils.cs b/SIL.Archiving/JSONUtils.cs old mode 100755 new mode 100644 diff --git a/SIL.Archiving/Properties/AssemblyInfo.cs b/SIL.Archiving/Properties/AssemblyInfo.cs old mode 100755 new mode 100644 index 7934654cb..25ad05caa --- a/SIL.Archiving/Properties/AssemblyInfo.cs +++ b/SIL.Archiving/Properties/AssemblyInfo.cs @@ -1,15 +1,9 @@ +// Copyright (c) 2016-2024 SIL International +// This software is licensed under the MIT License (http://opensource.org/licenses/MIT) + +using System; using System.Runtime.CompilerServices; -using SIL.Acknowledgements; -[assembly: InternalsVisibleTo("SIL.Archiving.Tests, PublicKey=0024000004800000940000000" + - "6020000002400005253413100040000010001008339b2ae1bf006934f6176dec6ea2a8a7d67383613dcb03d7197" + - "5e7b05ad546562c84529a4811e94c889e55f2532d1a90baaf20be9bff39ac6f5365bd605d70b90489840b7ba6d1" + - "c231b0e550c4abe4f60553856ef142a40a91e53d56e79f69dc79c4e95817de498aac924ee011f03b4e1c1d772d5" + - "1c4946c1185e3bfb621bc6")] +[assembly: CLSCompliant(true)] -[assembly: Acknowledgement("DotNetZip", Copyright = "Henrik Feldt/Dino Chiesa", Url = "https://github.com/haf/DotNetZip.Semverd", - LicenseUrl = "https://raw.githubusercontent.com/haf/DotNetZip.Semverd/master/LICENSE", Location = "./DotNetZip.dll", - Html = "
  • DotNetZip © Henrik Feldt/Dino Chiesa 2006-2018 (Multiple) - a library for handling zip archives
  • ")] -[assembly: Acknowledgement("L10NSharp", Url = "https://github.com/sillsdev/l10nsharp/", - Copyright = "Copyright © SIL International 2010-2017", LicenseUrl = "https://opensource.org/licenses/MIT", - Location = "./L10NSharp.dll")] \ No newline at end of file +[assembly: InternalsVisibleTo("SIL.Archiving.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001008339b2ae1bf006934f6176dec6ea2a8a7d67383613dcb03d71975e7b05ad546562c84529a4811e94c889e55f2532d1a90baaf20be9bff39ac6f5365bd605d70b90489840b7ba6d1c231b0e550c4abe4f60553856ef142a40a91e53d56e79f69dc79c4e95817de498aac924ee011f03b4e1c1d772d51c4946c1185e3bfb621bc6")] diff --git a/SIL.Archiving/Properties/Resources.Designer.cs b/SIL.Archiving/Properties/Resources.Designer.cs deleted file mode 100755 index 6c791efeb..000000000 --- a/SIL.Archiving/Properties/Resources.Designer.cs +++ /dev/null @@ -1,297 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace SIL.Archiving.Properties { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SIL.Archiving.Properties.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to [ - ///{"protocol":"AILCA","documentation":"ailca.html","choices":[ - /// {"label":"F","description":"access is Free to all"}, - /// {"label":"U","description":"all Users can access (requires registration)"}, - /// {"label":"RC","description":"Researchers and Community members are allowed access"}, - /// {"label":"C","description":"only Community members are allowed access (normally requires application to Depositor)"}, - /// {"label":"S","description":"only Subscribers are allowed access (requires application to Depositor)"}, - /// { [rest of string was truncated]";. - /// - internal static string AccessProtocols { - get { - return ResourceManager.GetString("AccessProtocols", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to <html> - /// <head></head> - /// <style> - /// body { - /// margin: 0; - /// } - /// body, table { - /// font-size: 12px; - /// font-family: 'Segoe UI', 'Tahoma', 'Arial', 'Helvetica', sans-serif; - /// } - /// td { - /// vertical-align: top; - /// } - /// .bold { - /// font-weight: 700; - /// } - /// .space-before { - /// padding-top: 10px; - /// } - /// .fixed { - /// font-family: 'Consolas', 'DejaVu Sans Mono', monospace; - /// } - /// </style> - /// <body style="background-color: rgb(230, 150, 100)"> - /// <table> - /// <tr> - /// <td colspan="3" class="bold">Name:</td> - /// [rest of string was truncated]";. - /// - internal static string ailca { - get { - return ResourceManager.GetString("ailca", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to <html> - /// <head></head> - /// <style> - /// body { - /// margin: 0; - /// } - /// body, table { - /// font-size: 12px; - /// font-family: 'Segoe UI', 'Tahoma', 'Arial', 'Helvetica', sans-serif; - /// } - /// td { - /// vertical-align: top; - /// } - /// .bold { - /// font-weight: 700; - /// } - /// .space-before { - /// padding-top: 10px; - /// } - /// .fixed { - /// font-family: 'Consolas', 'DejaVu Sans Mono', monospace; - /// } - /// .nowrap { white-space: nowrap; } - /// </style> - /// <body style="background-color: rgb(230, 150, 100)"> - /// <table> - /// <tr> - /// <td col [rest of string was truncated]";. - /// - internal static string ailla { - get { - return ResourceManager.GetString("ailla", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to <html> - /// <head></head> - /// <style> - /// body { - /// margin: 0; - /// } - /// body, table { - /// font-size: 12px; - /// font-family: 'Segoe UI', 'Tahoma', 'Arial', 'Helvetica', sans-serif; - /// } - /// td { - /// vertical-align: top; - /// } - /// .bold { - /// font-weight: 700; - /// } - /// .space-before { - /// padding-top: 10px; - /// } - /// .fixed { - /// font-family: 'Consolas', 'DejaVu Sans Mono', monospace; - /// } - /// .nowrap { white-space: nowrap; } - /// </style> - /// <body style="background-color: rgb(230, 150, 100)"> - /// <table> - /// <tr> - /// <td col [rest of string was truncated]";. - /// - internal static string anla { - get { - return ResourceManager.GetString("anla", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to [ - ///{"protocol":"Custom","documentation":"","choices":[ - /// {"label":"","description":""}]}, - ///]. - /// - internal static string CustomAccessProtocols { - get { - return ResourceManager.GetString("CustomAccessProtocols", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to <html> - /// <head></head> - /// <style> - /// body { - /// margin: 0; - /// } - /// body, table { - /// font-size: 12px; - /// font-family: 'Segoe UI', 'Tahoma', 'Arial', 'Helvetica', sans-serif; - /// } - /// td { - /// vertical-align: top; - /// } - /// .bold { - /// font-weight: 700; - /// } - /// .space-before { - /// padding-top: 10px; - /// } - /// .fixed { - /// font-family: 'Consolas', 'DejaVu Sans Mono', monospace; - /// } - /// </style> - /// <body style="background-color: rgb(230, 150, 100)"> - /// <table> - /// <tr> - /// <td colspan="3" class="bold">Name:</td> - /// [rest of string was truncated]";. - /// - internal static string elar { - get { - return ResourceManager.GetString("elar", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to <?xml version="1.0" encoding="UTF-8" ?><mets ID="sort-mets_mets" OBJID="sword-mets" LABEL="DSpace SWORD Item" PROFILE="DSpace SIL SIP Profile 1.0" xmlns="http://www.loc.gov/METS/" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.loc.gov/METS/ http://www.loc.gov/standards/mets/mets.xsd"> - /// <metsHdr/> - /// <dmdSec ID="ramp-mets-dmd-2" GROUPID="ramp-mets-dmd-2_group-1"> - /// <mdWrap LABEL="RAMP Metadata" MDTYPE="OTHER" OTHERMDTYPE="SIL. [rest of string was truncated]";. - /// - internal static string EmptyMets { - get { - return ResourceManager.GetString("EmptyMets", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to <html> - /// <head></head> - /// <body> - /// - /// </body> - ///</html>. - /// - internal static string reap { - get { - return ResourceManager.GetString("reap", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to <html> - /// <head></head> - /// <style> - /// body { - /// margin: 0; - /// } - /// body, table { - /// font-size: 12px; - /// font-family: 'Segoe UI', 'Tahoma', 'Arial', 'Helvetica', sans-serif; - /// } - /// td { - /// vertical-align: top; - /// } - /// .bold { - /// font-weight: 700; - /// } - /// .space-before { - /// padding-top: 10px; - /// } - /// .fixed { - /// font-family: 'Consolas', 'DejaVu Sans Mono', monospace; - /// } - /// .nowrap { white-space: nowrap; } - /// </style> - /// <body style="background-color: rgb(230, 150, 100)"> - /// <table> - /// <tr> - /// <td col [rest of string was truncated]";. - /// - internal static string tla { - get { - return ResourceManager.GetString("tla", resourceCulture); - } - } - } -} diff --git a/SIL.Archiving/Properties/Settings.Designer.cs b/SIL.Archiving/Properties/Settings.Designer.cs old mode 100755 new mode 100644 index be721f6f8..1505ac2ae --- a/SIL.Archiving/Properties/Settings.Designer.cs +++ b/SIL.Archiving/Properties/Settings.Designer.cs @@ -12,7 +12,7 @@ namespace SIL.Archiving.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.9.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); @@ -23,6 +23,15 @@ public static Settings Default { } } + [global::System.Configuration.ApplicationScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://www.mpi.nl/imdi/")] + public string IMDIWebSite { + get { + return ((string)(this["IMDIWebSite"])); + } + } + [global::System.Configuration.ApplicationScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("https://gateway.sil.org/display/RH/RAMP+Users%27+Manual")] @@ -41,14 +50,5 @@ public string RampContributorRoles { return ((string)(this["RampContributorRoles"])); } } - - [global::System.Configuration.ApplicationScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("http://www.mpi.nl/imdi/")] - public string IMDIWebSite { - get { - return ((string)(this["IMDIWebSite"])); - } - } } } diff --git a/SIL.Archiving/Properties/Settings.settings b/SIL.Archiving/Properties/Settings.settings old mode 100755 new mode 100644 index aedc73300..4748efedb --- a/SIL.Archiving/Properties/Settings.settings +++ b/SIL.Archiving/Properties/Settings.settings @@ -2,14 +2,14 @@ - - https://gateway.sil.org/display/RH/RAMP+Users%27+Manual - - - author;compiler;consultant;developer;editor;facilitator;illustrator;interviewer;photographer;recorder;researcher;signer;speaker;transcriber;translator - - - http://www.mpi.nl/imdi/ - + + http://www.mpi.nl/imdi/ + + + https://gateway.sil.org/display/RH/RAMP+Users%27+Manual + + + author;compiler;consultant;developer;editor;facilitator;illustrator;interviewer;photographer;recorder;researcher;signer;speaker;transcriber;translator + \ No newline at end of file diff --git a/SIL.Archiving/RampArchivingDlgViewModel.cs b/SIL.Archiving/RampArchivingDlgViewModel.cs index 861d1150f..cd5780a83 100644 --- a/SIL.Archiving/RampArchivingDlgViewModel.cs +++ b/SIL.Archiving/RampArchivingDlgViewModel.cs @@ -1,48 +1,27 @@ using System; using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; -using System.Runtime.InteropServices; using System.Security; using System.Text; using System.Threading; -using System.Windows.Forms; +using System.Threading.Tasks; using Ionic.Zip; -using L10NSharp; -using SIL.Archiving.Generic; -using SIL.Archiving.Properties; +using JetBrains.Annotations; +using SIL.Core.ClearShare; using SIL.Extensions; using SIL.IO; using SIL.PlatformUtilities; -using SIL.Windows.Forms.ClearShare; using Timer = System.Threading.Timer; +using static System.String; +using static SIL.Archiving.Resources.Resources; namespace SIL.Archiving { /// ------------------------------------------------------------------------------------ public class RampArchivingDlgViewModel: ArchivingDlgViewModel { - [DllImport("user32.dll", EntryPoint = "SetWindowPos")] - [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool SetWindowPosWindows(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags); - - private static bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, - int cy, uint uFlags) - { - // on Linux simply return true - return !Platform.IsWindows || SetWindowPosWindows(hWnd, hWndInsertAfter, x, y, cx, cy, uFlags); - } - // ReSharper disable InconsistentNaming - private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1); // brings window to top and makes it "always on top" - private static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2); // brings window to top but not "always on top" - private const UInt32 SWP_NOSIZE = 0x0001; - private const UInt32 SWP_NOMOVE = 0x0002; - private const UInt32 TOPMOST_FLAGS = SWP_NOMOVE | SWP_NOSIZE; - // ReSharper restore InconsistentNaming - #region RAMP and METS constants // ReSharper disable CSharpWarnings::CS1591 @@ -69,6 +48,7 @@ private static bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int public const string kContributor = "dc.contributor"; public const string kAbstractDescription = "dc.description.abstract"; public const string kStageDescription = "dc.description.stage"; + [PublicAPI] public const string kTableOfContentsDescription = "dc.description.tableofcontents"; public const string kVernacularContent = "dc.subject.vernacularContent"; @@ -77,6 +57,7 @@ private static bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int public const string kFlagHasSoftwareOrFontRequirements = "relation.requires.has"; public const string kFlagHasGeneralDescription = "description.has"; public const string kFlagHasAbstractDescription = "description.abstract.has"; + [PublicAPI] public const string kFlagHasTableOfContentsDescription = "description.tableofcontents.has"; public const string kFlagHasPromotionDescription = "description.promotion.has"; public const string kTrue = "Y"; @@ -152,20 +133,23 @@ private static bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int public const string kFileDescription = "description"; public const string kFileRelationship = "relationship"; public const string kRelationshipSource = "Source"; + [PublicAPI] public const string kRelationshipPresentation = "Presentation"; + [PublicAPI] public const string kRelationshipSupporting = "Supporting"; // ReSharper restore CSharpWarnings::CS1591 #endregion #region Data members + private readonly Dictionary _progressMessages = new Dictionary(); private readonly List _metsPairs; private AudienceType _metsAudienceType; private string _metsFilePath; private string _tempFolder; private Timer _timer; - private bool _workerException; private Dictionary _languageList; - private readonly Func _getFileDescription; // first param is filelist key, second param is filename + private readonly Func _getFileDescription; // first param is file list key, second param is filename + private int _imageCount = -1; private int _audioCount = -1; private int _videoCount = -1; @@ -174,32 +158,11 @@ private static bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int #region properties /// ------------------------------------------------------------------------------------ - internal override string ArchiveType => LocalizationManager.GetString( - "DialogBoxes.ArchivingDlg.RAMPArchiveType", "RAMP (SIL Only)"); + public override Standard ArchiveType => Standard.REAP; /// ------------------------------------------------------------------------------------ public override string NameOfProgramToLaunch => kRampProcessName; - /// ------------------------------------------------------------------------------------ - public override string InformativeText - { - get - { - return string.Format(LocalizationManager.GetString("DialogBoxes.ArchivingDlg.RAMPOverviewText", - "{0} is a utility for entering metadata and uploading submissions to SIL's internal archive, " + - "REAP. If you have access to this archive, this tool will help you use {0} to archive your " + - "{1} data. {2} When the {0} package has been created, you can launch {0} and enter any " + - "additional information before doing the actual submission.", - "Parameter 0 is the word 'RAMP' (the first one will be turned into a hyperlink); " + - "Parameter 1 is the name of the calling (host) program (SayMore, FLEx, etc.); " + - "Parameter 2 is additional app-specific information."), NameOfProgramToLaunch, AppName, - _appSpecificArchivalProcessInfo); - } - } - - /// ------------------------------------------------------------------------------------ - public override string ArchiveInfoHyperlinkText => NameOfProgramToLaunch; - /// ------------------------------------------------------------------------------------ public override string ArchiveInfoUrl => Properties.Settings.Default.RampWebSite; @@ -221,35 +184,50 @@ public override string InformativeText /// /// Gets the number of image files in the list(s) of files to archive. /// - /// Public (and self-populating on-demand) to facilitate testing + /// Public (and self-populating on-demand) to facilitate testing. If this + /// property is accessed before initialization is complete, it will return -1. /// ------------------------------------------------------------------------------------ public int ImageCount { get { - if (_fileLists != null && _imageCount < 0) + if (FileLists.Count > 0 && _imageCount < 0) ExtractInformationFromFiles(); return _imageCount; } } /// ------------------------------------------------------------------------------------ + /// + /// Gets the number of audio files in the list(s) of files to archive. + /// + /// Public (and self-populating on-demand) to facilitate testing. If this + /// property is accessed before initialization is complete, it will return -1. + /// ------------------------------------------------------------------------------------ + [PublicAPI] public int AudioCount { get { - if (_fileLists != null && _audioCount < 0) + if (FileLists.Count > 0 && _audioCount < 0) ExtractInformationFromFiles(); return _audioCount; } } /// ------------------------------------------------------------------------------------ + /// + /// Gets the number of video files in the list(s) of files to archive. + /// + /// Public (and self-populating on-demand) to facilitate testing. If this + /// property is accessed before initialization is complete, it will return -1. + /// ------------------------------------------------------------------------------------ + [PublicAPI] public int VideoCount { get { - if (_fileLists != null && _videoCount < 0) + if (FileLists.Count > 0 && _videoCount < 0) ExtractInformationFromFiles(); return _videoCount; } @@ -263,7 +241,7 @@ public int VideoCount /// ------------------------------------------------------------------------------------ private void ExtractInformationFromFiles() { - ExtractInformationFromFiles(_fileLists.SelectMany(f => f.Value.Item1)); + ExtractInformationFromFiles(FileLists.SelectMany(f => f.Value.Item1)); } /// ------------------------------------------------------------------------------------ @@ -329,27 +307,23 @@ private void AddModesToSet(IEnumerable files) /// The application name /// Title of the submission /// Identifier (used as filename) for the package being created - /// Application can use this to pass - /// additional information that will be displayed to the user in the dialog to explain - /// any application-specific details about the archival process. /// Delegate to request client to call methods to set - /// which files should be archived (this is deferred to allow display of progress message) + /// which files should be archived (this is deferred to allow display of progress message). + /// Clients will normally do this by calling AddFileGroup one or more times. /// Callback function to get a file description based /// on the file-list key (param 1) and the filename (param 2) /// ------------------------------------------------------------------------------------ public RampArchivingDlgViewModel(string appName, string title, string id, - string appSpecificArchivalProcessInfo, Action setFilesToArchive, + Action setFilesToArchive, Func getFileDescription) : base(appName, title, id, - appSpecificArchivalProcessInfo, setFilesToArchive) + setFilesToArchive) { - if (getFileDescription == null) - throw new ArgumentNullException("getFileDescription"); - _getFileDescription = getFileDescription; + _getFileDescription = getFileDescription ?? throw new ArgumentNullException(nameof(getFileDescription)); ShowRecordingCountNotLength = false; ImagesArePhotographs = true; - _metsPairs = new List(new [] {JSONUtils.MakeKeyValuePair(kPackageTitle, _titles[_id])}); + _metsPairs = new List(new [] {JSONUtils.MakeKeyValuePair(kPackageTitle, PackageTitle)}); foreach (var orphanedRampPackage in Directory.GetFiles(Path.GetTempPath(), "*" + kRampFileExtension)) { @@ -359,19 +333,33 @@ public RampArchivingDlgViewModel(string appName, string title, string id, } } + protected override async Task SetFilesToArchive(CancellationToken cancellationToken) + { + await base.SetFilesToArchive(cancellationToken); + + if (cancellationToken.IsCancellationRequested) + throw new OperationCanceledException(); + + foreach (var fileList in FileLists.Where(fileList => fileList.Value.Item1.Any())) + { + var normalizedName = NormalizeFilename(fileList.Key, + Path.GetFileName(fileList.Value.Item1.First())); + _progressMessages[normalizedName] = fileList.Value.Item2; + } + } + /// ------------------------------------------------------------------------------------ protected override bool DoArchiveSpecificInitialization() { - DisplayMessage(LocalizationManager.GetString("DialogBoxes.ArchivingDlg.SearchingForRampMsg", - "Searching for the RAMP program..."), MessageType.Volatile); + DisplayMessage(Progress.GetMessage(StringId.SearchingForArchiveUploadingProgram), + MessageType.Volatile); - Application.DoEvents(); PathToProgramToLaunch = GetExeFileLocation(); if (PathToProgramToLaunch == null) { - DisplayMessage(LocalizationManager.GetString("DialogBoxes.ArchivingDlg.RampNotFoundMsg", - "The RAMP program cannot be found!"), MessageType.Error); + DisplayMessage(Progress.GetMessage(StringId.ArchiveUploadingProgramNotFound), + MessageType.Error); return false; } @@ -383,24 +371,29 @@ public override int CalculateMaxProgressBarValue() { // One for analyzing each list, one for copying each file, one for adding each file // to the zip file and one for the mets.xml file. - return _fileLists.Count + 2 * _fileLists.SelectMany(kvp => kvp.Value.Item1).Count() + 1; + return FileLists.Count + 2 * FileLists.SelectMany(kvp => kvp.Value.Item1).Count() + 1; } #endregion #region Methods to add app-specific METS pairs + /// ------------------------------------------------------------------------------------ /// /// Sets the "Broad Type", the audience for which the resource being archived is /// primarily intended. This is set automatically as a side-effect of setting the /// stage or type. /// + /// Audience has already been set and + /// cannot be changed to a different value. + /// The audience type is not one that is + /// currently handled. /// ------------------------------------------------------------------------------------ public void SetAudience(AudienceType audienceType) { if (IsMetadataPropertySet(MetadataProperties.Audience)) { if (_metsAudienceType != audienceType) - throw new InvalidOperationException(string.Format("Audience has already been set and cannot be changed to a different value.")); + throw new InvalidOperationException("Audience has already been set and cannot be changed to a different value."); return; // Already added } @@ -577,7 +570,10 @@ public void SetVernacularMaterialsAndContentType(VernacularMaterialsType vernacu /// (for published textbooks call SetScholarlyWorkType instead as these are generally for /// a wider audience). ///
    + /// /// ------------------------------------------------------------------------------------ + [PublicAPI] public void SetTrainingResourceType(TrainingResourceType trainingResourceType) { SetAudience(AudienceType.Training); @@ -605,6 +601,7 @@ public void SetTrainingResourceType(TrainingResourceType trainingResourceType) /// a wider audience). ///
    /// ------------------------------------------------------------------------------------ + [PublicAPI] public void SetInternalWorkType(InternalWorkType internalWorkType) { SetAudience(AudienceType.Internal); @@ -629,7 +626,10 @@ public void SetInternalWorkType(InternalWorkType internalWorkType) /// /// Sets the bibliographic type of the resource being archived (for a "wider audience") /// + /// The scholarlyWorkType value is one that is + /// not currently handled. /// ------------------------------------------------------------------------------------ + [PublicAPI] public void SetScholarlyWorkType(ScholarlyWorkType scholarlyWorkType) { SetAudience(AudienceType.Wider); @@ -659,6 +659,7 @@ public void SetScholarlyWorkType(ScholarlyWorkType scholarlyWorkType) /// pay attention to the comments for each work stage to avoid pairing it with an /// invalid audience type. /// ------------------------------------------------------------------------------------ + [PublicAPI] public void SetStage(WorkStage stage) { PreventDuplicateMetadataProperty(MetadataProperties.Stage); @@ -710,7 +711,7 @@ private void PreventInvalidAudienceTypeForWorkStage(Enum invalidAudience, WorkSt { if ((invalidAudience != null) && (invalidAudience.HasFlag(_metsAudienceType))) { - throw new InvalidOperationException(string.Format( + throw new InvalidOperationException(Format( "Resources with an audience of \"{0}\" cannot have a work stage of {1}", _metsAudienceType, stage)); } @@ -731,6 +732,7 @@ private void SetStage(string stage) /// necessary to OR the sub-domains with their corresponding domains, since the /// subdomains already have the correct domain bits set. /// ------------------------------------------------------------------------------------ + [PublicAPI] public void SetDomains(SilDomain domains) { PreventDuplicateMetadataProperty(MetadataProperties.Domains); @@ -759,7 +761,7 @@ public void SetDomains(SilDomain domains) if (domains.HasFlag(SilDomain.Emus_ArtisticCommunicationProfile)) AddSubDomain(kEthnomusicologyAbbrev, "artistic communication profile"); if (domains.HasFlag(SilDomain.Emus_PerformanceCollection)) - AddSubDomain(kEthnomusicologyAbbrev, "aperformance collection"); + AddSubDomain(kEthnomusicologyAbbrev, "performance collection"); if (domains.HasFlag(SilDomain.Emus_SongCollection)) AddSubDomain(kEthnomusicologyAbbrev, "song collection"); if (domains.HasFlag(SilDomain.Emus_SummaryArtisticEventFormAnalysis)) @@ -895,15 +897,16 @@ private void AddDomain(string domainAbbrev, string domainName) { // if a mets pair already exists for domains, add this domain to the existing list. var existingValue = _metsPairs.Find(s => s.Contains(kSilDomain)); - if (!string.IsNullOrEmpty(existingValue)) + if (!IsNullOrEmpty(existingValue)) { int pos = existingValue.IndexOf(']'); - string newValue = existingValue.Insert(pos, string.Format(",\"{0}:{1}\"", domainAbbrev, domainName)); + string newValue = existingValue.Insert(pos, $",\"{domainAbbrev}:{domainName}\""); _metsPairs[_metsPairs.IndexOf(existingValue)] = newValue; } else { - _metsPairs.Add(JSONUtils.MakeKeyValuePair(kSilDomain, string.Format("{0}:{1}", domainAbbrev, domainName), true)); + _metsPairs.Add(JSONUtils.MakeKeyValuePair(kSilDomain, + $"{domainAbbrev}:{domainName}", true)); } } @@ -916,19 +919,19 @@ private void AddDomain(string domainAbbrev, string domainName) /// ------------------------------------------------------------------------------------ private void AddSubDomain(string domainAbbrev, string subDomain) { - // if a mets pair already exists for this domain, add this subdomain to the existing list. - var key = string.Format(kFmtDomainSubtype, domainAbbrev); + // if a mets pair already exists for this domain, add this sub-domain to the existing list. + var key = Format(kFmtDomainSubtype, domainAbbrev); var existingValue = _metsPairs.Find(s => s.Contains(key)); - if (!string.IsNullOrEmpty(existingValue)) + if (!IsNullOrEmpty(existingValue)) { int pos = existingValue.IndexOf(']'); - string newValue = existingValue.Insert(pos, string.Format(",\"{0} ({1})\"", subDomain, domainAbbrev)); + string newValue = existingValue.Insert(pos, $",\"{subDomain} ({domainAbbrev})\""); _metsPairs[_metsPairs.IndexOf(existingValue)] = newValue; } else { _metsPairs.Add(JSONUtils.MakeKeyValuePair(key, - string.Format("{0} ({1})", subDomain, domainAbbrev), true)); + $"{subDomain} ({domainAbbrev})", true)); } } @@ -937,6 +940,7 @@ private void AddSubDomain(string domainAbbrev, string subDomain) /// Adds a METS pair for the Date this resource was initially created ///
    /// ------------------------------------------------------------------------------------ + [PublicAPI] public void SetCreationDate(DateTime date) { SetCreationDate(date.ToISO8601TimeFormatDateOnlyString()); @@ -948,6 +952,7 @@ public void SetCreationDate(DateTime date) /// was collected or work was being done on this resource ///
    /// ------------------------------------------------------------------------------------ + [PublicAPI] public void SetCreationDate(int startYear, int endYear) { if (endYear < startYear) @@ -960,7 +965,7 @@ public void SetCreationDate(int startYear, int endYear) if (startYear < 0) endYr += (endYear < 0) ? " BCE" : " CE"; - SetCreationDate(string.Format("{0}-{1}", startYr, endYr)); + SetCreationDate($"{startYr}-{endYr}"); } /// ------------------------------------------------------------------------------------ @@ -969,10 +974,11 @@ public void SetCreationDate(int startYear, int endYear) /// was collected or work was being done on this resource ///
    /// ------------------------------------------------------------------------------------ + [PublicAPI] public void SetCreationDate(string date) { - if (string.IsNullOrEmpty(date)) - throw new ArgumentNullException("date"); + if (IsNullOrEmpty(date)) + throw new ArgumentNullException(nameof(date)); PreventDuplicateMetadataProperty(MetadataProperties.CreationDate); @@ -984,6 +990,7 @@ public void SetCreationDate(string date) /// Adds a METS pair for the Date this resource was last updated ///
    /// ------------------------------------------------------------------------------------ + [PublicAPI] public void SetModifiedDate(DateTime date) { PreventDuplicateMetadataProperty(MetadataProperties.ModifiedDate); @@ -999,6 +1006,7 @@ public void SetModifiedDate(DateTime date) /// The 3-letter ISO 639-2 code for the language /// The English name of the language /// ------------------------------------------------------------------------------------ + [PublicAPI] public void SetSubjectLanguage(string iso3Code, string languageName) { PreventDuplicateMetadataProperty(MetadataProperties.SubjectLanguage); @@ -1012,7 +1020,7 @@ public void SetSubjectLanguage(string iso3Code, string languageName) /// /// Sets the given METS flag (typically appears as a checkbox in RAMP) to true/yes ("Y") /// - /// One of the kFlag... contants + /// One of the kFlag... constants /// ------------------------------------------------------------------------------------ private void SetFlag(string flagKey) { @@ -1027,6 +1035,7 @@ private void SetFlag(string flagKey) /// Be as specific a possible, indicating version number(s) /// where appropriate /// ------------------------------------------------------------------------------------ + [PublicAPI] public void SetSoftwareRequirements(params string[] requirements) { SetSoftwareRequirements((IEnumerable)requirements); @@ -1040,12 +1049,12 @@ public void SetSoftwareRequirements(params string[] requirements) /// Be as specific a possible, indicating version number(s) /// where appropriate /// ------------------------------------------------------------------------------------ + [PublicAPI] public void SetSoftwareRequirements(IEnumerable requirements) { - requirements = requirements.Where(r => !string.IsNullOrEmpty(r)); + requirements = requirements.Where(r => !IsNullOrEmpty(r)).ToList(); - HashSet softwareKeyValuePairs = new HashSet(); -// ReSharper disable PossibleMultipleEnumeration + var softwareKeyValuePairs = new HashSet(); if (requirements.Any()) { PreventDuplicateMetadataProperty(MetadataProperties.SoftwareRequirements); @@ -1057,7 +1066,6 @@ public void SetSoftwareRequirements(IEnumerable requirements) _metsPairs.Add(JSONUtils.MakeArrayFromValues(kSoftwareOrFontRequirements, softwareKeyValuePairs)); } -// ReSharper restore PossibleMultipleEnumeration } /// ------------------------------------------------------------------------------------ @@ -1070,13 +1078,14 @@ public void SetSoftwareRequirements(IEnumerable requirements) /// /// The 3-letter ISO 639-2 codes for the languages /// ------------------------------------------------------------------------------------ + [PublicAPI] public void SetContentLanguages(params string[] iso3Codes) { var languages = new List(); foreach (var iso3Code in iso3Codes) { - if (!string.IsNullOrEmpty(iso3Code)) + if (!IsNullOrEmpty(iso3Code)) languages.Add(new ArchivingLanguage(iso3Code)); } @@ -1094,20 +1103,22 @@ public void SetContentLanguages(params string[] iso3Codes) /// The 3-letter ISO 639-2 codes for the languages, as well as /// the English name, dialect and writing system script /// ------------------------------------------------------------------------------------ + [PublicAPI] public void SetContentLanguages(IEnumerable languages) { var languageValues = new HashSet(); var scripts = new HashSet(); - foreach (var lang in languages.Where(r => !string.IsNullOrEmpty(r.Iso3Code))) + foreach (var lang in languages.Where(r => !IsNullOrEmpty(r.Iso3Code))) { - var langPair = JSONUtils.MakeKeyValuePair(kDefaultKey, string.Format("{0}:{1}", lang.Iso3Code, GetLanguageName(lang.Iso3Code))); - if (!string.IsNullOrEmpty(lang.Dialect)) + var langPair = JSONUtils.MakeKeyValuePair(kDefaultKey, + $"{lang.Iso3Code}:{GetLanguageName(lang.Iso3Code)}"); + if (!IsNullOrEmpty(lang.Dialect)) langPair += "," + JSONUtils.MakeKeyValuePair("dialect", lang.Dialect); languageValues.Add(langPair); - if (!string.IsNullOrEmpty(lang.Script)) + if (!IsNullOrEmpty(lang.Script)) scripts.Add(JSONUtils.MakeKeyValuePair(kDefaultKey, lang.Script)); } @@ -1120,18 +1131,17 @@ public void SetContentLanguages(IEnumerable languages) if (scripts.Any()) _metsPairs.Add(JSONUtils.MakeArrayFromValues(kContentLanguageScripts, scripts)); } - - } /// ------------------------------------------------------------------------------------ /// /// Sets the schema(s) to which this resource -- or the file(s) it contains -- conforms. /// - /// Known schema name (typically, but not nescessarily, + /// Known schema name (typically, but not necessarily, /// for XML files). RAMP doesn't say what to do if the resource contains files conforming to /// multiple standards, but I would say just make a comma-separated list. /// ------------------------------------------------------------------------------------ + [PublicAPI] public void SetSchemaConformance(string schemaDescriptor) { PreventDuplicateMetadataProperty(MetadataProperties.SchemaConformance); @@ -1145,10 +1155,11 @@ public void SetSchemaConformance(string schemaDescriptor) /// /// For example, "2505 entries" /// ------------------------------------------------------------------------------------ + [PublicAPI] public void SetDatasetExtent(string extent) { - if (string.IsNullOrEmpty(extent)) - throw new ArgumentNullException("extent"); + if (IsNullOrEmpty(extent)) + throw new ArgumentNullException(nameof(extent)); PreventDuplicateMetadataProperty(MetadataProperties.DatasetExtent); @@ -1160,6 +1171,7 @@ public void SetDatasetExtent(string extent) /// Sets the total duration of all audio and/or video recording in this resource. /// /// ------------------------------------------------------------------------------------ + [PublicAPI] public void SetAudioVideoExtent(TimeSpan totalDuration) { SetAudioVideoExtent(totalDuration.ToString()); @@ -1170,6 +1182,7 @@ public void SetAudioVideoExtent(TimeSpan totalDuration) /// Sets the total duration of all audio and/or video recording in this resource. /// /// ------------------------------------------------------------------------------------ + [PublicAPI] public void SetAudioVideoExtent(string totalDuration) { PreventDuplicateMetadataProperty(MetadataProperties.RecordingExtent); @@ -1182,10 +1195,11 @@ public void SetAudioVideoExtent(string totalDuration) /// Sets the collection of contributors (and their roles) to this resource. /// /// ------------------------------------------------------------------------------------ + [PublicAPI] public void SetContributors(ContributionCollection contributions) { if (contributions == null) - throw new ArgumentNullException("contributions"); + throw new ArgumentNullException(nameof(contributions)); if (contributions.Count == 0) return; @@ -1197,11 +1211,11 @@ public void SetContributors(ContributionCollection contributions) } /// ------------------------------------------------------------------------------------ - private string GetContributorsMetsPairs(Contribution contribution) + private static string GetContributorsMetsPairs(Contribution contribution) { - var roleCode = (contribution.Role != null && + var roleCode = contribution.Role != null && Properties.Settings.Default.RampContributorRoles.Contains(contribution.Role.Code) ? - contribution.Role.Code : string.Empty); + contribution.Role.Code : Empty; return JSONUtils.MakeKeyValuePair(kDefaultKey, contribution.ContributorName) + kSeparator + JSONUtils.MakeKeyValuePair(kRole, roleCode); @@ -1216,6 +1230,7 @@ private string GetContributorsMetsPairs(Contribution contribution) /// 20 major LWCs. Not sure what happens if an unrecognized code gets passed to this. /// Feel free to try it and find out. /// ------------------------------------------------------------------------------------ + [PublicAPI] public void SetDescription(string description, string language) { SetGeneralDescription(new[] { GetKvpsForLanguageSpecificString(language, description) }); @@ -1230,10 +1245,11 @@ public void SetDescription(string description, string language) /// what happens if an unrecognized code gets passed to this. Feel free to try it and /// find out. /// ------------------------------------------------------------------------------------ + [PublicAPI] public void SetDescription(IDictionary descriptions) { if (descriptions == null) - throw new ArgumentNullException("descriptions"); + throw new ArgumentNullException(nameof(descriptions)); if (descriptions.Count == 0) return; @@ -1274,7 +1290,7 @@ protected override void SetAbstract_Impl(IDictionary description IEnumerable metsDescriptions; - if (descriptions.Count == 1 && string.IsNullOrEmpty(descriptions.Keys.ElementAt(0))) + if (descriptions.Count == 1 && IsNullOrEmpty(descriptions.Keys.ElementAt(0))) metsDescriptions = new [] {JSONUtils.MakeKeyValuePair(kDefaultKey, descriptions.Values.ElementAt(0))}; else metsDescriptions = descriptions.Select(desc => GetKvpsForLanguageSpecificString(desc.Key, desc.Value)); @@ -1291,6 +1307,7 @@ protected override void SetAbstract_Impl(IDictionary description /// 20 major LWCs. Not sure what happens if an unrecognized code gets passed to this. /// Feel free to try it and find out. /// ------------------------------------------------------------------------------------ + [PublicAPI] public void SetPromotion(string text, string language) { SetPromotion(new[] { GetKvpsForLanguageSpecificString(language, text) }); @@ -1305,10 +1322,11 @@ public void SetPromotion(string text, string language) /// happens if an unrecognized code gets passed to this. Feel free to try it and find /// out. /// ------------------------------------------------------------------------------------ + [PublicAPI] public void SetPromotion(IDictionary descriptions) { if (descriptions == null) - throw new ArgumentNullException("descriptions"); + throw new ArgumentNullException(nameof(descriptions)); if (descriptions.Count == 0) return; @@ -1353,75 +1371,28 @@ private string GetKvpsForLanguageSpecificString(string lang, string s) #region RAMP calling methods /// ------------------------------------------------------------------------------------ - internal override void LaunchArchivingProgram() + public override void LaunchArchivingProgram() { if (!File.Exists(PackagePath)) { - ReportError(null, string.Format("RAMP package prematurely removed: {0}", PackagePath)); + ReportError(null, Progress.GetMessage(StringId.RampPackageRemoved)); return; } - LaunchArchivingProgram(EnsureRampHasFocusAndWaitForPackageToUnlock); - } - - /// ------------------------------------------------------------------------------------ - private void EnsureRampHasFocusAndWaitForPackageToUnlock() - { - if (IsMono) + try { - BringToFrontMono(); + base.LaunchArchivingProgram(); } - else + catch (InvalidOperationException) { - BringToFrontWindows(); + // Every 4 seconds we'll check to see if the RAMP package is locked. When + // it gets unlocked by RAMP, then we'll delete it. + _timer = new Timer(CheckIfPackageFileIsLocked, PackagePath, 2000, 4000); } - - // Every 4 seconds we'll check to see if the RAMP package is locked. When - // it gets unlocked by RAMP, then we'll delete it. - _timer = new Timer(CheckIfPackageFileIsLocked, PackagePath, 2000, 4000); - } - - private static void BringToFrontWindows() - { - var processes = Process.GetProcessesByName(kRampProcessName); - if (processes.Length < 1) return; - - // First, make the window topmost: this puts it in front of all other windows - // and sets it as "always on top." - SetWindowPos(processes[0].MainWindowHandle, HWND_TOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS); - - // Second, make the window notopmost: this removes the "always on top" behavior - // and positions the window on top of all other "not always on top" windows. - SetWindowPos(processes[0].MainWindowHandle, HWND_NOTOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS); - } - - private static void BringToFrontMono() - { - // On mono this requires xdotool or wmctrl - string args = null; - if (!string.IsNullOrEmpty(FileLocationUtilities.LocateInProgramFiles("xdotool", true))) /* try to find xdotool first */ - args = "-c \"for pid in `xdotool search --name RAMP`; do xdotool windowactivate $pid; done\""; - else if (!string.IsNullOrEmpty(FileLocationUtilities.LocateInProgramFiles("wmctrl", true))) /* if xdotool is not installed, look for wmctrl */ - args = "-c \"wmctrl -a RAMP\""; - - if (string.IsNullOrEmpty(args)) return; - - var prs = new Process - { - StartInfo = - { - FileName = "bash", - Arguments = args, - UseShellExecute = false, - RedirectStandardError = true - } - }; - - prs.Start(); } /// ------------------------------------------------------------------------------------ - private void CheckIfPackageFileIsLocked(Object packageFile) + private void CheckIfPackageFileIsLocked(object packageFile) { if (!FileHelper.IsLocked(packageFile as string)) CleanUpTempRampPackage(); @@ -1432,25 +1403,22 @@ private void CheckIfPackageFileIsLocked(Object packageFile) /// ------------------------------------------------------------------------------------ /// Public to facilitate testing /// ------------------------------------------------------------------------------------ - public override bool CreatePackage() + public override async Task CreatePackage(CancellationToken cancellationToken) { IsBusy = true; var success = CreateMetsFile() != null; if (success) - success = CreateRampPackage(); + success = await CreateRampPackage(cancellationToken); - CleanUp(); + DeleteTempFolder(); if (success) - { - DisplayMessage(LocalizationManager.GetString("DialogBoxes.ArchivingDlg.ReadyToCallRampMsg", - "Ready to hand the package to RAMP"), MessageType.Success); - } + DisplayMessage(StringId.ReadyToCallRampMsg, MessageType.Success); IsBusy = false; - return success; + return success? PackagePath : null; } /// ------------------------------------------------------------------------------------ @@ -1475,33 +1443,34 @@ public override string GetMetadata() foreach (var value in _metsPairs) bldr.AppendFormat("{0},", value); - return string.Format("{{{0}}}", bldr.ToString().TrimEnd(',')); + return $"{{{bldr.ToString().TrimEnd(',')}}}"; } /// ------------------------------------------------------------------------------------ - public string CreateMetsFile() + internal string CreateMetsFile() { try { - var metsData = Resources.EmptyMets.Replace("", "" + JSONUtils.EncodeData(GetMetadata())); - _tempFolder = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); - Directory.CreateDirectory(_tempFolder); - _metsFilePath = Path.Combine(_tempFolder, "mets.xml"); - File.WriteAllText(_metsFilePath, metsData); + var metsData = GetResource(Name.EmptyMets_xml).Replace("", + "" + JSONUtils.EncodeData(GetMetadata())); + _tempFolder = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + Directory.CreateDirectory(_tempFolder); + _metsFilePath = Path.Combine(_tempFolder, "mets.xml"); + File.WriteAllText(_metsFilePath, metsData); } catch (Exception e) { - if ((e is IOException) || (e is UnauthorizedAccessException) || (e is SecurityException)) + if (e is IOException || e is UnauthorizedAccessException || + e is SecurityException) { - ReportError(e, LocalizationManager.GetString("DialogBoxes.ArchivingDlg.CreatingInternalReapMetsFileErrorMsg", - "There was an error attempting to create the RAMP/REAP mets file.")); + ReportError(e, Progress.GetMessage(StringId.ErrorCreatingMetsFile)); return null; } + throw; } - if (IncrementProgressBarAction != null) - IncrementProgressBarAction(); + Progress.IncrementProgress(); return _metsFilePath; } @@ -1509,7 +1478,7 @@ public string CreateMetsFile() /// ------------------------------------------------------------------------------------ private void SetMetsPairsForFiles() { - if (_fileLists.Any()) + if (FileLists.Any()) { string value = GetMode(); if (value != null) @@ -1519,14 +1488,14 @@ private void SetMetsPairsForFiles() { // Return JSON array of files with their descriptions. _metsPairs.Add(JSONUtils.MakeArrayFromValues(kSourceFilesForMets, - GetSourceFilesForMetsData(_fileLists))); + GetSourceFilesForMetsData(FileLists))); MarkMetadataPropertyAsSet(MetadataProperties.Files); } if (ImageCount > 0) - _metsPairs.Add(JSONUtils.MakeKeyValuePair(kImageExtent, string.Format("{0} image{1}.", - _imageCount.ToString(CultureInfo.InvariantCulture), - (_imageCount == 1) ? "" : "s"))); + _metsPairs.Add(JSONUtils.MakeKeyValuePair(kImageExtent, + $"{_imageCount.ToString(CultureInfo.InvariantCulture)} " + + $"image{(_imageCount == 1 ? "" : "s")}.")); var avExtent = new StringBuilder(); const string delimiter = "; "; @@ -1534,10 +1503,16 @@ private void SetMetsPairsForFiles() if (ShowRecordingCountNotLength) { if (_audioCount > 0) - avExtent.AppendLineFormat("{0} audio recording file{1}", new object[] { _audioCount, (_audioCount == 1) ? "" : "s" }, delimiter); + { + avExtent.AppendLineFormat("{0} audio recording file{1}", + new object[] { _audioCount, _audioCount == 1 ? "" : "s" }, delimiter); + } if (_videoCount > 0) - avExtent.AppendLineFormat("{0} video recording file{1}", new object[] { _videoCount, (_videoCount == 1) ? "" : "s" }, delimiter); + { + avExtent.AppendLineFormat("{0} video recording file{1}", + new object[] { _videoCount, _videoCount == 1 ? "" : "s" }, delimiter); + } SetAudioVideoExtent(avExtent + "."); } @@ -1556,8 +1531,12 @@ private string GetMode() ExtractInformationFromFiles(); if ((_modes == null) || - (IsMetadataPropertySet(MetadataProperties.DatasetExtent) && !_modes.Contains(kModeDataset))) - throw new InvalidOperationException("Cannot set dataset extent for a resource which does not contain any \"dataset\" files."); + (IsMetadataPropertySet(MetadataProperties.DatasetExtent) && + !_modes.Contains(kModeDataset))) + { + throw new InvalidOperationException( + "Cannot set dataset extent for a resource which does not contain any \"dataset\" files."); + } return JSONUtils.MakeBracketedListFromValues(kFileTypeModeList, _modes); } @@ -1599,7 +1578,7 @@ public IEnumerable GetSourceFilesForMetsData(IDictionary CreateRampPackage(CancellationToken cancellationToken) { + bool result = false; try { - PackagePath = Path.Combine(Path.GetTempPath(), _id + kRampFileExtension); + PackagePath = Path.Combine(Path.GetTempPath(), PackageId + kRampFileExtension); - using (_worker = new BackgroundWorker()) - { - _cancelProcess = false; - _workerException = false; - _worker.ProgressChanged += HandleBackgroundWorkerProgressChanged; - _worker.WorkerReportsProgress = true; - _worker.WorkerSupportsCancellation = true; - _worker.DoWork += CreateZipFileInWorkerThread; - _worker.RunWorkerAsync(); - - while (_worker.IsBusy) - Application.DoEvents(); - } - } - catch (Exception e) - { - ReportError(e, LocalizationManager.GetString( - "DialogBoxes.ArchivingDlg.CreatingZipFileErrorMsg", - "There was a problem starting process to create zip file.")); + await Task.Run(() => CreateZipFile(cancellationToken), cancellationToken); - return false; + if (!File.Exists(PackagePath)) + ReportError(null, Progress.GetMessage(StringId.FailedToMakePackage)); + else + result = true; } - finally + catch (OperationCanceledException) { - _worker = null; } - - if (!File.Exists(PackagePath)) + catch (Exception exception) { - ReportError(null, string.Format("Failed to make the RAMP package: {0}", PackagePath)); - return false; + ReportError(exception, Progress.GetMessage(StringId.ErrorCreatingArchive)); } - return !_cancelProcess && !_workerException; + return result; } /// ------------------------------------------------------------------------------------ - private void CreateZipFileInWorkerThread(object sender, DoWorkEventArgs e) + private void CreateZipFile(CancellationToken cancellationToken) { - try - { - if (Thread.CurrentThread.Name == null) - Thread.CurrentThread.Name = "CreateZipFileInWorkerThread"; - - // Before adding the files to the RAMP (zip) file, we need to copy all the - // files to a temp folder, flattening out the directory structure and renaming - // the files as needed to comply with REAP guidelines. - // REVIEW: Are multiple periods and/or non-Roman script really a problem? + // Before adding the files to the RAMP (zip) file, we need to copy all the + // files to a temp folder, flattening out the directory structure and renaming + // the files as needed to comply with REAP guidelines. + // REVIEW: Are multiple periods and/or non-Roman script really a problem? - _worker.ReportProgress(0, PreparingFilesMsg); + ReportProgress(Progress.GetMessage(StringId.PreparingFiles), MessageType.Success, + cancellationToken); - var filesToCopyAndZip = new Dictionary(); - foreach (var list in _fileLists) + var filesToCopyAndZip = new Dictionary(); + foreach (var list in FileLists) + { + ReportProgress(IsNullOrEmpty(list.Key) ? PackageId : list.Key, MessageType.Detail, + cancellationToken); + foreach (var file in list.Value.Item1) { - _worker.ReportProgress(1 /* actual value ignored, progress just increments */, - string.IsNullOrEmpty(list.Key) ? _id: list.Key); - foreach (var file in list.Value.Item1) - { - string newFileName = Path.GetFileName(file); - newFileName = NormalizeFilename(list.Key, newFileName); - filesToCopyAndZip[file] = Path.Combine(_tempFolder, newFileName); - } - if (_cancelProcess) - return; + string newFileName = Path.GetFileName(file); + newFileName = NormalizeFilename(list.Key, newFileName); + filesToCopyAndZip[file] = Path.Combine(_tempFolder, newFileName); } + } - _worker.ReportProgress(0, LocalizationManager.GetString("DialogBoxes.ArchivingDlg.CopyingFilesMsg", - "Copying files")); + ReportProgress(Progress.GetMessage(StringId.CopyingFiles), MessageType.Success, + cancellationToken); - foreach (var fileToCopy in filesToCopyAndZip) + foreach (var fileToCopy in filesToCopyAndZip) + { + ReportProgress(Path.GetFileName(fileToCopy.Key), MessageType.Detail, + cancellationToken); + if (FileCopyOverride != null) { - if (_cancelProcess) - return; - _worker.ReportProgress(1 /* actual value ignored, progress just increments */, - Path.GetFileName(fileToCopy.Key)); - if (FileCopyOverride != null) + try { - try + if (FileCopyOverride(this, fileToCopy.Key, fileToCopy.Value)) { - if (FileCopyOverride(this, fileToCopy.Key, fileToCopy.Value)) - { - if (!File.Exists(fileToCopy.Value)) - throw new FileNotFoundException("Calling application claimed to copy file but didn't", fileToCopy.Value); - continue; - } - } - catch (Exception error) - { - var msg = GetFileExcludedMsg(NameOfProgramToLaunch, fileToCopy.Value); - ReportError(error, msg); + if (!File.Exists(fileToCopy.Value)) + throw new FileNotFoundException( + "Calling application claimed to copy file but didn't", + fileToCopy.Value); + continue; } } - // Don't use File.Copy because it's asynchronous. - CopyFile(fileToCopy.Key, fileToCopy.Value); + catch (Exception error) + { + var msg = GetFileExcludedMsg(fileToCopy.Value); + ReportError(error, msg); + } } - _worker.ReportProgress(0, GetSavingFilesMsg(NameOfProgramToLaunch)); - - using (var zip = new ZipFile()) - { - // RAMP packages must not be compressed or RAMP can't read them. - zip.UseZip64WhenSaving = Zip64Option.AsNecessary; // See SP-2291 - zip.CompressionLevel = Ionic.Zlib.CompressionLevel.None; - zip.AddFiles(filesToCopyAndZip.Values, @"\"); - zip.AddFile(_metsFilePath, string.Empty); - zip.SaveProgress += HandleZipSaveProgress; - zip.Save(PackagePath); - - if (!_cancelProcess && IncrementProgressBarAction != null) - Thread.Sleep(800); - } + // Don't use File.Copy because it's asynchronous. + CopyFile(fileToCopy.Key, fileToCopy.Value); } - catch (Exception exception) + + ReportMajorProgressPoint(StringId.SavingFilesInPackage, cancellationToken); + + using (var zip = new ZipFile()) { - _worker.ReportProgress(0, new KeyValuePair(exception, - LocalizationManager.GetString("DialogBoxes.ArchivingDlg.CreatingRAMPFileErrorMsg", - "There was an error attempting to create the RAMP file."))); + // RAMP packages must not be compressed or RAMP can't read them. + zip.UseZip64WhenSaving = Zip64Option.AsNecessary; // See SP-2291 + zip.CompressionLevel = Ionic.Zlib.CompressionLevel.None; + zip.AddFiles(filesToCopyAndZip.Values, @"\"); + zip.AddFile(_metsFilePath, Empty); + zip.SaveProgress += delegate(object sender, SaveProgressEventArgs args) + { + HandleZipSaveProgress(args, cancellationToken); + }; + zip.Save(PackagePath); - _workerException = true; + if (!cancellationToken.IsCancellationRequested) + Thread.Sleep(800); } } @@ -1748,44 +1699,16 @@ private void CreateZipFileInWorkerThread(object sender, DoWorkEventArgs e) /// saved to the disk. /// /// ------------------------------------------------------------------------------------ - private void HandleZipSaveProgress(object s, SaveProgressEventArgs e) + private void HandleZipSaveProgress(SaveProgressEventArgs e, + CancellationToken cancellationToken) { - if (_cancelProcess || e.EventType != ZipProgressEventType.Saving_BeforeWriteEntry) + if (e.EventType != ZipProgressEventType.Saving_BeforeWriteEntry) return; - string msg; - if (_progressMessages.TryGetValue(e.CurrentEntry.FileName, out msg)) + if (_progressMessages.TryGetValue(e.CurrentEntry.FileName, out var msg)) DisplayMessage(msg, MessageType.Progress); - _worker.ReportProgress(e.EntriesSaved + 1, Path.GetFileName(e.CurrentEntry.FileName)); - } - - /// ------------------------------------------------------------------------------------ - void HandleBackgroundWorkerProgressChanged(object sender, ProgressChangedEventArgs e) - { - if (e.UserState == null || _cancelProcess) - return; - - if (e.UserState is KeyValuePair) - { - var kvp = (KeyValuePair)e.UserState; - ReportError(kvp.Key, kvp.Value); - return; - } - - if (!string.IsNullOrEmpty(e.UserState as string)) - { - if (e.ProgressPercentage == 0) - { - DisplayMessage(e.UserState.ToString(), MessageType.Success); - return; - } - - DisplayMessage(e.UserState.ToString(), MessageType.Detail); - } - - if (IncrementProgressBarAction != null) - IncrementProgressBarAction(); + ReportProgress(Path.GetFileName(e.CurrentEntry.FileName), MessageType.Detail, cancellationToken); } #endregion @@ -1813,7 +1736,7 @@ private static string GetLanguageFileLocation() //Ramp 3.0 Package doesn't have languages.yaml if (!Directory.Exists(Path.Combine(dir, "data"))) { - return string.Empty; + return Empty; } } // on Linux the exe and data directory are not in the same directory @@ -1829,24 +1752,24 @@ private static string GetLanguageFileLocation() //Ramp 3.0 Package doesn't have languages.yaml if (!Directory.Exists(Path.Combine(dir, "data"))) { - return string.Empty; + return Empty; } } // get the data directory dir = Path.Combine(dir, "data"); if (!Directory.Exists(dir)) - throw new DirectoryNotFoundException(string.Format("The path {0} is not valid.", dir)); + throw new DirectoryNotFoundException(Format("The path {0} is not valid.", dir)); // get the options directory dir = Path.Combine(dir, "options"); if (!Directory.Exists(dir)) - throw new DirectoryNotFoundException(string.Format("The path {0} is not valid.", dir)); + throw new DirectoryNotFoundException(Format("The path {0} is not valid.", dir)); // get the languages.yaml file var langFile = Path.Combine(dir, "languages.yaml"); if (!File.Exists(langFile)) - throw new FileNotFoundException(string.Format("The file {0} was not found.", langFile)); + throw new FileNotFoundException(Format("The file {0} was not found.", langFile), langFile); return langFile; } @@ -1901,31 +1824,20 @@ public string GetLanguageName(string iso3Code) } #endregion + #region Clean-up methods /// ------------------------------------------------------------------------------------ - public override void Cancel() + protected internal override void CleanUp() { - base.Cancel(); - - CleanUp(); + base.CleanUp(); + DeleteTempFolder(); CleanUpTempRampPackage(); } - /// ------------------------------------------------------------------------------------ - public override IArchivingSession AddSession(string sessionId) - { - throw new NotImplementedException(); - } - - public override IArchivingPackage ArchivingPackage + private void DeleteTempFolder() { - get { throw new NotImplementedException(); } - } - - #region Clean-up methods - /// ------------------------------------------------------------------------------------ - public void CleanUp() - { - try { Directory.Delete(_tempFolder, true); } + try { + Directory.Delete(_tempFolder, true); + } // ReSharper disable once EmptyGeneralCatchClause catch { } } @@ -1933,6 +1845,7 @@ public void CleanUp() /// ------------------------------------------------------------------------------------ public void CleanUpTempRampPackage() { + // REVIEW: How long is this test supposed to last? // Comment out as a test !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! //try { File.Delete(RampPackagePath); } //catch { } diff --git a/SIL.Archiving/Resources/EmptyMets.xml b/SIL.Archiving/Resources/EmptyMets.xml old mode 100755 new mode 100644 index 8a99cd22b..0eaceafec --- a/SIL.Archiving/Resources/EmptyMets.xml +++ b/SIL.Archiving/Resources/EmptyMets.xml @@ -1,4 +1,4 @@ - + diff --git a/SIL.Archiving/Resources/Resources.cs b/SIL.Archiving/Resources/Resources.cs new file mode 100644 index 000000000..8b627c66d --- /dev/null +++ b/SIL.Archiving/Resources/Resources.cs @@ -0,0 +1,37 @@ +using System.IO; +using System.Reflection; +using System.Resources; + +namespace SIL.Archiving.Resources +{ + internal static class Resources + { + public enum Name + { + AccessProtocols_json, + CustomAccessProtocols_json, + EmptyMets_xml, + } + + public static string GetResource(Name namedResource) + { + return GetResource(namedResource.ToString().Replace("_", ".")); + } + + public static string GetResource(string filename) + { + using (var stream = Assembly.GetExecutingAssembly() + .GetManifestResourceStream($"SIL.Archiving.Resources.{filename}")) + { + if (stream == null) + { + throw new MissingManifestResourceException( + $"{filename} resource not found"); + } + + var reader = new StreamReader(stream); + return reader.ReadToEnd(); + } + } + } +} diff --git a/SIL.Archiving/SIL.Archiving.csproj b/SIL.Archiving/SIL.Archiving.csproj index 8f89ba13c..5bcf82d5b 100644 --- a/SIL.Archiving/SIL.Archiving.csproj +++ b/SIL.Archiving/SIL.Archiving.csproj @@ -3,17 +3,27 @@ SIL.Archiving SIL.Archiving - SIL.Archiving contains Windows Forms UI elements and classes for archiving data to REAP using the RAMP application. - true + SIL.Archiving contains classes for archiving data to REAP and IMDI. + + + + + + + + + + + + All - @@ -22,25 +32,38 @@ - + + + + + + + + + + + + + + + + - True + True True Settings.settings - + SettingsSingleFileGenerator Settings.Designer.cs - - \ No newline at end of file diff --git a/SIL.Archiving/app.config b/SIL.Archiving/app.config deleted file mode 100755 index bdd9c96b9..000000000 --- a/SIL.Archiving/app.config +++ /dev/null @@ -1,22 +0,0 @@ - - - - -
    - - - - - - https://gateway.sil.org/display/RH/RAMP+Users%27+Manual - - - author;compiler;consultant;developer;editor;facilitator;illustrator;interviewer;photographer;recorder;researcher;signer;speaker;transcriber;translator - - - http://www.mpi.nl/imdi/ - - - - - diff --git a/SIL.Windows.Forms.Tests/ClearShare/ContributionTests.cs b/SIL.Core.Tests/ClearShare/ContributionTests.cs similarity index 98% rename from SIL.Windows.Forms.Tests/ClearShare/ContributionTests.cs rename to SIL.Core.Tests/ClearShare/ContributionTests.cs index 9a2d5d989..4bae090f7 100644 --- a/SIL.Windows.Forms.Tests/ClearShare/ContributionTests.cs +++ b/SIL.Core.Tests/ClearShare/ContributionTests.cs @@ -1,8 +1,8 @@ -using System; +using System; using NUnit.Framework; -using SIL.Windows.Forms.ClearShare; +using SIL.Core.ClearShare; -namespace SIL.Windows.Forms.Tests.ClearShare +namespace SIL.Tests.ClearShare { [TestFixture] public class ContributionTests diff --git a/SIL.Core.Tests/ClearShare/LicenseTests.cs b/SIL.Core.Tests/ClearShare/LicenseTests.cs new file mode 100644 index 000000000..14b13c3c9 --- /dev/null +++ b/SIL.Core.Tests/ClearShare/LicenseTests.cs @@ -0,0 +1,69 @@ +using NUnit.Framework; +using SIL.Core.ClearShare; + +namespace SIL.Tests.ClearShare +{ + /// ---------------------------------------------------------------------------------------- + [TestFixture] + public class LicenseTests + { + /// ------------------------------------------------------------------------------------ + [Test] + public void AreContentsEqual_OtherIsNull_ReturnsFalse() + { + var l = License.CreativeCommons_Attribution_ShareAlike; + Assert.IsFalse(l.AreContentsEqual(null)); + } + + /// ------------------------------------------------------------------------------------ + [Test] + public void AreContentsEqual_AreDifferent_ReturnsFalse() + { + var l1 = License.CreativeCommons_Attribution_ShareAlike; + var l2 = License.CreativeCommons_Attribution; + Assert.IsFalse(l1.AreContentsEqual(l2)); + } + + /// ------------------------------------------------------------------------------------ + [Test] + public void AreContentsEqual_AreSame_ReturnsTrue() + { + var l1 = License.CreativeCommons_Attribution_ShareAlike; + var l2 = License.CreativeCommons_Attribution_ShareAlike; + Assert.IsTrue(l1.AreContentsEqual(l2)); + } + + /// ------------------------------------------------------------------------------------ + [Test] + public void Equals_SameInstance_ReturnsTrue() + { + var l = License.CreativeCommons_Attribution_ShareAlike; + Assert.IsTrue(l.Equals(l)); + } + + /// ------------------------------------------------------------------------------------ + [Test] + public void Equals_CompareToNull_ReturnsFalse() + { + var l = License.CreativeCommons_Attribution_ShareAlike; + Assert.IsFalse(l.Equals(null)); + } + + /// ------------------------------------------------------------------------------------ + [Test] + public void Equals_CompareToObjOfDifferentType_ReturnsFalse() + { + var l = License.CreativeCommons_Attribution_ShareAlike; + Assert.IsFalse(l.Equals("junk")); + } + + /// ------------------------------------------------------------------------------------ + [Test] + public void Equals_AreSame_ReturnsTrue() + { + var l1 = License.CreativeCommons_Attribution_ShareAlike; + var l2 = License.CreativeCommons_Attribution_ShareAlike; + Assert.IsTrue(l1.Equals(l2)); + } + } +} diff --git a/SIL.Windows.Forms.Tests/ClearShare/OlacSystemTests.cs b/SIL.Core.Tests/ClearShare/OlacSystemTests.cs similarity index 95% rename from SIL.Windows.Forms.Tests/ClearShare/OlacSystemTests.cs rename to SIL.Core.Tests/ClearShare/OlacSystemTests.cs index 0b3a86c36..5ded293e2 100644 --- a/SIL.Windows.Forms.Tests/ClearShare/OlacSystemTests.cs +++ b/SIL.Core.Tests/ClearShare/OlacSystemTests.cs @@ -1,8 +1,8 @@ -using System; +using System; using NUnit.Framework; -using SIL.Windows.Forms.ClearShare; +using SIL.Core.ClearShare; -namespace SIL.Windows.Forms.Tests.ClearShare +namespace SIL.Tests.ClearShare { [TestFixture] public class OlacSystemTests diff --git a/SIL.Windows.Forms.Tests/ClearShare/RoleTests.cs b/SIL.Core.Tests/ClearShare/RoleTests.cs similarity index 96% rename from SIL.Windows.Forms.Tests/ClearShare/RoleTests.cs rename to SIL.Core.Tests/ClearShare/RoleTests.cs index 5ea4f46cc..079955615 100644 --- a/SIL.Windows.Forms.Tests/ClearShare/RoleTests.cs +++ b/SIL.Core.Tests/ClearShare/RoleTests.cs @@ -1,7 +1,7 @@ -using NUnit.Framework; -using SIL.Windows.Forms.ClearShare; +using NUnit.Framework; +using SIL.Core.ClearShare; -namespace SIL.Windows.Forms.Tests.ClearShare +namespace SIL.Tests.ClearShare { [TestFixture] public class RoleTests diff --git a/SIL.Windows.Forms/ClearShare/Contribution.cs b/SIL.Core/ClearShare/Contribution.cs similarity index 98% rename from SIL.Windows.Forms/ClearShare/Contribution.cs rename to SIL.Core/ClearShare/Contribution.cs index 5ecfa3483..3db329f79 100644 --- a/SIL.Windows.Forms/ClearShare/Contribution.cs +++ b/SIL.Core/ClearShare/Contribution.cs @@ -1,6 +1,6 @@ -using System; +using System; -namespace SIL.Windows.Forms.ClearShare +namespace SIL.Core.ClearShare { /// /// Records a single contribution of a single individual to a single "work". diff --git a/SIL.Windows.Forms/ClearShare/ContributionCollection.cs b/SIL.Core/ClearShare/ContributionCollection.cs similarity index 97% rename from SIL.Windows.Forms/ClearShare/ContributionCollection.cs rename to SIL.Core/ClearShare/ContributionCollection.cs index 7dfb8d52d..4f6f5bfc4 100644 --- a/SIL.Windows.Forms/ClearShare/ContributionCollection.cs +++ b/SIL.Core/ClearShare/ContributionCollection.cs @@ -1,7 +1,7 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Text; -namespace SIL.Windows.Forms.ClearShare +namespace SIL.Core.ClearShare { /// ---------------------------------------------------------------------------------------- public interface IAutoCompleteValueProviderWeird { diff --git a/SIL.Windows.Forms/ClearShare/License.cs b/SIL.Core/ClearShare/License.cs similarity index 69% rename from SIL.Windows.Forms/ClearShare/License.cs rename to SIL.Core/ClearShare/License.cs index 1583a262e..f0fa4c9d9 100644 --- a/SIL.Windows.Forms/ClearShare/License.cs +++ b/SIL.Core/ClearShare/License.cs @@ -1,9 +1,7 @@ -using System.Drawing; - -namespace SIL.Windows.Forms.ClearShare +namespace SIL.Core.ClearShare { /// - /// describes a single license, under which many works can be licensed for use + /// Describes a single license, under which many works can be licensed for use /// public class License { @@ -14,35 +12,23 @@ public class License public string Name { get; private set; } - public Image Logo { get; private set; } - //TODO: support the full six options at http://creativecommons.org/licenses/, plus public domain /// ------------------------------------------------------------------------------------ - public static License CreativeCommons_Attribution_ShareAlike - { - get + public static License CreativeCommons_Attribution_ShareAlike => + new License { - return new License - { - Name = "Creative Commons. Attribution-ShareAlike 3.0", - Url = "http://creativecommons.org/licenses/by-sa/3.0/" - }; - } - } + Name = "Creative Commons. Attribution-ShareAlike 3.0", + Url = "http://creativecommons.org/licenses/by-sa/3.0/" + }; /// ------------------------------------------------------------------------------------ - public static License CreativeCommons_Attribution - { - get + public static License CreativeCommons_Attribution => + new License { - return new License - { - Name = "Creative Commons. Attribution 3.0", - Url = "http://creativecommons.org/licenses/by/3.0/" - }; - } - } + Name = "Creative Commons. Attribution 3.0", + Url = "http://creativecommons.org/licenses/by/3.0/" + }; /// ------------------------------------------------------------------------------------ public override int GetHashCode() @@ -51,7 +37,6 @@ public override int GetHashCode() { int result = (Url != null ? Url.GetHashCode() : 0); result = (result * 397) ^ (Name != null ? Name.GetHashCode() : 0); - result = (result * 397) ^ (Logo != null ? Logo.GetHashCode() : 0); return result; } } @@ -79,7 +64,6 @@ public override bool Equals(object other) /// ------------------------------------------------------------------------------------ public bool AreContentsEqual(License other) { - // TODO: compare logo images. return (other != null && Name.Equals(other.Name) && Url.Equals(other.Url)); } } diff --git a/SIL.Windows.Forms/Resources/OlacRoles.xml b/SIL.Core/ClearShare/OlacRoles.xml similarity index 100% rename from SIL.Windows.Forms/Resources/OlacRoles.xml rename to SIL.Core/ClearShare/OlacRoles.xml diff --git a/SIL.Windows.Forms/ClearShare/OlacSystem.cs b/SIL.Core/ClearShare/OlacSystem.cs similarity index 65% rename from SIL.Windows.Forms/ClearShare/OlacSystem.cs rename to SIL.Core/ClearShare/OlacSystem.cs index 03edb27db..f2fe677c8 100644 --- a/SIL.Windows.Forms/ClearShare/OlacSystem.cs +++ b/SIL.Core/ClearShare/OlacSystem.cs @@ -1,14 +1,16 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Reflection; using System.Text; using System.Xml; using System.Xml.Linq; +using JetBrains.Annotations; using SIL.Code; -using SIL.Windows.Forms.Properties; +using SIL.Reporting; -namespace SIL.Windows.Forms.ClearShare +namespace SIL.Core.ClearShare { ///----------------------------------------------------------------------------------------- /// @@ -18,11 +20,12 @@ namespace SIL.Windows.Forms.ClearShare ///----------------------------------------------------------------------------------------- public class OlacSystem { - private IEnumerable _roles; + private List _roles; private static readonly XNamespace s_nsOlac = "http://www.language-archives.org/OLAC/1.1/"; private static readonly XNamespace s_nsDc = "http://purl.org/dc/elements/1.1/"; /// ------------------------------------------------------------------------------------ + [PublicAPI] public void LoadWorkFromXml(Work work, string xml) { //TODO: parse the xml as olac. For a first pass, we can ignore anything we don't understand. @@ -44,10 +47,10 @@ public void LoadWorkFromXml(Work work, string xml) /// ------------------------------------------------------------------------------------ public string GetOlacRecordElement() { - return string.Format(@"", s_nsOlac, s_nsDc); + return $@""; } /// ------------------------------------------------------------------------------------ @@ -56,11 +59,12 @@ public string GetContributorElement(string roleCode, string contributorName) Guard.AgainstNull(roleCode, "roleCode"); Guard.AgainstNull(contributorName, "contributorName"); - return string.Format("{1}", roleCode, contributorName); + return $"{contributorName}"; } /// ------------------------------------------------------------------------------------ + [PublicAPI] public string GetXmlForWork(Work work) { var bldr = new StringBuilder(); @@ -96,23 +100,58 @@ public string GetXmlForWork(Work work) /// Get all the roles in the system's controlled vocabulary /// /// ------------------------------------------------------------------------------------ - public IEnumerable GetRoles() + public IEnumerable GetRoles(string customRolesXmlFilename = null) { + List ReadRoles(Stream stream) + { + List roles; + + try + { + using (var xmlReader = XmlReader.Create(stream)) + { + var doc = XDocument.Load(xmlReader); + + // This is a bit confusing because the role heading node is at the same level + // (i.e. a sibling of) as all the associated term nodes. Therefore, the first + // thing to do is get the heading nodes that are children of section nodes. + // Then find the one heading node whose content is "Role". Then backup to the + // section containing that heading node and take all "term" child nodes of + // that section (i.e. that are siblings of the role heading node). + roles = doc.Descendants("body").Descendants("section") + .Elements("heading") + .Where(n => n.Value == "Role").Ancestors("section").First() + .Descendants("term") + .Select(n => new Role(n.Element("code").Value, + n.Element("name").Value, n.Element("definition").Value)).ToList(); + xmlReader.Close(); + } + } + finally + { + stream.Close(); + stream.Dispose(); + } + + return roles; + } + if (_roles == null) { - // TODO: Provide a way for user-specified roles to be read from a roles.xml - // file in a folder somewhere related to the application. - var doc = XDocument.Parse(Resources.OlacRoles); - - // This is a bit confusing because the role heading node is at the same level - // (i.e. a sibling of) as all the associated term nodes. Therefore, the first - // thing to do is get the heading nodes that are children of section nodes. - // Then find the one heading node whose content is "Role". Then backup to the - // section containing that heading node and take all "term" child nodes of - // that section (i.e. that are siblings of the role heading node). - _roles = doc.Descendants("body").Descendants("section").Elements("heading") - .Where(n => n.Value == "Role").Ancestors("section").First().Descendants("term") - .Select(n => new Role(n.Element("code").Value, n.Element("name").Value, n.Element("definition").Value)); + if (customRolesXmlFilename != null) + { + try + { + _roles = ReadRoles(new FileStream(customRolesXmlFilename, FileMode.Open)); + } + catch (Exception e) + { + ErrorReport.ReportNonFatalException(e); + } + } + + _roles ??= ReadRoles(Assembly.GetExecutingAssembly() + .GetManifestResourceStream("SIL.Core.ClearShare.OlacRoles.xml")); } return _roles; @@ -134,13 +173,14 @@ public bool TryGetRoleByCode(string code, out Role role) /// Used to look up roles in the system's controlled vocabulary /// /// ------------------------------------------------------------------------------------ + [PublicAPI] public Role GetRoleByCodeOrThrow(string code) { var role = GetRoles().FirstOrDefault(r => r.Code == code); if (role == null) { - var msg = string.Format("This version of OLAC does not contain a role with code '{0}'.", code); + var msg = $"This version of OLAC does not contain a role with code '{code}'."; throw new ArgumentOutOfRangeException(msg); } @@ -152,6 +192,7 @@ public Role GetRoleByCodeOrThrow(string code) /// Used to look up roles in the system's controlled vocabulary /// /// ------------------------------------------------------------------------------------ + [PublicAPI] public bool TryGetRoleByName(string name, out Role role) { role = GetRoles().FirstOrDefault(r => r.Name == name); diff --git a/SIL.Windows.Forms/ClearShare/Role.cs b/SIL.Core/ClearShare/Role.cs similarity index 97% rename from SIL.Windows.Forms/ClearShare/Role.cs rename to SIL.Core/ClearShare/Role.cs index 96448e626..347019d97 100644 --- a/SIL.Windows.Forms/ClearShare/Role.cs +++ b/SIL.Core/ClearShare/Role.cs @@ -1,7 +1,7 @@ -using System; +using System; using System.Text; -namespace SIL.Windows.Forms.ClearShare +namespace SIL.Core.ClearShare { public class Role { diff --git a/SIL.Windows.Forms/ClearShare/Work.cs b/SIL.Core/ClearShare/Work.cs similarity index 90% rename from SIL.Windows.Forms/ClearShare/Work.cs rename to SIL.Core/ClearShare/Work.cs index 74533b494..2c49080bc 100644 --- a/SIL.Windows.Forms/ClearShare/Work.cs +++ b/SIL.Core/ClearShare/Work.cs @@ -1,6 +1,6 @@ -using System.Collections.Generic; +using System.Collections.Generic; -namespace SIL.Windows.Forms.ClearShare +namespace SIL.Core.ClearShare { /// /// The word "work" in isolation doesn't suggest the right sense, here. Think of "derived-work" or "work of fiction". diff --git a/SIL.Core/SIL.Core.csproj b/SIL.Core/SIL.Core.csproj index 5bc772ecd..c9895e467 100644 --- a/SIL.Core/SIL.Core.csproj +++ b/SIL.Core/SIL.Core.csproj @@ -5,6 +5,14 @@ SIL.Core provides general utilities for language software. It is the base library for all Palaso libraries. + + + + + + + + diff --git a/SIL.Media.Tests/SIL.Media.Tests.csproj b/SIL.Media.Tests/SIL.Media.Tests.csproj index 4d405e5b8..32411b10c 100644 --- a/SIL.Media.Tests/SIL.Media.Tests.csproj +++ b/SIL.Media.Tests/SIL.Media.Tests.csproj @@ -19,6 +19,7 @@ + diff --git a/SIL.Scripture.Tests/SIL.Scripture.Tests.csproj b/SIL.Scripture.Tests/SIL.Scripture.Tests.csproj index bb22ebcb0..927604ab3 100644 --- a/SIL.Scripture.Tests/SIL.Scripture.Tests.csproj +++ b/SIL.Scripture.Tests/SIL.Scripture.Tests.csproj @@ -14,6 +14,7 @@ + diff --git a/SIL.Windows.Forms.Archiving/ArchivingDlg.Designer.cs b/SIL.Windows.Forms.Archiving/ArchivingDlg.Designer.cs new file mode 100644 index 000000000..6ae64e05a --- /dev/null +++ b/SIL.Windows.Forms.Archiving/ArchivingDlg.Designer.cs @@ -0,0 +1,261 @@ +using SIL.Windows.Forms.Progress; + +namespace SIL.Windows.Forms.Archiving +{ + partial class ArchivingDlg + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this._tableLayoutPanel = new System.Windows.Forms.TableLayoutPanel(); + this._buttonCreatePackage = new System.Windows.Forms.Button(); + this._linkOverview = new System.Windows.Forms.LinkLabel(); + this._progressBar = new System.Windows.Forms.ProgressBar(); + this._logBox = new SIL.Windows.Forms.Progress.LogBox(); + this._buttonLaunchRamp = new System.Windows.Forms.Button(); + this._buttonCancel = new System.Windows.Forms.Button(); + this._flowLayoutExtra = new System.Windows.Forms.FlowLayoutPanel(); + this._chkMetadataOnly = new System.Windows.Forms.CheckBox(); + this.locExtender = new L10NSharp.UI.L10NSharpExtender(this.components); + this._tableLayoutPanel.SuspendLayout(); + this._flowLayoutExtra.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.locExtender)).BeginInit(); + this.SuspendLayout(); + // + // _tableLayoutPanel + // + this._tableLayoutPanel.ColumnCount = 3; + this._tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this._tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this._tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this._tableLayoutPanel.Controls.Add(this._buttonCreatePackage, 0, 4); + this._tableLayoutPanel.Controls.Add(this._linkOverview, 0, 0); + this._tableLayoutPanel.Controls.Add(this._progressBar, 0, 2); + this._tableLayoutPanel.Controls.Add(this._logBox, 0, 1); + this._tableLayoutPanel.Controls.Add(this._buttonLaunchRamp, 1, 4); + this._tableLayoutPanel.Controls.Add(this._buttonCancel, 2, 4); + this._tableLayoutPanel.Controls.Add(this._flowLayoutExtra, 0, 3); + this._tableLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this._tableLayoutPanel.Location = new System.Drawing.Point(12, 12); + this._tableLayoutPanel.Name = "_tableLayoutPanel"; + this._tableLayoutPanel.RowCount = 5; + this._tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this._tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this._tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this._tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this._tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this._tableLayoutPanel.Size = new System.Drawing.Size(355, 408); + this._tableLayoutPanel.TabIndex = 0; + // + // _buttonCreatePackage + // + this._buttonCreatePackage.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this._buttonCreatePackage.AutoSize = true; + this._buttonCreatePackage.Enabled = false; + this.locExtender.SetLocalizableToolTip(this._buttonCreatePackage, null); + this.locExtender.SetLocalizationComment(this._buttonCreatePackage, null); + this.locExtender.SetLocalizingId(this._buttonCreatePackage, "DialogBoxes.ArchivingDlg._buttonCreatePackage"); + this._buttonCreatePackage.Location = new System.Drawing.Point(58, 382); + this._buttonCreatePackage.Margin = new System.Windows.Forms.Padding(8, 0, 0, 0); + this._buttonCreatePackage.Name = "_buttonCreatePackage"; + this._buttonCreatePackage.Size = new System.Drawing.Size(106, 26); + this._buttonCreatePackage.TabIndex = 0; + this._buttonCreatePackage.Text = "&1) Create Package"; + this._buttonCreatePackage.UseVisualStyleBackColor = true; + this._buttonCreatePackage.Click += new System.EventHandler(this.CreatePackage); + // + // _linkOverview + // + this._linkOverview.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this._linkOverview.AutoSize = true; + this._tableLayoutPanel.SetColumnSpan(this._linkOverview, 3); + this._linkOverview.LinkArea = new System.Windows.Forms.LinkArea(0, 0); + this.locExtender.SetLocalizableToolTip(this._linkOverview, null); + this.locExtender.SetLocalizationComment(this._linkOverview, ""); + this.locExtender.SetLocalizationPriority(this._linkOverview, L10NSharp.LocalizationPriority.NotLocalizable); + this.locExtender.SetLocalizingId(this._linkOverview, "DialogBoxes.ArchivingDlg.OverviewText"); + this._linkOverview.Location = new System.Drawing.Point(3, 0); + this._linkOverview.Name = "_linkOverview"; + this._linkOverview.Size = new System.Drawing.Size(349, 13); + this._linkOverview.TabIndex = 3; + this._linkOverview.Text = "#"; + this._linkOverview.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.HandleRampLinkClicked); + // + // _progressBar + // + this._progressBar.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this._tableLayoutPanel.SetColumnSpan(this._progressBar, 3); + this._progressBar.Location = new System.Drawing.Point(0, 326); + this._progressBar.Margin = new System.Windows.Forms.Padding(0, 0, 0, 12); + this._progressBar.Name = "_progressBar"; + this._progressBar.Size = new System.Drawing.Size(355, 15); + this._progressBar.TabIndex = 4; + // + // _logBox + // + this._logBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this._logBox.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this._logBox.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(171)))), ((int)(((byte)(173)))), ((int)(((byte)(179))))); + this._logBox.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this._logBox.CancelRequested = false; + this._tableLayoutPanel.SetColumnSpan(this._logBox, 3); + this._logBox.ErrorEncountered = false; + this._logBox.Font = new System.Drawing.Font("Segoe UI", 9F); + this._logBox.GetDiagnosticsMethod = null; + this.locExtender.SetLocalizableToolTip(this._logBox, null); + this.locExtender.SetLocalizationComment(this._logBox, null); + this.locExtender.SetLocalizingId(this._logBox, "LogBox"); + this._logBox.Location = new System.Drawing.Point(0, 18); + this._logBox.Margin = new System.Windows.Forms.Padding(0, 5, 0, 5); + this._logBox.MaxLength = 715827882; + this._logBox.MaxLengthErrorMessage = "Maximum length exceeded!"; + this._logBox.Name = "_logBox"; + this._logBox.ProgressIndicator = null; + this._logBox.ShowCopyToClipboardMenuItem = false; + this._logBox.ShowDetailsMenuItem = false; + this._logBox.ShowDiagnosticsMenuItem = false; + this._logBox.ShowFontMenuItem = false; + this._logBox.ShowMenu = false; + this._logBox.Size = new System.Drawing.Size(355, 303); + this._logBox.TabIndex = 5; + this._logBox.TabStop = false; + this._logBox.ReportErrorLinkClicked += new System.EventHandler(this.HandleLogBoxReportErrorLinkClicked); + // + // _buttonLaunchRamp + // + this._buttonLaunchRamp.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this._buttonLaunchRamp.AutoSize = true; + this._buttonLaunchRamp.DialogResult = System.Windows.Forms.DialogResult.OK; + this._buttonLaunchRamp.Enabled = false; + this.locExtender.SetLocalizableToolTip(this._buttonLaunchRamp, null); + this.locExtender.SetLocalizationComment(this._buttonLaunchRamp, null); + this.locExtender.SetLocalizingId(this._buttonLaunchRamp, "DialogBoxes.ArchivingDlg._buttonLaunchRamp"); + this._buttonLaunchRamp.Location = new System.Drawing.Point(172, 382); + this._buttonLaunchRamp.Margin = new System.Windows.Forms.Padding(8, 0, 0, 0); + this._buttonLaunchRamp.Name = "_buttonLaunchRamp"; + this._buttonLaunchRamp.Size = new System.Drawing.Size(100, 26); + this._buttonLaunchRamp.TabIndex = 1; + this._buttonLaunchRamp.Text = "&2) Launch {0}"; + this._buttonLaunchRamp.UseVisualStyleBackColor = true; + // + // _buttonCancel + // + this._buttonCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this._buttonCancel.AutoSize = true; + this._buttonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.locExtender.SetLocalizableToolTip(this._buttonCancel, null); + this.locExtender.SetLocalizationComment(this._buttonCancel, null); + this.locExtender.SetLocalizingId(this._buttonCancel, "DialogBoxes.ArchivingDlg._buttonCancel"); + this._buttonCancel.Location = new System.Drawing.Point(280, 382); + this._buttonCancel.Margin = new System.Windows.Forms.Padding(8, 0, 0, 0); + this._buttonCancel.Name = "_buttonCancel"; + this._buttonCancel.Size = new System.Drawing.Size(75, 26); + this._buttonCancel.TabIndex = 2; + this._buttonCancel.Text = "Cancel"; + this._buttonCancel.UseVisualStyleBackColor = true; + this._buttonCancel.Click += new System.EventHandler(this.HandleButtonCancelClick); + // + // _flowLayoutExtra + // + this._flowLayoutExtra.AutoSize = true; + this._flowLayoutExtra.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this._tableLayoutPanel.SetColumnSpan(this._flowLayoutExtra, 3); + this._flowLayoutExtra.Controls.Add(this._chkMetadataOnly); + this._flowLayoutExtra.Dock = System.Windows.Forms.DockStyle.Top; + this._flowLayoutExtra.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; + this._flowLayoutExtra.Location = new System.Drawing.Point(3, 356); + this._flowLayoutExtra.Name = "_flowLayoutExtra"; + this._flowLayoutExtra.Size = new System.Drawing.Size(349, 23); + this._flowLayoutExtra.TabIndex = 6; + // + // _chkMetadataOnly + // + this._chkMetadataOnly.AutoSize = true; + this.locExtender.SetLocalizableToolTip(this._chkMetadataOnly, "Select this to prevent copying actual data files into archive"); + this.locExtender.SetLocalizationComment(this._chkMetadataOnly, null); + this.locExtender.SetLocalizingId(this._chkMetadataOnly, "ArchivingDlg._chkMetadataOnly"); + this._chkMetadataOnly.Location = new System.Drawing.Point(3, 3); + this._chkMetadataOnly.Name = "_chkMetadataOnly"; + this._chkMetadataOnly.Size = new System.Drawing.Size(93, 17); + this._chkMetadataOnly.TabIndex = 0; + this._chkMetadataOnly.Text = "Metadata only"; + this._chkMetadataOnly.UseVisualStyleBackColor = true; + this._chkMetadataOnly.Visible = false; + this._chkMetadataOnly.CheckedChanged += new System.EventHandler(this._chkMetadataOnly_CheckedChanged); + // + // locExtender + // + this.locExtender.LocalizationManagerId = "SIL.Windows.Forms.Archiving"; + this.locExtender.PrefixForNewItems = null; + // + // ArchivingDlg + // + this.AcceptButton = this._buttonLaunchRamp; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this._buttonCancel; + this.ClientSize = new System.Drawing.Size(379, 432); + this.Controls.Add(this._tableLayoutPanel); + this.locExtender.SetLocalizableToolTip(this, null); + this.locExtender.SetLocalizationComment(this, "Parameter is application name"); + this.locExtender.SetLocalizingId(this, "DialogBoxes.ArchivingDlg.WindowTitle"); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.MinimumSize = new System.Drawing.Size(355, 320); + this.Name = "ArchivingDlg"; + this.Padding = new System.Windows.Forms.Padding(12); + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; + this.Text = "{0}: Archive using {1}"; + this._tableLayoutPanel.ResumeLayout(false); + this._tableLayoutPanel.PerformLayout(); + this._flowLayoutExtra.ResumeLayout(false); + this._flowLayoutExtra.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.locExtender)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.LinkLabel _linkOverview; + private System.Windows.Forms.ProgressBar _progressBar; + private L10NSharp.UI.L10NSharpExtender locExtender; + private LogBox _logBox; + protected System.Windows.Forms.TableLayoutPanel _tableLayoutPanel; + protected System.Windows.Forms.FlowLayoutPanel _flowLayoutExtra; + protected System.Windows.Forms.Button _buttonLaunchRamp; + protected System.Windows.Forms.Button _buttonCreatePackage; + protected System.Windows.Forms.Button _buttonCancel; + private System.Windows.Forms.CheckBox _chkMetadataOnly; + } +} \ No newline at end of file diff --git a/SIL.Windows.Forms.Archiving/ArchivingDlg.cs b/SIL.Windows.Forms.Archiving/ArchivingDlg.cs new file mode 100644 index 000000000..9754ab988 --- /dev/null +++ b/SIL.Windows.Forms.Archiving/ArchivingDlg.cs @@ -0,0 +1,596 @@ +using System; +using System.Diagnostics; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; +using L10NSharp; +using SIL.Archiving; +using SIL.EventsAndDelegates; +using SIL.IO; +using SIL.PlatformUtilities; +using SIL.Reporting; +using SIL.Windows.Forms.Miscellaneous; +using SIL.Windows.Forms.PortableSettingsProvider; +using static System.Environment; +using static System.String; +using static SIL.Archiving.ArchivingDlgViewModel.StringId; + +// ReSharper disable InconsistentNaming + +namespace SIL.Windows.Forms.Archiving +{ + /// ---------------------------------------------------------------------------------------- + public partial class ArchivingDlg : Form, IArchivingProgressDisplay + { + [DllImport("user32.dll", EntryPoint = "SetWindowPos")] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool SetWindowPosWindows(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags); + + // ReSharper disable InconsistentNaming + // ReSharper disable IdentifierTypo + private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1); // brings window to top and makes it "always on top" + private static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2); // brings window to top but not "always on top" + private const UInt32 SWP_NOSIZE = 0x0001; + private const UInt32 SWP_NOMOVE = 0x0002; + private const UInt32 TOPMOST_FLAGS = SWP_NOMOVE | SWP_NOSIZE; + // ReSharper restore InconsistentNaming + // ReSharper restore IdentifierTypo + + private readonly FormSettings _settings; + protected readonly ArchivingDlgViewModel _viewModel; + protected readonly string _launchButtonTextFormat; + protected readonly string _appSpecificArchivalProcessInfo; + protected readonly string _archiveInfoHyperlinkText; + private Exception _exceptionToNotReport; + + private CancellationTokenSource _cts; + + protected virtual string ArchiveTypeForTitleBar => + _viewModel?.ArchiveType == ArchivingDlgViewModel.Standard.REAP ? + LocalizationManager.GetString("DialogBoxes.ArchivingDlg.RAMPArchiveType", + "RAMP (SIL Only)") : null; + + protected virtual string InformativeText => + _viewModel?.ArchiveType == ArchivingDlgViewModel.Standard.REAP ? + Format(LocalizationManager.GetString("DialogBoxes.ArchivingDlg.RAMPOverviewText", + "{0} is a utility for entering metadata and uploading submissions to SIL's internal archive, " + + "REAP. If you have access to this archive, this tool will help you use {0} to archive your " + + "{1} data. {2} When the {0} package has been created, you can launch {0} and enter any " + + "additional information before doing the actual submission.", + "Parameter 0 is the word 'RAMP' (the first one will be turned into a hyperlink); " + + "Parameter 1 is the name of the calling (host) program (SayMore, FLEx, etc.); " + + "Parameter 2 is additional app-specific information."), _viewModel.NameOfProgramToLaunch, _viewModel.AppName, + _appSpecificArchivalProcessInfo) : _appSpecificArchivalProcessInfo; + + /// ------------------------------------------------------------------------------------ + /// Caller can use this to retrieve and persist form settings (typically + /// after form is closed). + /// ------------------------------------------------------------------------------------ + public FormSettings FormSettings => _settings; + + /// ------------------------------------------------------------------------------------ + /// View model + /// Application can use this to pass + /// additional information that will be displayed to the user in the dialog to explain + /// any application-specific details about the archival process. For archive systems + /// ( that this "generic" archiving dialog box + /// does not specifically know what to show as informative text, the value passed in + /// to this parameter will be displayed. + /// The ID of the localization manager for the + /// calling application. + /// Application can set this to ensure a consistent look + /// in the UI (especially useful for when a localization requires a particular font). + /// Location, size, and state where the client would like the + /// dialog box to appear (can be null) + /// Text in the InformativeText that will be + /// marked as a hyperlink to ArchiveInfoUrl (first occurrence only). Defaults to + /// > + /// ------------------------------------------------------------------------------------ + public ArchivingDlg(ArchivingDlgViewModel model, string appSpecificArchivalProcessInfo, + string localizationManagerId = null, Font programDialogFont = null, + FormSettings settings = null, string archiveInfoHyperlinkText = null) + { + _settings = settings ?? FormSettings.Create(this); + + _viewModel = model; + _appSpecificArchivalProcessInfo = appSpecificArchivalProcessInfo; + _archiveInfoHyperlinkText = archiveInfoHyperlinkText ?? + _viewModel.NameOfProgramToLaunch; + + InitializeComponent(); + + if (!IsNullOrEmpty(localizationManagerId)) + locExtender.LocalizationManagerId = localizationManagerId; + + _launchButtonTextFormat = _buttonLaunchRamp.Text; + _progressBar.Visible = false; + _chkMetadataOnly.Visible = _viewModel is ISupportMetadataOnly; + + _logBox.Tag = false; + + model.OnReportMessage += DisplayMessage; + model.OnError += DisplayError; + model.OnExceptionDuringLaunch += HandleLaunchException; + + if (programDialogFont != null) + { + _linkOverview.Font = programDialogFont; + _logBox.Font = FontHelper.MakeFont(programDialogFont, FontStyle.Bold); + _buttonCancel.Font = programDialogFont; + _buttonCreatePackage.Font = programDialogFont; + _buttonLaunchRamp.Font = programDialogFont; + Font = programDialogFont; + } + + _buttonLaunchRamp.Click += (s, e) => model.LaunchArchivingProgram(); + + _buttonCancel.MouseLeave += delegate + { + if (_cts != null) + WaitCursor.Show(); + }; + + _buttonCancel.MouseEnter += delegate + { + if (_cts != null) + WaitCursor.Hide(); + }; + + } + + private void HandleButtonCancelClick(object sender, EventArgs args) + { + try + { + _cts?.Cancel(); + // British spelling for the ID. American spelling for the string :-) + DisplayMessage(NewLine + LocalizationManager.GetString("DialogBoxes.ArchivingDlg.CancellingMsg", "Canceling..."), ArchivingDlgViewModel.MessageType.Error); + } + catch (Exception e) + { + // Probably a race condition - process just ended. + Console.WriteLine(e); + } + } + + private void HandleLaunchException(EventArgs args) + { + if (_viewModel.ArchiveType == ArchivingDlgViewModel.Standard.REAP && + args.Item is InvalidOperationException) + { + _exceptionToNotReport = args.Item; + EnsureRampHasFocusAndWaitForPackageToUnlock(); + } + } + + /// ------------------------------------------------------------------------------------ + private void EnsureRampHasFocusAndWaitForPackageToUnlock() + { + if (ArchivingDlgViewModel.IsMono) + BringToFrontMono(); + else + BringToFrontWindows(); + } + + private static bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, + int cy, uint uFlags) + { + // on Linux simply return true + return !Platform.IsWindows || SetWindowPosWindows(hWnd, hWndInsertAfter, x, y, cx, cy, uFlags); + } + + private static void BringToFrontWindows() + { + var processes = Process.GetProcessesByName(RampArchivingDlgViewModel.kRampProcessName); + if (processes.Length < 1) return; + + // First, make the window topmost: this puts it in front of all other windows + // and sets it as "always on top." + SetWindowPos(processes[0].MainWindowHandle, HWND_TOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS); + + // Second, make the window not top-most: this removes the "always on top" behavior + // and positions the window on top of all other "not always on top" windows. + SetWindowPos(processes[0].MainWindowHandle, HWND_NOTOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS); + } + + private static void BringToFrontMono() + { + // On mono this requires xdotool or wmctrl + string args = null; + if (!IsNullOrEmpty(FileLocationUtilities.LocateInProgramFiles("xdotool", true))) /* try to find xdotool first */ + args = "-c \"for pid in `xdotool search --name RAMP`; do xdotool windowactivate $pid; done\""; + else if (!IsNullOrEmpty(FileLocationUtilities.LocateInProgramFiles("wmctrl", true))) /* if xdotool is not installed, look for wmctrl */ + args = "-c \"wmctrl -a RAMP\""; + + if (IsNullOrEmpty(args)) return; + + var prs = new Process + { + StartInfo = + { + FileName = "bash", + Arguments = args, + UseShellExecute = false, + RedirectStandardError = true + } + }; + + prs.Start(); + } + + private void CreatePackage(object sender, EventArgs eventArgs) + { + Focus(); + DisableControlsDuringPackageCreation(); + _progressBar.Visible = true; + WaitCursor.Show(); + _logBox.Clear(); + + Task.Run(async () => await CreatePackageAsync()); + } + + private async Task CreatePackageAsync() + { + _cts = new CancellationTokenSource(); + + try + { + var result = await _viewModel.CreatePackage(_cts.Token); + + if (result != null) + PackageCreationComplete(result); + } + catch (OperationCanceledException) + { + CompleteCancellation(); + } + catch (Exception ex) + { + // Handle any other exceptions + DisplayMessage(GetMessage(ErrorCreatingArchive) + + NewLine + ex.Message, ArchivingDlgViewModel.MessageType.Error); + } + finally + { + try + { + _cts.Dispose(); + } + catch (Exception e) + { + Logger.WriteError(e); + } + + _cts = null; + + if (InvokeRequired) + Invoke(new Action (ResetUIForUserInteraction)); + else + ResetUIForUserInteraction(); + } + } + + private void ResetUIForUserInteraction() + { + _progressBar.Visible = false; + WaitCursor.Hide(); + } + + private void CompleteCancellation() + { + // American spelling for the ID. British spelling for the string :-) + DisplayMessage(LocalizationManager.GetString( + "DialogBoxes.ArchivingDlg.CanceledMsg", "Cancelled..."), + ArchivingDlgViewModel.MessageType.Error); + + Thread.Sleep(500); + DialogResult = DialogResult.Cancel; + Close(); + } + + protected virtual void PackageCreationComplete(string result) + { + + if (InvokeRequired) + Invoke(new Action(() => { _buttonLaunchRamp.Enabled = true; })); + else + _buttonLaunchRamp.Enabled = true; + } + + protected virtual void DisableControlsDuringPackageCreation() + { + _buttonCreatePackage.Enabled = false; + _chkMetadataOnly.Enabled = false; + } + + /// ------------------------------------------------------------------------------------ + protected void UpdateLaunchButtonText() + { + _buttonLaunchRamp.Text = Format(_launchButtonTextFormat, _viewModel.NameOfProgramToLaunch); + } + + /// ------------------------------------------------------------------------------------ + protected void UpdateOverviewText() + { + _linkOverview.Text = InformativeText; + } + + #region Implementation of IArchivingProgressDisplay + + public void IncrementProgress() + { + if (InvokeRequired) + Invoke(new Action(() => { _progressBar.Increment(1); })); + else + _progressBar.Increment(1); + } + + /// ------------------------------------------------------------------------------------ + public virtual string GetMessage(ArchivingDlgViewModel.StringId msgId) + { + switch (msgId) + { + case PreArchivingStatus: + return LocalizationManager.GetString( + "DialogBoxes.ArchivingDlg.PrearchivingStatusMsg", + "The following files will be added to the archive:"); + case SearchingForArchiveUploadingProgram: + return Format(LocalizationManager.GetString( + "DialogBoxes.ArchivingDlg.SearchingForRampMsg", + "Searching for the {0} program...", + "Parameter is the path to the auxiliary archive upload program (RAMP, etc.)."), + _viewModel.PathToProgramToLaunch); + case ArchiveUploadingProgramNotFound: + return Format(LocalizationManager.GetString("DialogBoxes.ArchivingDlg.RampNotFoundMsg", + "The {0} program cannot be found!", + "Parameter is the path to the auxiliary archive upload program (RAMP, etc.)."), + _viewModel.PathToProgramToLaunch); + case ErrorStartingArchivalProgram: + return Format(LocalizationManager.GetString( + "DialogBoxes.ArchivingDlg.StartingRampErrorMsg", + "There was an error attempting to open the archive package in {0}.", + "Parameter is the path to the auxiliary archive upload program (RAMP, etc.)."), + _viewModel.PathToProgramToLaunch); + case PreparingFiles: + return LocalizationManager.GetString( + "DialogBoxes.ArchivingDlg.PreparingFilesMsg", + "Analyzing component files"); + case SavingFilesInPackage: + return Format(LocalizationManager.GetString( + "DialogBoxes.ArchivingDlg.SavingFilesInPackageMsg", + "Saving files in {0} package", + "Parameter is the type of archive (e.g., RAMP/IMDI)"), ArchiveTypeName); + case FileExcludedFromPackage: + return Format(LocalizationManager.GetString( + "DialogBoxes.ArchivingDlg.FileExcludedFromPackage", + "File excluded from {0} package: ", + "Parameter is the type of archive (e.g., RAMP/IMDI)"), ArchiveTypeName); + case PathNotWritable: + return LocalizationManager.GetString( + "DialogBoxes.ArchivingDlg.PathNotWritableMsg", + "The path is not writable: {0}"); + case ReadyToCallRampMsg: + return LocalizationManager.GetString( + "DialogBoxes.ArchivingDlg.ReadyToCallRampMsg", + "Ready to hand the package to RAMP"); + case CopyingFiles: + return LocalizationManager.GetString( + "DialogBoxes.ArchivingDlg.CopyingFilesMsg", + "Copying files"); + case FailedToMakePackage: + return Format(LocalizationManager.GetString( + "DialogBoxes.ArchivingDlg.FailedToMakePackage", + "Failed to make the {0} package: {1}", + "Param 0: The type of archive (e.g., RAMP); " + + "Param 1: Path to the package that was supposed to get created"), + ArchiveTypeName, _viewModel.PackagePath); + case ErrorCreatingMetsFile: + return Format(LocalizationManager.GetString("DialogBoxes.ArchivingDlg.CreatingInternalReapMetsFileErrorMsg", + "There was an error attempting to create the {0}/{1} {2} file.", + "Param 0: \"RAMP\" (program for uploading to REAP)" + + "Param 1: \"REAP\" (SIL's corporate archive repository)" + + "Param 2: \"METS\" (Acronym for the Metadata Encoding & Transmission Standard used by the U.S. Library of Congress)"), + RampArchivingDlgViewModel.kRampProcessName, "REAP", "METS"); + case RampPackageRemoved: + return Format(LocalizationManager.GetString("DialogBoxes.ArchivingDlg.", + "{0} package prematurely removed: {1}", + "Param 0: \"RAMP\" (program for uploading to REAP)" + + "Param 1: Path to the missing package"), + _viewModel.PackagePath); + case ErrorCreatingArchive: + return Format(LocalizationManager.GetString( + "DialogBoxes.ArchivingDlg.CreatingArchiveErrorMsg", + "There was an error attempting to create the {0} package: {1}", + "Parameter is the type of archive (e.g., RAMP/IMDI)"), + ArchiveTypeName, _viewModel.PackagePath); + default: + throw new ArgumentOutOfRangeException(nameof(msgId), msgId, null); + } + } + + public virtual string ArchiveTypeName => + _viewModel?.ArchiveType == ArchivingDlgViewModel.Standard.REAP + ? RampArchivingDlgViewModel.kRampProcessName : + _viewModel?.ArchiveType.ToString() ?? "Other"; + + #endregion + + /// ------------------------------------------------------------------------------------ + protected void DisplayMessage(string msg, ArchivingDlgViewModel.MessageType type) + { + if (InvokeRequired) + Invoke(new Action(() => { DisplayMessageOnUIThread(msg, type); })); + + DisplayMessageOnUIThread(msg, type); + } + + /// ------------------------------------------------------------------------------------ + private void DisplayMessageOnUIThread(string msg, ArchivingDlgViewModel.MessageType type) + { + if ((bool) _logBox.Tag) + { + _logBox.Clear(); + _logBox.Tag = false; + } + switch (type) + { + case ArchivingDlgViewModel.MessageType.Normal: + _logBox.WriteMessage(msg); + break; + case ArchivingDlgViewModel.MessageType.Indented: + _logBox.WriteMessage(NewLine + " " + msg); + break; + case ArchivingDlgViewModel.MessageType.Detail: + _logBox.WriteMessageWithFontStyle(FontStyle.Regular, "\t" + msg); + break; + case ArchivingDlgViewModel.MessageType.Bullet: + _logBox.WriteMessageWithFontStyle(FontStyle.Regular, " \u00B7 {0}", msg); + break; + case ArchivingDlgViewModel.MessageType.Progress: + _logBox.WriteMessage(NewLine + msg); + break; + case ArchivingDlgViewModel.MessageType.Warning: + _logBox.WriteWarning(msg); + break; + case ArchivingDlgViewModel.MessageType.Error: + _logBox.WriteMessageWithColor("Red", msg + NewLine); + break; + case ArchivingDlgViewModel.MessageType.Success: + _logBox.WriteMessageWithColor(Color.DarkGreen, NewLine + msg); + break; + case ArchivingDlgViewModel.MessageType.Volatile: + _logBox.WriteMessage(msg); + _logBox.Tag = true; + break; + } + } + + /// ------------------------------------------------------------------------------------ + private void DisplayError(string msg, string packageTitle, Exception e) + { + if (e == _exceptionToNotReport) + { + _exceptionToNotReport = null; + return; + } + + if (InvokeRequired) + Invoke(new Action(() => { DisplayErrorOnUIThread(msg, packageTitle, e); })); + + DisplayErrorOnUIThread(msg, packageTitle, e); + } + + /// ------------------------------------------------------------------------------------ + private void DisplayErrorOnUIThread(string msg, string packageTitle, Exception e) + { + if (_logBox.IsHandleCreated) + { + WaitCursor.Hide(); + _logBox.WriteError(msg, packageTitle); + if (e != null) + _logBox.WriteException(e); + } + } + + /// ------------------------------------------------------------------------------------ + protected override void OnLoad(EventArgs e) + { + _settings.InitializeForm(this); + base.OnLoad(e); + + Text = Format(Text, _viewModel.AppName, ArchiveTypeForTitleBar ?? ArchiveTypeName); + + UpdateLaunchButtonText(); + + _linkOverview.Text = InformativeText; + _linkOverview.Links.Clear(); + + if (!IsNullOrEmpty(_viewModel.ArchiveInfoUrl) && !IsNullOrEmpty(_archiveInfoHyperlinkText)) + { + int i = _linkOverview.Text.IndexOf(_archiveInfoHyperlinkText, StringComparison.InvariantCulture); + if (i >= 0) + _linkOverview.Links.Add(i, _archiveInfoHyperlinkText.Length, _viewModel.ArchiveInfoUrl); + } + + // this is for a display problem in mono + _linkOverview.SizeToContents(); + } + + /// ------------------------------------------------------------------------------------ + protected override void OnShown(EventArgs e) + { + base.OnShown(e); + Initialize(); + } + + /// ------------------------------------------------------------------------------------ + protected async void Initialize() + { + _cts = new CancellationTokenSource(); + WaitCursor.Show(); + + try + { + _buttonCreatePackage.Enabled = _chkMetadataOnly.Enabled = + await _viewModel.Initialize(this, _cts.Token); + _logBox.ScrollToTop(); + _progressBar.Maximum = _viewModel.CalculateMaxProgressBarValue(); + } + catch (OperationCanceledException) + { + CompleteCancellation(); + } + catch (Exception ex) + { + ErrorReport.ReportNonFatalException(ex); + Close(); + } + finally + { + if (InvokeRequired) + Invoke(new Action (ResetUIForUserInteraction)); + else + ResetUIForUserInteraction(); + + try + { + _cts.Dispose(); + } + catch (Exception e) + { + Logger.WriteError(e); + } + _cts = null; + } + } + + /// ------------------------------------------------------------------------------------ + private void HandleRampLinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + var tgt = e.Link.LinkData as string; + + if (!IsNullOrEmpty(tgt)) + { + var ps = new ProcessStartInfo(tgt) + { + UseShellExecute = true, + Verb = "open" + }; + Process.Start(ps); + } + } + + /// ------------------------------------------------------------------------------------ + private void HandleLogBoxReportErrorLinkClicked(object sender, EventArgs e) + { + Close(); + } + + private void _chkMetadataOnly_CheckedChanged(object sender, EventArgs e) + { + ((ISupportMetadataOnly)_viewModel).MetadataOnly = _chkMetadataOnly.Checked; + } + } +} diff --git a/SIL.Archiving/ArchivingDlg.resx b/SIL.Windows.Forms.Archiving/ArchivingDlg.resx old mode 100755 new mode 100644 similarity index 96% rename from SIL.Archiving/ArchivingDlg.resx rename to SIL.Windows.Forms.Archiving/ArchivingDlg.resx index b0ea264f8..3c8fb332c --- a/SIL.Archiving/ArchivingDlg.resx +++ b/SIL.Windows.Forms.Archiving/ArchivingDlg.resx @@ -120,9 +120,6 @@ 17, 17 - - 17, 17 - 42 diff --git a/SIL.Archiving/IMDI/IMDIArchivingDlg.cs b/SIL.Windows.Forms.Archiving/IMDI/IMDIArchivingDlg.cs similarity index 57% rename from SIL.Archiving/IMDI/IMDIArchivingDlg.cs rename to SIL.Windows.Forms.Archiving/IMDI/IMDIArchivingDlg.cs index 8b21e951b..4ce45d64f 100644 --- a/SIL.Archiving/IMDI/IMDIArchivingDlg.cs +++ b/SIL.Windows.Forms.Archiving/IMDI/IMDIArchivingDlg.cs @@ -3,12 +3,17 @@ using System.IO; using System.Linq; using System.Windows.Forms; +using JetBrains.Annotations; using L10NSharp; +using SIL.Archiving; +using SIL.Archiving.IMDI; using SIL.Windows.Forms.PortableSettingsProvider; +// ReSharper disable InconsistentNaming -namespace SIL.Archiving.IMDI +namespace SIL.Windows.Forms.Archiving.IMDI { /// + [PublicAPI] public class IMDIArchivingDlg : ArchivingDlg { private TableLayoutPanel _destinationFolderTable; @@ -19,22 +24,44 @@ public class IMDIArchivingDlg : ArchivingDlg private LinkLabel _browseIMDIProgram; private ComboBox _selectIMDIPreset; - /// - public IMDIArchivingDlg(ArchivingDlgViewModel model, string localizationManagerId, Font programDialogFont, FormSettings settings) - : base(model, localizationManagerId, programDialogFont, settings) + /// ------------------------------------------------------------------------------------ + /// View model + /// Application can use this to pass + /// additional information that will be displayed to the user in the dialog to explain + /// any application-specific details about the archival process. + /// The ID of the localization manager for the + /// calling application. + /// Application can set this to ensure a consistent look + /// in the UI (especially useful for when a localization requires a particular font). + /// Location, size, and state where the client would like the + /// dialog box to appear (can be null) + /// ------------------------------------------------------------------------------------ + public IMDIArchivingDlg(IMDIArchivingDlgViewModel model, + string appSpecificArchivalProcessInfo, string localizationManagerId = null, + Font programDialogFont = null, FormSettings settings = null) + : base(model, appSpecificArchivalProcessInfo, localizationManagerId, + programDialogFont, settings, LocalizationManager.GetString( + "DialogBoxes.ArchivingDlg.IsleMetadataInitiative", "Isle Metadata Initiative", + "Typically this probably does not need to be localized.")) { // DO NOT SHOW THE LAUNCH OPTION AT THIS TIME model.PathToProgramToLaunch = null; + model.InitializationFailed += Model_InitializationFailed; InitializeNewControls(); // get the saved IMDI program value - GetSavedValues(); + model.GetSavedValues(); // set control properties SetControlProperties(); } + private void Model_InitializationFailed(object sender, EventArgs e) + { + _browseDestinationFolder.Enabled = false; + } + private void InitializeNewControls() { AddDestinationFolder(); @@ -80,12 +107,97 @@ private void AddDestinationFolder() _flowLayoutExtra.Controls.Add(_destinationFolderTable); } + public override string ArchiveTypeName => + LocalizationManager.GetString("DialogBoxes.ArchivingDlg.IMDIArchiveType", "IMDI", + "This is the abbreviation for Isle Metadata Initiative (https://www.mpi.nl/imdi/). " + + "Typically this probably does not need to be localized."); + + /// ------------------------------------------------------------------------------------ + protected override string InformativeText + { + get + { + string programInfo = string.IsNullOrEmpty(_viewModel.NameOfProgramToLaunch) ? + string.Format(LocalizationManager.GetString( + "DialogBoxes.ArchivingDlg.NoIMDIProgramInfoText", + "The {0} package will be created in {1}.", + "Parameter 0 is 'IMDI'; " + + "Parameter 1 is the path where the package is created."), + _viewModel.ArchiveType, _viewModel.PackagePath) + : + string.Format(LocalizationManager.GetString( + "DialogBoxes.ArchivingDlg.IMDIProgramInfoText", + "This tool will help you use {0} to archive your {1} data. When the {1} package has been " + + "created, you can launch {0} and enter any additional information before doing the actual submission.", + "Parameter 0 is the name of the program that will be launched to further prepare the IMDI data for submission; " + + "Parameter 1 is the name of the calling (host) program (SayMore, FLEx, etc.)"), + _viewModel.NameOfProgramToLaunch, _viewModel.AppName); + return string.Format(LocalizationManager.GetString( + "DialogBoxes.ArchivingDlg.IMDIOverviewText", + "{0} ({1}) is a metadata standard to describe multi-media and multi-modal language " + + "resources. The standard provides interoperability for browsable and searchable " + + "corpus structures and resource descriptions.", + "Parameter 0 is 'Isle Metadata Initiative' (the first occurrence will be turned into a hyperlink); " + + "Parameter 1 is 'IMDI'"), + _archiveInfoHyperlinkText, _viewModel.ArchiveType) + + " " + _appSpecificArchivalProcessInfo + + " " + programInfo; + } + } + protected override void DisableControlsDuringPackageCreation() { base.DisableControlsDuringPackageCreation(); _destinationFolderTable.Visible = false; } + protected override void PackageCreationComplete(string result) + { + base.PackageCreationComplete(result); + + var mainExportFile = result; + + if (mainExportFile != null) + { + void PutIMDIPackagePathOnClipboard() + { + // copy the path to the imdi file to the clipboard + + // SP-818: Crash in IMDI export when dialog tries to put string on clipboard + // 18 FEB 2014, Phil Hopper: I found this possible solution using retries on StackOverflow + // https://stackoverflow.com/questions/5707990/requested-clipboard-operation-did-not-succeed + //Clipboard.SetData(DataFormats.Text, _imdiData.MainExportFile); + Clipboard.SetDataObject(mainExportFile, true, 3, 500); + } + + if (InvokeRequired) + Invoke(new Action(PutIMDIPackagePathOnClipboard)); + else + PutIMDIPackagePathOnClipboard(); + + var successMsg = LocalizationManager.GetString("DialogBoxes.ArchivingDlg.ReadyToCallIMDIMsg", + "Exported to {0}. This path is now on your clipboard. If you are using Arbil, go to File, Import, then paste this path in."); + DisplayMessage(string.Format(successMsg, mainExportFile), ArchivingDlgViewModel.MessageType.Success); + } + } + + public override string GetMessage(ArchivingDlgViewModel.StringId msgId) + { + switch (msgId) + { + case ArchivingDlgViewModel.StringId.IMDIPackageInvalid: + return LocalizationManager.GetString( + "DialogBoxes.ArchivingDlg.IMDIPackageInvalid", "The IMDI package is invalid.", + "This is displayed in the Archive Using IMDI dialog box if the calling " + + "program fails to initialize the IMDI package with valid settings."); + case ArchivingDlgViewModel.StringId.IMDIActorsGroup: + return LocalizationManager.GetString( + "DialogBoxes.ArchivingDlg.IMDIActorsGroup", "Actors", + "This is the heading displayed in the Archive Using IMDI dialog box for the files for the actors/participants"); + default: return base.GetMessage(msgId); + } + } + void SetDestinationLabelText() { var labelText = ((IMDIArchivingDlgViewModel)_viewModel).OutputFolder; @@ -182,7 +294,6 @@ private void SelectIMDIProgramOnClick(object sender, EventArgs eventArgs) { ((IMDIArchivingDlgViewModel)_viewModel).OtherProgramPath = chooseIMDIProgram.FileName; SetControlProperties(); - } } } @@ -234,7 +345,6 @@ private void SetControlProperties() //UpdateLaunchButtonText(); _buttonLaunchRamp.Visible = false; _tableLayoutPanel.SetColumn(_buttonCreatePackage, 1); - _buttonCancel.Text = LocalizationManager.GetString("DialogBoxes.IMDIArchivingDlg.CloseButtonLabel", "Close"); _buttonCreatePackage.Text = LocalizationManager.GetString("DialogBoxes.IMDIArchivingDlg.CreatePackageButtonLabel", "Create Package"); UpdateOverviewText(); } diff --git a/SIL.Windows.Forms.Archiving/LinkLabelExtensions.cs b/SIL.Windows.Forms.Archiving/LinkLabelExtensions.cs new file mode 100644 index 000000000..5594b55b1 --- /dev/null +++ b/SIL.Windows.Forms.Archiving/LinkLabelExtensions.cs @@ -0,0 +1,83 @@ +using System; +using System.Text; +using System.Windows.Forms; +using SIL.Archiving; + +namespace SIL.Windows.Forms.Archiving +{ + /// ------------------------------------------------------------------------------------ + public static class LinkLabelExtensions + { + /// ------------------------------------------------------------------------------------ + /// + /// Used to size a link label in mono because it is not working at all + /// + /// + /// ------------------------------------------------------------------------------------ + public static void SizeToContents(this LinkLabel linkLabel) + { + var w = linkLabel.ClientSize.Width; + + using (var g = linkLabel.CreateGraphics()) + { + + if (ArchivingDlgViewModel.IsMono) + { + // split at the existing like breaks + var segments = linkLabel.Text.Replace("\r", "").Split(new[] {'\n'}, StringSplitOptions.None); + var newText = new StringBuilder(); + + foreach (var segment in segments) + { + var thisSegment = segment.Trim(); + + while (MeasureText(linkLabel, g, thisSegment, linkLabel.Font).Width > w) + { + var line = string.Empty; + var lastSpace = 0; + + for (var i = 0; i < thisSegment.Length; i++) + { + if (char.IsWhiteSpace(thisSegment[i])) + { + if (MeasureText(linkLabel, g, line, linkLabel.Font).Width > w) + { + newText.AppendLine(thisSegment.Substring(0, lastSpace)); + thisSegment = thisSegment.Substring(lastSpace + 1); + break; + } + + lastSpace = i; + } + line += thisSegment[i]; + } + } + + // check for left-overs + if (thisSegment.Length > 0) + newText.AppendLine(thisSegment); + } + + linkLabel.Text = newText.ToString(); + } + + var size = MeasureText(linkLabel, g, linkLabel.Text, linkLabel.Font, new System.Drawing.Size(w, Int32.MaxValue)); + linkLabel.Height = size.Height; + } + } + private static System.Drawing.Size MeasureText(this LinkLabel linkLabel, System.Drawing.Graphics g, string text, System.Drawing.Font font) + { + if (linkLabel.UseCompatibleTextRendering) + return g.MeasureString(text, font).ToSize(); + else + return TextRenderer.MeasureText(g, text, font); + } + private static System.Drawing.Size MeasureText(this LinkLabel linkLabel, System.Drawing.Graphics g, string text, System.Drawing.Font font, System.Drawing.Size proposedSize) + { + if (linkLabel.UseCompatibleTextRendering) + return g.MeasureString(text, font, proposedSize.Width).ToSize(); + else + return TextRenderer.MeasureText(g, text, font, proposedSize, TextFormatFlags.WordBreak); + } + } +} diff --git a/SIL.Windows.Forms.Archiving/Properties/AssemblyInfo.cs b/SIL.Windows.Forms.Archiving/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..f67f16d01 --- /dev/null +++ b/SIL.Windows.Forms.Archiving/Properties/AssemblyInfo.cs @@ -0,0 +1,15 @@ +using System.Runtime.CompilerServices; +using SIL.Acknowledgements; + +[assembly: InternalsVisibleTo("SIL.Windows.Forms.Archiving.Tests, PublicKey=0024000004800000940000000" + + "6020000002400005253413100040000010001008339b2ae1bf006934f6176dec6ea2a8a7d67383613dcb03d7197" + + "5e7b05ad546562c84529a4811e94c889e55f2532d1a90baaf20be9bff39ac6f5365bd605d70b90489840b7ba6d1" + + "c231b0e550c4abe4f60553856ef142a40a91e53d56e79f69dc79c4e95817de498aac924ee011f03b4e1c1d772d5" + + "1c4946c1185e3bfb621bc6")] + +[assembly: Acknowledgement("DotNetZip", Copyright = "Henrik Feldt/Dino Chiesa", Url = "https://github.com/haf/DotNetZip.Semverd", + LicenseUrl = "https://raw.githubusercontent.com/haf/DotNetZip.Semverd/master/LICENSE", Location = "./DotNetZip.dll", + Html = "
  • DotNetZip © Henrik Feldt/Dino Chiesa 2006-2018 (Multiple) - a library for handling zip archives
  • ")] +[assembly: Acknowledgement("L10NSharp", Url = "https://github.com/sillsdev/l10nsharp/", + Copyright = "Copyright © SIL International 2010-2017", LicenseUrl = "https://opensource.org/licenses/MIT", + Location = "./L10NSharp.dll")] \ No newline at end of file diff --git a/SIL.Windows.Forms.Archiving/Properties/Resources.Designer.cs b/SIL.Windows.Forms.Archiving/Properties/Resources.Designer.cs new file mode 100644 index 000000000..f4ff45ed2 --- /dev/null +++ b/SIL.Windows.Forms.Archiving/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace SIL.Windows.Forms.Archiving.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SIL.Windows.Forms.Archiving.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/SIL.Archiving/Properties/Resources.resx b/SIL.Windows.Forms.Archiving/Properties/Resources.resx old mode 100755 new mode 100644 similarity index 72% rename from SIL.Archiving/Properties/Resources.resx rename to SIL.Windows.Forms.Archiving/Properties/Resources.resx index 3b5dad038..267ed3e43 --- a/SIL.Archiving/Properties/Resources.resx +++ b/SIL.Windows.Forms.Archiving/Properties/Resources.resx @@ -1,4 +1,4 @@ - + - -
    - + +
    +
    - - - + + + @@ -26,4 +26,12 @@ - + + + + + + + + + \ No newline at end of file diff --git a/SIL.Windows.Forms.Tests/ClearShare/ClearShareUsage.cs b/SIL.Windows.Forms.Tests/ClearShare/ClearShareUsage.cs index d80351fe1..df376a3ec 100644 --- a/SIL.Windows.Forms.Tests/ClearShare/ClearShareUsage.cs +++ b/SIL.Windows.Forms.Tests/ClearShare/ClearShareUsage.cs @@ -1,6 +1,7 @@ -using System.Linq; +using System.Linq; using System.Xml; using NUnit.Framework; +using SIL.Core.ClearShare; using SIL.IO; using SIL.Windows.Forms.ClearShare; diff --git a/SIL.Windows.Forms.Tests/ClearShare/ContributorsListControlViewModelTests.cs b/SIL.Windows.Forms.Tests/ClearShare/ContributorsListControlViewModelTests.cs index 6a14cab8d..f72edac5e 100644 --- a/SIL.Windows.Forms.Tests/ClearShare/ContributorsListControlViewModelTests.cs +++ b/SIL.Windows.Forms.Tests/ClearShare/ContributorsListControlViewModelTests.cs @@ -1,6 +1,7 @@ using System.Linq; using Moq; using NUnit.Framework; +using SIL.Core.ClearShare; using SIL.Windows.Forms.ClearShare; namespace SIL.Windows.Forms.Tests.ClearShare diff --git a/SIL.Windows.Forms.Tests/ClearShare/LicenseTests.cs b/SIL.Windows.Forms.Tests/ClearShare/LicenseInfoTests.cs similarity index 63% rename from SIL.Windows.Forms.Tests/ClearShare/LicenseTests.cs rename to SIL.Windows.Forms.Tests/ClearShare/LicenseInfoTests.cs index c5f644988..bbde984ee 100644 --- a/SIL.Windows.Forms.Tests/ClearShare/LicenseTests.cs +++ b/SIL.Windows.Forms.Tests/ClearShare/LicenseInfoTests.cs @@ -1,71 +1,12 @@ -using NUnit.Framework; +using NUnit.Framework; using SIL.Windows.Forms.ClearShare; namespace SIL.Windows.Forms.Tests.ClearShare { /// ---------------------------------------------------------------------------------------- [TestFixture] - public class LicenseTests + public class LicenseInfoTests { - /// ------------------------------------------------------------------------------------ - [Test] - public void AreContentsEqual_OtherIsNull_ReturnsFalse() - { - var l = License.CreativeCommons_Attribution_ShareAlike; - Assert.IsFalse(l.AreContentsEqual(null)); - } - - /// ------------------------------------------------------------------------------------ - [Test] - public void AreContentsEqual_AreDifferent_ReturnsFalse() - { - var l1 = License.CreativeCommons_Attribution_ShareAlike; - var l2 = License.CreativeCommons_Attribution; - Assert.IsFalse(l1.AreContentsEqual(l2)); - } - - /// ------------------------------------------------------------------------------------ - [Test] - public void AreContentsEqual_AreSame_ReturnsTrue() - { - var l1 = License.CreativeCommons_Attribution_ShareAlike; - var l2 = License.CreativeCommons_Attribution_ShareAlike; - Assert.IsTrue(l1.AreContentsEqual(l2)); - } - - /// ------------------------------------------------------------------------------------ - [Test] - public void Equals_SameInstance_ReturnsTrue() - { - var l = License.CreativeCommons_Attribution_ShareAlike; - Assert.IsTrue(l.Equals(l)); - } - - /// ------------------------------------------------------------------------------------ - [Test] - public void Equals_CompareToNull_ReturnsFalse() - { - var l = License.CreativeCommons_Attribution_ShareAlike; - Assert.IsFalse(l.Equals(null)); - } - - /// ------------------------------------------------------------------------------------ - [Test] - public void Equals_CompareToObjOfDifferentType_ReturnsFalse() - { - var l = License.CreativeCommons_Attribution_ShareAlike; - Assert.IsFalse(l.Equals("junk")); - } - - /// ------------------------------------------------------------------------------------ - [Test] - public void Equals_AreSame_ReturnsTrue() - { - var l1 = License.CreativeCommons_Attribution_ShareAlike; - var l2 = License.CreativeCommons_Attribution_ShareAlike; - Assert.IsTrue(l1.Equals(l2)); - } - [Test] public void FromToken_CreativeCommons_GiveExpectedAttributes() { diff --git a/SIL.Windows.Forms.Tests/ClearShare/MetaDataDisplayTests.cs b/SIL.Windows.Forms.Tests/ClearShare/MetaDataDisplayTests.cs index 97b0b4a9e..cb675ef87 100644 --- a/SIL.Windows.Forms.Tests/ClearShare/MetaDataDisplayTests.cs +++ b/SIL.Windows.Forms.Tests/ClearShare/MetaDataDisplayTests.cs @@ -1,5 +1,6 @@ -using System.Windows.Forms; +using System.Windows.Forms; using NUnit.Framework; +using SIL.Core.ClearShare; using SIL.Windows.Forms.ClearShare; using SIL.Windows.Forms.ClearShare.WinFormsUI; diff --git a/SIL.Windows.Forms.Tests/ClearShare/MetadataEditorControlTests.cs b/SIL.Windows.Forms.Tests/ClearShare/MetadataEditorControlTests.cs index 3500e2ce0..21464cf20 100644 --- a/SIL.Windows.Forms.Tests/ClearShare/MetadataEditorControlTests.cs +++ b/SIL.Windows.Forms.Tests/ClearShare/MetadataEditorControlTests.cs @@ -1,5 +1,6 @@ using System.Windows.Forms; using NUnit.Framework; +using SIL.Core.ClearShare; using SIL.Windows.Forms.ClearShare; using SIL.Windows.Forms.ClearShare.WinFormsUI; diff --git a/SIL.Windows.Forms.Tests/ClearShare/MetadataTests.cs b/SIL.Windows.Forms.Tests/ClearShare/MetadataTests.cs index 0a66ba658..775abc35c 100644 --- a/SIL.Windows.Forms.Tests/ClearShare/MetadataTests.cs +++ b/SIL.Windows.Forms.Tests/ClearShare/MetadataTests.cs @@ -538,7 +538,7 @@ public void ChangingFromCCLicense_WorksOkay() private void VerifyMetadataUnchangedSavingToTag(Metadata oldMetadata, XmpTag tag, string header) { // XmpTag objects are wretched to work with, so load it into another Metadata object for testing. - // This way we test both SaveInImageTag and LoadProperties for roundtripping. + // This way we test both SaveInImageTag and LoadProperties for round-tripping. oldMetadata.SaveInImageTag(tag); var newMetadata = new Metadata(); Metadata.LoadProperties(tag, newMetadata); diff --git a/SIL.Windows.Forms.WritingSystems.Tests/App.config b/SIL.Windows.Forms.WritingSystems.Tests/App.config new file mode 100644 index 000000000..e6c577f71 --- /dev/null +++ b/SIL.Windows.Forms.WritingSystems.Tests/App.config @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/SIL.Windows.Forms/ClearShare/ContributorsListControlViewModel.cs b/SIL.Windows.Forms/ClearShare/ContributorsListControlViewModel.cs index 12c94aff7..f556a0422 100644 --- a/SIL.Windows.Forms/ClearShare/ContributorsListControlViewModel.cs +++ b/SIL.Windows.Forms/ClearShare/ContributorsListControlViewModel.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Windows.Forms; +using SIL.Core.ClearShare; using SIL.Windows.Forms.Widgets.BetterGrid; namespace SIL.Windows.Forms.ClearShare diff --git a/SIL.Windows.Forms/ClearShare/CreativeCommonsLicense.cs b/SIL.Windows.Forms/ClearShare/CreativeCommonsLicense.cs index e80c5ba17..1f47484f2 100644 --- a/SIL.Windows.Forms/ClearShare/CreativeCommonsLicense.cs +++ b/SIL.Windows.Forms/ClearShare/CreativeCommonsLicense.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Drawing; using System.Globalization; -using NDesk.DBus.Introspection; namespace SIL.Windows.Forms.ClearShare { @@ -363,13 +362,6 @@ public string Version private string _qualifier = null; - [Obsolete("Use IntergovernmentalOrganizationQualifier")] - public bool IntergovernmentalOriganizationQualifier - { - get => IntergovernmentalOrganizationQualifier; - set => IntergovernmentalOrganizationQualifier = value; - } - /// /// CC 3.0 licenses were ported to better serve the needs of Intergovernmental Organizations (IGOs). /// CC 4.0 does not include a separate IGO suite (at least 2016-2023); presumably, stock CC 4.0 licenses should serve IGOs well as is. diff --git a/SIL.Windows.Forms/ClearShare/LicenseInfo.cs b/SIL.Windows.Forms/ClearShare/LicenseInfo.cs index 45526c213..4cedfc2ed 100644 --- a/SIL.Windows.Forms/ClearShare/LicenseInfo.cs +++ b/SIL.Windows.Forms/ClearShare/LicenseInfo.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Drawing; +using JetBrains.Annotations; using L10NSharp; namespace SIL.Windows.Forms.ClearShare @@ -10,14 +11,11 @@ public abstract class LicenseInfo { public static LicenseInfo FromXmp(Dictionary properties) { - if(properties.ContainsKey("license") && properties["license"].Contains("creativecommons")) - { + if (properties.ContainsKey("license") && properties["license"].Contains("creativecommons")) return CreativeCommonsLicense.FromMetadata(properties); - } - else if ( properties.ContainsKey("rights (en)")) - { + + if ( properties.ContainsKey("rights (en)")) return CustomLicense.FromMetadata(properties); - } return new NullLicense(); } @@ -57,6 +55,7 @@ public virtual Image GetImage() /// REVIEW: How do we know whether this is a well-known license? Presently, only is always well-known. /// REVIEW (Hasso) 2023.07: This is never used (internally, at least) and all overriding implementations return false, too. ///
    + [PublicAPI] public virtual bool EditingAllowed => false; public abstract string Url { get; set; } @@ -117,11 +116,9 @@ public override string GetDescription(IEnumerable languagePriorityIds, o return GetBestLicenseTranslation("NullLicense", englishText, comment, languagePriorityIds, out idOfLanguageUsed); } - public override string Token - { + public override string Token => //do not think of changing this, there is data out there that could get messed up - get { return "ask"; } - } + "ask"; public override string GetMinimalFormForCredits(IEnumerable languagePriorityIds, out string idOfLanguageUsed) { @@ -136,7 +133,7 @@ public override string ToString() public override string Url { - get { return ""; } + get => ""; set { } } } @@ -175,17 +172,15 @@ public override string GetDescription(IEnumerable languagePriorityIds, o } //We don't actually have a way of knowing what language this is, so we use "und", from http://www.loc.gov/standards/iso639-2/faq.html#25 - //I hearby coin "Zook's First Law": Eventually any string entered by a user will wish it had been tagged with a language identifier + //I hereby coin "Zook's First Law": Eventually any string entered by a user will wish it had been tagged with a language identifier //"Zook's Second Law" can be: Eventually any string entered by a user will wish it was a multi-string (multiple (language,value) pairs) idOfLanguageUsed = "und"; return RightsStatement; } - public override string Token - { + public override string Token => //do not think of changing this, there is data out there that could get messed up - get { return "custom"; } - } + "custom"; public override Image GetImage() { diff --git a/SIL.Windows.Forms/ClearShare/LicenseWithLogo.cs b/SIL.Windows.Forms/ClearShare/LicenseWithLogo.cs new file mode 100644 index 000000000..c1b020ff9 --- /dev/null +++ b/SIL.Windows.Forms/ClearShare/LicenseWithLogo.cs @@ -0,0 +1,39 @@ +using System.Drawing; +using SIL.Core.ClearShare; + +namespace SIL.Windows.Forms.ClearShare +{ + /// + /// Describes a single license, under which many works can be licensed for use + /// + public class LicenseWithLogo : License + { + public Image Logo { get; private set; } + + /// ------------------------------------------------------------------------------------ + public override int GetHashCode() + { + unchecked + { + int result = base.GetHashCode(); + result = (result * 397) ^ (Logo != null ? Logo.GetHashCode() : 0); + return result; + } + } + + /// ------------------------------------------------------------------------------------ + public override bool Equals(object other) + { + if (other == null) + return false; + + if (ReferenceEquals(this, other)) + return true; + + if (GetType() != other.GetType()) + return false; + + return AreContentsEqual(other as License); + } + } +} diff --git a/SIL.Windows.Forms/ClearShare/Metadata.cs b/SIL.Windows.Forms/ClearShare/Metadata.cs index 25361c16f..90ecc8f71 100644 --- a/SIL.Windows.Forms/ClearShare/Metadata.cs +++ b/SIL.Windows.Forms/ClearShare/Metadata.cs @@ -1,12 +1,13 @@ -// Copyright (c) 2018-2023 SIL International +// Copyright (c) 2018-2024 SIL International // This software is licensed under the MIT License (http://opensource.org/licenses/MIT) + using System; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Text; using System.Text.RegularExpressions; -using L10NSharp; +using JetBrains.Annotations; using SIL.Code; using SIL.Extensions; using SIL.IO; @@ -14,7 +15,6 @@ using TagLib.IFD; using TagLib.Image; using TagLib.Xmp; -using File = System.IO.File; namespace SIL.Windows.Forms.ClearShare { @@ -774,6 +774,7 @@ public static bool HaveStoredExemplar(FileCategory category) /// Deletes the stored exemplar (if it exists). This can be useful if a program /// wants to establish "CC BY" as the default license for a new product. ///
    + [PublicAPI] public static void DeleteStoredExemplar(FileCategory category) { var path = GetExemplarPath(category); @@ -788,11 +789,12 @@ public static void DeleteStoredExemplar(FileCategory category) /// The summary will be in the first language available. /// /// - public string GetSummaryParagraph(IEnumerable languagePriorityIds, out string idOfLanguageUsed) + [PublicAPI] + public string GetSummaryParagraph(IEnumerable languagePriorityIds, out string idOfLanguageUsed, string localizedCreatorLabel = "Creator") { var b = new StringBuilder(); - string creatorLabel = LocalizationManager.GetString("MetadataDisplay.CreatorLabel", "Creator"); - b.AppendLine(creatorLabel+": " + Creator); + b.Append(localizedCreatorLabel).Append(": ").AppendLine(Creator); + b.AppendLine($"{localizedCreatorLabel}: {Creator}"); b.AppendLine(CopyrightNotice); if(!string.IsNullOrEmpty(CollectionName)) b.AppendLine(CollectionName); diff --git a/SIL.Windows.Forms/ClearShare/WinFormsUI/ContributorsListControl.cs b/SIL.Windows.Forms/ClearShare/WinFormsUI/ContributorsListControl.cs index bc2e95122..4e65e78a7 100644 --- a/SIL.Windows.Forms/ClearShare/WinFormsUI/ContributorsListControl.cs +++ b/SIL.Windows.Forms/ClearShare/WinFormsUI/ContributorsListControl.cs @@ -9,6 +9,7 @@ using JetBrains.Annotations; using L10NSharp.UI; using SIL.Code; +using SIL.Core.ClearShare; using SIL.Windows.Forms.Widgets.BetterGrid; namespace SIL.Windows.Forms.ClearShare.WinFormsUI diff --git a/SIL.Windows.Forms/ClearShare/WinFormsUI/MetadataDisplayControl.cs b/SIL.Windows.Forms/ClearShare/WinFormsUI/MetadataDisplayControl.cs index 607685342..c23499e24 100644 --- a/SIL.Windows.Forms/ClearShare/WinFormsUI/MetadataDisplayControl.cs +++ b/SIL.Windows.Forms/ClearShare/WinFormsUI/MetadataDisplayControl.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using System.Windows.Forms; using L10NSharp; +using SIL.Core.ClearShare; using SIL.Windows.Forms.Widgets; namespace SIL.Windows.Forms.ClearShare.WinFormsUI diff --git a/SIL.Windows.Forms/ClearShare/WinFormsUI/MetadataEditorControl.cs b/SIL.Windows.Forms/ClearShare/WinFormsUI/MetadataEditorControl.cs index 29cd816a2..4387a0753 100644 --- a/SIL.Windows.Forms/ClearShare/WinFormsUI/MetadataEditorControl.cs +++ b/SIL.Windows.Forms/ClearShare/WinFormsUI/MetadataEditorControl.cs @@ -3,6 +3,7 @@ using System.Drawing.Drawing2D; using System.Windows.Forms; using L10NSharp; +using SIL.Core.ClearShare; namespace SIL.Windows.Forms.ClearShare.WinFormsUI { diff --git a/SIL.Windows.Forms/ClearShare/WinFormsUI/MetadataEditorDialog.cs b/SIL.Windows.Forms/ClearShare/WinFormsUI/MetadataEditorDialog.cs index 9c8e915bd..4f3aee307 100644 --- a/SIL.Windows.Forms/ClearShare/WinFormsUI/MetadataEditorDialog.cs +++ b/SIL.Windows.Forms/ClearShare/WinFormsUI/MetadataEditorDialog.cs @@ -1,6 +1,7 @@ using System; using System.Windows.Forms; using L10NSharp; +using SIL.Core.ClearShare; namespace SIL.Windows.Forms.ClearShare.WinFormsUI { diff --git a/SIL.Windows.Forms/ImageToolbox/ImageToolboxControl.cs b/SIL.Windows.Forms/ImageToolbox/ImageToolboxControl.cs index 8a9584844..64a6b976f 100644 --- a/SIL.Windows.Forms/ImageToolbox/ImageToolboxControl.cs +++ b/SIL.Windows.Forms/ImageToolbox/ImageToolboxControl.cs @@ -4,6 +4,7 @@ using System.Windows.Forms; using L10NSharp; using SIL.Code; +using SIL.Core.ClearShare; using SIL.PlatformUtilities; using SIL.Reporting; using SIL.Windows.Forms.ClearShare; diff --git a/SIL.Windows.Forms/ImageToolbox/ImageToolboxDialog.Designer.cs b/SIL.Windows.Forms/ImageToolbox/ImageToolboxDialog.Designer.cs index e93de8bf0..40d4e8510 100644 --- a/SIL.Windows.Forms/ImageToolbox/ImageToolboxDialog.Designer.cs +++ b/SIL.Windows.Forms/ImageToolbox/ImageToolboxDialog.Designer.cs @@ -1,4 +1,5 @@ -using SIL.Windows.Forms.ClearShare; +using SIL.Core.ClearShare; +using SIL.Windows.Forms.ClearShare; namespace SIL.Windows.Forms.ImageToolbox { diff --git a/SIL.Windows.Forms/ImageToolbox/ImageToolboxDialog.cs b/SIL.Windows.Forms/ImageToolbox/ImageToolboxDialog.cs index 60aa3fbe7..9c74640b9 100644 --- a/SIL.Windows.Forms/ImageToolbox/ImageToolboxDialog.cs +++ b/SIL.Windows.Forms/ImageToolbox/ImageToolboxDialog.cs @@ -1,5 +1,6 @@ using System; using System.Windows.Forms; +using SIL.Core.ClearShare; using SIL.Reporting; using SIL.Windows.Forms.ClearShare; diff --git a/SIL.Windows.Forms/ImageToolbox/PalasoImage.cs b/SIL.Windows.Forms/ImageToolbox/PalasoImage.cs index fdc7e3dae..39ccb2f11 100644 --- a/SIL.Windows.Forms/ImageToolbox/PalasoImage.cs +++ b/SIL.Windows.Forms/ImageToolbox/PalasoImage.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Text; using SIL.Code; +using SIL.Core.ClearShare; using SIL.IO; using SIL.Windows.Forms.ClearShare; diff --git a/SIL.Windows.Forms/Properties/Resources.Designer.cs b/SIL.Windows.Forms/Properties/Resources.Designer.cs index 14e679812..b320d2994 100644 --- a/SIL.Windows.Forms/Properties/Resources.Designer.cs +++ b/SIL.Windows.Forms/Properties/Resources.Designer.cs @@ -130,27 +130,6 @@ internal static System.Drawing.Bitmap MenuButtonArrow { } } - /// - /// Looks up a localized string similar to <?xml version="1.0"?> - ///<OLAC_doc> - /// <header> - /// <status code="adopted" type="recommendation"/> - /// <!-- Promoted to Adopted on 2008-12-19 --> - /// <title>OLAC Role Vocabulary</title> - /// <baseName>role</baseName> - /// <issued>20060406</issued> - /// <previousIssued>20031010</previousIssued> - /// <abstract> - /// <p>Role is an attribute of both the Creator and Contributor elements. (Please note that Dublin - /// Core now discourages the use of the Creator element, recommending that all Role information be - /// associated with C [rest of string was truncated]";. - /// - internal static string OlacRoles { - get { - return ResourceManager.GetString("OlacRoles", resourceCulture); - } - } - /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// diff --git a/SIL.Windows.Forms/Properties/Resources.resx b/SIL.Windows.Forms/Properties/Resources.resx index f7f3cd1de..a63ad0f42 100644 --- a/SIL.Windows.Forms/Properties/Resources.resx +++ b/SIL.Windows.Forms/Properties/Resources.resx @@ -124,9 +124,6 @@ ..\Resources\RecycleBin.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - ..\Resources\OlacRoles.xml;System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 - ..\Resources\RemoveGridRowNormal.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a diff --git a/SIL.WritingSystems.Tests/WritingSystemOrphanFinderTests.cs b/SIL.WritingSystems.Tests/WritingSystemOrphanFinderTests.cs index 983279792..8ed28fb1c 100644 --- a/SIL.WritingSystems.Tests/WritingSystemOrphanFinderTests.cs +++ b/SIL.WritingSystems.Tests/WritingSystemOrphanFinderTests.cs @@ -80,7 +80,7 @@ public void FindOrphans_NoOrphansFound_WritingSystemRepoAndFileUntouched() } [Test] - public void FindOrphans_OrphanFoundIsValidRfcTag_WritingsystemIsAddedToWritingSystemRepoAndFileUntouched() + public void FindOrphans_OrphanFoundIsValidRfcTag_WritingSystemIsAddedToWritingSystemRepoAndFileUntouched() { using (var e = new TestEnvironment("en", "de")) { diff --git a/SIL.WritingSystems/IetfLanguageTag.cs b/SIL.WritingSystems/IetfLanguageTag.cs index b2f984fe1..d687d1eb4 100644 --- a/SIL.WritingSystems/IetfLanguageTag.cs +++ b/SIL.WritingSystems/IetfLanguageTag.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Globalization; using System.Linq; using System.Text; diff --git a/TestApps/ArchivingTestApp/ArchivingTestApp.csproj b/TestApps/ArchivingTestApp/ArchivingTestApp.csproj new file mode 100644 index 000000000..a870d4a17 --- /dev/null +++ b/TestApps/ArchivingTestApp/ArchivingTestApp.csproj @@ -0,0 +1,23 @@ + + + + WinExe + net8.0-windows + enable + true + enable + latest + ArchivingTestApp.Program + + + + + + + + + + + + + \ No newline at end of file diff --git a/TestApps/ArchivingTestApp/MainForm.Designer.cs b/TestApps/ArchivingTestApp/MainForm.Designer.cs new file mode 100644 index 000000000..e2ffae0d3 --- /dev/null +++ b/TestApps/ArchivingTestApp/MainForm.Designer.cs @@ -0,0 +1,169 @@ +namespace ArchivingTestApp +{ + partial class MainForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + m_btnIMDI = new Button(); + m_tableLayoutPanelMain = new TableLayoutPanel(); + m_btnRamp = new Button(); + m_lblTitle = new SIL.Windows.Forms.Widgets.BetterLabel(); + m_txtTitle = new TextBox(); + m_btnAddFiles = new Button(); + m_listFiles = new ListView(); + m_tableLayoutPanelMain.SuspendLayout(); + SuspendLayout(); + // + // m_btnIMDI + // + m_btnIMDI.Anchor = AnchorStyles.Bottom | AnchorStyles.Right; + m_btnIMDI.AutoSize = true; + m_btnIMDI.Enabled = false; + m_btnIMDI.Location = new Point(812, 469); + m_btnIMDI.Margin = new Padding(4, 3, 4, 3); + m_btnIMDI.Name = "m_btnIMDI"; + m_btnIMDI.Size = new Size(99, 29); + m_btnIMDI.TabIndex = 0; + m_btnIMDI.Text = "IMDI Archive"; + m_btnIMDI.UseVisualStyleBackColor = true; + m_btnIMDI.Click += m_btnIMDI_Click; + // + // m_tableLayoutPanelMain + // + m_tableLayoutPanelMain.ColumnCount = 3; + m_tableLayoutPanelMain.ColumnStyles.Add(new ColumnStyle()); + m_tableLayoutPanelMain.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F)); + m_tableLayoutPanelMain.ColumnStyles.Add(new ColumnStyle()); + m_tableLayoutPanelMain.Controls.Add(m_btnIMDI, 2, 2); + m_tableLayoutPanelMain.Controls.Add(m_btnRamp, 1, 2); + m_tableLayoutPanelMain.Controls.Add(m_lblTitle, 0, 0); + m_tableLayoutPanelMain.Controls.Add(m_txtTitle, 1, 0); + m_tableLayoutPanelMain.Controls.Add(m_btnAddFiles, 0, 1); + m_tableLayoutPanelMain.Controls.Add(m_listFiles, 1, 1); + m_tableLayoutPanelMain.Dock = DockStyle.Fill; + m_tableLayoutPanelMain.Location = new Point(9, 9); + m_tableLayoutPanelMain.Margin = new Padding(4, 3, 4, 3); + m_tableLayoutPanelMain.Name = "m_tableLayoutPanelMain"; + m_tableLayoutPanelMain.RowCount = 3; + m_tableLayoutPanelMain.RowStyles.Add(new RowStyle()); + m_tableLayoutPanelMain.RowStyles.Add(new RowStyle(SizeType.Percent, 100F)); + m_tableLayoutPanelMain.RowStyles.Add(new RowStyle()); + m_tableLayoutPanelMain.Size = new Size(915, 501); + m_tableLayoutPanelMain.TabIndex = 1; + // + // m_btnRamp + // + m_btnRamp.Anchor = AnchorStyles.Bottom | AnchorStyles.Right; + m_btnRamp.AutoSize = true; + m_btnRamp.Enabled = false; + m_btnRamp.Location = new Point(702, 471); + m_btnRamp.Margin = new Padding(4, 3, 4, 3); + m_btnRamp.Name = "m_btnRamp"; + m_btnRamp.Padding = new Padding(0, 0, 9, 0); + m_btnRamp.Size = new Size(102, 27); + m_btnRamp.TabIndex = 1; + m_btnRamp.Text = "RAMP Archive"; + m_btnRamp.UseVisualStyleBackColor = true; + m_btnRamp.Click += m_btnRamp_Click; + // + // m_lblTitle + // + m_lblTitle.BorderStyle = BorderStyle.None; + m_lblTitle.Enabled = false; + m_lblTitle.ForeColor = SystemColors.ControlText; + m_lblTitle.IsTextSelectable = false; + m_lblTitle.Location = new Point(4, 3); + m_lblTitle.Margin = new Padding(4, 3, 4, 3); + m_lblTitle.Multiline = true; + m_lblTitle.Name = "m_lblTitle"; + m_lblTitle.ReadOnly = true; + m_lblTitle.Size = new Size(117, 15); + m_lblTitle.TabIndex = 2; + m_lblTitle.TabStop = false; + m_lblTitle.Text = "Title of Submission:"; + // + // m_txtTitle + // + m_txtTitle.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right; + m_txtTitle.Location = new Point(129, 3); + m_txtTitle.Margin = new Padding(4, 3, 4, 3); + m_txtTitle.Name = "m_txtTitle"; + m_txtTitle.Size = new Size(675, 23); + m_txtTitle.TabIndex = 3; + // + // m_btnAddFiles + // + m_btnAddFiles.Anchor = AnchorStyles.Top; + m_btnAddFiles.AutoSize = true; + m_btnAddFiles.Location = new Point(25, 32); + m_btnAddFiles.Name = "m_btnAddFiles"; + m_btnAddFiles.Size = new Size(75, 25); + m_btnAddFiles.TabIndex = 4; + m_btnAddFiles.Text = "Add Files"; + m_btnAddFiles.UseVisualStyleBackColor = true; + m_btnAddFiles.Click += HandleAddFilesClick; + // + // m_listFiles + // + m_listFiles.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right; + m_listFiles.HeaderStyle = ColumnHeaderStyle.Nonclickable; + m_listFiles.HideSelection = true; + m_listFiles.LabelWrap = false; + m_listFiles.Location = new Point(128, 32); + m_listFiles.MultiSelect = false; + m_listFiles.Name = "m_listFiles"; + m_listFiles.Size = new Size(677, 431); + m_listFiles.TabIndex = 5; + m_listFiles.UseCompatibleStateImageBehavior = false; + m_listFiles.View = View.List; + // + // MainForm + // + AutoScaleDimensions = new SizeF(7F, 15F); + AutoScaleMode = AutoScaleMode.Font; + ClientSize = new Size(933, 519); + Controls.Add(m_tableLayoutPanelMain); + Margin = new Padding(4, 3, 4, 3); + Name = "MainForm"; + Padding = new Padding(9); + Text = "Archiving Dialog Test app"; + m_tableLayoutPanelMain.ResumeLayout(false); + m_tableLayoutPanelMain.PerformLayout(); + ResumeLayout(false); + } + + #endregion + + private Button m_btnIMDI; + private TableLayoutPanel m_tableLayoutPanelMain; + private Button m_btnRamp; + private SIL.Windows.Forms.Widgets.BetterLabel m_lblTitle; + private TextBox m_txtTitle; + private Button m_btnAddFiles; + private ListView m_listFiles; + } +} diff --git a/TestApps/ArchivingTestApp/MainForm.cs b/TestApps/ArchivingTestApp/MainForm.cs new file mode 100644 index 000000000..f8bcff64c --- /dev/null +++ b/TestApps/ArchivingTestApp/MainForm.cs @@ -0,0 +1,130 @@ +using L10NSharp; +using SIL.Archiving; +using SIL.Archiving.Generic; +using SIL.Archiving.IMDI; +using SIL.IO; +using SIL.Windows.Forms.Archiving; +using SIL.Windows.Forms.Archiving.IMDI; +using static System.IO.Path; +using static System.String; + +namespace ArchivingTestApp +{ + public partial class MainForm : Form + { + private const string kAppName = "Archiving Test App"; + + private string GetTitle() + { + var title = m_txtTitle.Text; + if (title.Length == 0) + title = "Arbitrary title"; + return title; + } + + public MainForm() + { + InitializeComponent(); + } + + private void m_btnRamp_Click(object sender, EventArgs e) + { + var title = GetTitle(); + var model = new RampArchivingDlgViewModel(kAppName, title, + title.ToLatinOnly("~", "_", ""), SetFilesToArchive, GetFileDescription); + using (var rampArchiveDlg = new ArchivingDlg(model, LocalizationManager.GetString( + "ArchivingTestApp.MainForm.AdditionalArchiveProcessInfo", "This is just a test."))) + { + rampArchiveDlg.ShowDialog(this); + } + } + + private void m_btnIMDI_Click(object sender, EventArgs e) + { + var title = GetTitle(); + TempFile.NamePrefix = kAppName.Replace(" ", "_"); + var folder = new TempFile().Path; + RobustFile.Delete(folder); + Directory.CreateDirectory(folder); + var model = new IMDIArchivingDlgViewModel(kAppName, title, + title.ToLatinOnly("~", "_", ""), false, SetFilesToArchive, folder); + + model.ArchivingPackage.AccessCode = "internal"; + model.ArchivingPackage.Access.DateAvailable = "3 March 2029"; + model.ArchivingPackage.Access.Owner = "Fred"; + model.ArchivingPackage.Publisher = "SIL Elbonia"; + model.ArchivingPackage.Location = new ArchivingLocation + { + Address = "1234 Shoulder St.; Armstrong", + Country = "Elbonia" + }; + model.ArchivingPackage.Author = "Test Dude"; + model.ArchivingPackage.Owner = "Mike"; + + foreach (ListViewGroup group in m_listFiles.Groups) + { + var session = model.AddSession(group.Header); + foreach (var file in (from ListViewItem item in @group.Items select item.Text)) + { + session.AddFile(new ArchivingFile(file)); + session.AddFileAccess(file, (ArchivingPackage)model.ArchivingPackage); + } + + session.Genre = "Dance"; + session.SubGenre = "Entertainment"; + session.PlanningType = "Spontaneous"; + } + + + using (var imdiArchiveDlg = new IMDIArchivingDlg(model, LocalizationManager.GetString( + "ArchivingTestApp.MainForm.AdditionalImdiArchiveProcessInfo", "This is a test of IMDI archival."))) + { + imdiArchiveDlg.ShowDialog(this); + } + } + + private void SetFilesToArchive(ArchivingDlgViewModel model, CancellationToken cancellationToken) + { + foreach (ListViewGroup group in m_listFiles.Groups) + { + var files = (from ListViewItem item in @group.Items select item.Text).ToList(); + model.AddFileGroup(group.Header, files, Format(LocalizationManager.GetString( + "ArchivingTestApp.MainForm.AddingFileGroupProgressMsg", + "Adding {0}", "Param is group name (directory)"), group.Header)); + } + } + + private static string GetFileDescription(string key, string filename) + { + return filename.Replace("\\", "_"); + } + + private void HandleAddFilesClick(object sender, EventArgs e) + { + using (var dlg = new OpenFileDialog()) + { + dlg.CheckFileExists = true; + dlg.Multiselect = true; + dlg.Title = Format(LocalizationManager.GetString( + "ArchivingTestApp.MainForm.AddingFileGroupProgressMsg", + "{0} - Select files to archive", "Param is app name"), kAppName); + if (dlg.ShowDialog(this) == DialogResult.OK && dlg.FileNames.Any()) + { + var group = new ListViewGroup(GetDirectoryName(dlg.FileNames[0]) ?? "root"); + + foreach (var file in dlg.FileNames) + { + var item = new ListViewItem(file) { Group = group }; + m_listFiles.Items.Add(item); + } + + m_listFiles.Groups.Add(group); + m_listFiles.Refresh(); + + m_btnRamp.Enabled = true; + m_btnIMDI.Enabled = true; + } + } + } + } +} diff --git a/TestApps/ArchivingTestApp/MainForm.resx b/TestApps/ArchivingTestApp/MainForm.resx new file mode 100644 index 000000000..af32865ec --- /dev/null +++ b/TestApps/ArchivingTestApp/MainForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/TestApps/ArchivingTestApp/Program.cs b/TestApps/ArchivingTestApp/Program.cs new file mode 100644 index 000000000..6bd17d30a --- /dev/null +++ b/TestApps/ArchivingTestApp/Program.cs @@ -0,0 +1,32 @@ +using L10NSharp; +using SIL.IO; + +namespace ArchivingTestApp +{ + internal static class Program + { + internal static ILocalizationManager? PrimaryL10NManager; + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main(string[] args) + { + // To customize application configuration such as set high DPI settings or default font, + // see https://aka.ms/applicationconfiguration. + //ApplicationConfiguration.Initialize(); + + var preferredUILocale = "en"; + if (args.Length > 0) + preferredUILocale = args[0].ToLowerInvariant(); + + var localizationFolder = Path.GetDirectoryName( + FileLocationUtilities.GetFileDistributedWithApplication($"Palaso.{preferredUILocale}.xlf")); + PrimaryL10NManager = LocalizationManager.Create(preferredUILocale, "Palaso", "Palaso", + "1.0.0", localizationFolder, "SIL/Palaso", null, "testapp@sil.org", new [] {"SIL."}); + + Application.Run(new MainForm()); + } + } +} \ No newline at end of file diff --git a/TestApps/SIL.Windows.Forms.TestApp/ContributorsForm.cs b/TestApps/SIL.Windows.Forms.TestApp/ContributorsForm.cs index ebe9643f8..414316118 100644 --- a/TestApps/SIL.Windows.Forms.TestApp/ContributorsForm.cs +++ b/TestApps/SIL.Windows.Forms.TestApp/ContributorsForm.cs @@ -3,6 +3,7 @@ using System.ComponentModel; using System.Drawing; using System.Windows.Forms; +using SIL.Core.ClearShare; using SIL.Windows.Forms.ClearShare; using SIL.Windows.Forms.ClearShare.WinFormsUI; using SIL.Windows.Forms.Widgets.BetterGrid; diff --git a/TestApps/SIL.Windows.Forms.TestApp/TestAppForm.cs b/TestApps/SIL.Windows.Forms.TestApp/TestAppForm.cs index 978db2993..a1f6f2216 100644 --- a/TestApps/SIL.Windows.Forms.TestApp/TestAppForm.cs +++ b/TestApps/SIL.Windows.Forms.TestApp/TestAppForm.cs @@ -9,6 +9,7 @@ using System.Text; using System.Threading; using System.Windows.Forms; +using SIL.Core.ClearShare; using SIL.Extensions; using SIL.IO; using SIL.Lexicon; diff --git a/appveyor.yml b/appveyor.yml index 327100da9..3587e5062 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,7 @@ version: '{build}' branches: only: - master -image: Visual Studio 2019 +image: Visual Studio 2022 init: - cmd: | set GITVERSION_BUILD_NUMBER=%APPVEYOR_BUILD_NUMBER%