From 2838f6f7a288fad20da07bed39dd2763e5bd434e Mon Sep 17 00:00:00 2001 From: Ariel Rorabaugh Date: Wed, 10 Dec 2025 10:28:47 -0500 Subject: [PATCH 1/4] Add LicenseTSS and CreatorTSS virtual properties to CmPicture --- CHANGELOG.md | 2 + src/SIL.LCModel.Core/SIL.LCModel.Core.csproj | 2 +- .../SIL.LCModel.Utils.csproj | 2 +- src/SIL.LCModel/DomainImpl/CmPicture.cs | 72 +++++++++++++++++++ .../DomainImpl/CmPictureTests.cs | 16 +++++ 5 files changed, 92 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9445f32..c6aafa0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added +- [SIL.LCModel] Add new virtual property LicenseTSS in CmPicture, to access info about the picture's copyright and license. +- [SIL.LCModel] Add new virtual property CreatorTSS in CmPicture, to access info about the picture's creator. - [SIL.LCModel] Add SpecificItemAndFieldName() to ISenseOrEntry - [SIL.LCModel] Added a parameter to GetBestGuess() and TryGetBestGuess() to do lowercase matching regardless of the occurrence index - [SIL.LCModel] Add GetCaptionOrHeadword() to CmPicture diff --git a/src/SIL.LCModel.Core/SIL.LCModel.Core.csproj b/src/SIL.LCModel.Core/SIL.LCModel.Core.csproj index 5043a6fd..20f83c0c 100644 --- a/src/SIL.LCModel.Core/SIL.LCModel.Core.csproj +++ b/src/SIL.LCModel.Core/SIL.LCModel.Core.csproj @@ -1,4 +1,4 @@ - + netstandard2.0;net462;net8.0 diff --git a/src/SIL.LCModel.Utils/SIL.LCModel.Utils.csproj b/src/SIL.LCModel.Utils/SIL.LCModel.Utils.csproj index 3e9ae38e..68e3c49a 100644 --- a/src/SIL.LCModel.Utils/SIL.LCModel.Utils.csproj +++ b/src/SIL.LCModel.Utils/SIL.LCModel.Utils.csproj @@ -1,4 +1,4 @@ - + net462;netstandard2.0 diff --git a/src/SIL.LCModel/DomainImpl/CmPicture.cs b/src/SIL.LCModel/DomainImpl/CmPicture.cs index 9f5a5b2a..cfacb512 100644 --- a/src/SIL.LCModel/DomainImpl/CmPicture.cs +++ b/src/SIL.LCModel/DomainImpl/CmPicture.cs @@ -17,6 +17,8 @@ using System.Diagnostics; using System.IO; using System.Linq; +using System.Security; +using SIL.Core; using SIL.LCModel.Core.Cellar; using SIL.LCModel.Core.KernelInterfaces; using SIL.LCModel.Core.Text; @@ -312,6 +314,76 @@ public ITsString PathNameTSS } } + /// ------------------------------------------------------------------------------------ + /// + /// Method called to implement virtual property. Returns license metadata of associated + /// file. + /// ---------------------------------------------------------------------------------- + [VirtualProperty(CellarPropertyType.String)] + public ITsString LicenseTSS + { + get + { + var path = PictureFileRA?.AbsoluteInternalPath; + SIL.Core.ClearShare.MetadataCore metadata; + try + { + metadata = SIL.Core.ClearShare.MetadataCore.CreateMetadataCoreFromFile(path); + } + catch (Exception e) + { + // Error getting metadata from path + metadata = null; + } + + if (metadata == null) + return null; + + var analWs = m_cache.WritingSystemFactory.GetStrFromWs(m_cache.DefaultAnalWs); + var vernWs = m_cache.WritingSystemFactory.GetStrFromWs(m_cache.DefaultVernWs); + + // Get the license in first analysis writing system if available, otherwise first vernacular ws, otherwise English. + var license = metadata.License?.GetMinimalFormForCredits(new[] { analWs, vernWs, "en" }, out _); + if (string.IsNullOrEmpty(metadata.CopyrightNotice) && string.IsNullOrEmpty(license)) + return null; + + // We want the short copyright notice, but it isn't safe to ask for if CopyrightNotice is null. + var copyright = string.IsNullOrEmpty(metadata.CopyrightNotice) + ? string.Empty + : metadata.ShortCopyrightNotice; + return m_cache.MakeUserTss(SecurityElement.Escape(string.Join(", ", new[] { copyright, license }.Where(txt => !string.IsNullOrEmpty(txt))))); + } + } + + /// ------------------------------------------------------------------------------------ + /// + /// Method called to implement virtual property. Returns creator metadata of associated + /// file. (LT-7104 requested internal path instead of original path.) + /// ---------------------------------------------------------------------------------- + [VirtualProperty(CellarPropertyType.String)] + public ITsString CreatorTSS + { + get + { + var path = PictureFileRA?.AbsoluteInternalPath; + SIL.Core.ClearShare.MetadataCore metadata; + try + { + metadata = SIL.Core.ClearShare.MetadataCore.CreateMetadataCoreFromFile(path); + } + catch (Exception e) + { + // Error getting metadata from path + metadata = null; + } + + if (metadata == null || metadata.Creator == null) + return null; + + return m_cache.MakeUserTss(metadata.Creator); + } + } + /// /// Get the sense number of the owning LexSense. /// ENHANCE DamienD: register this property as modified when its dependencies change diff --git a/tests/SIL.LCModel.Tests/DomainImpl/CmPictureTests.cs b/tests/SIL.LCModel.Tests/DomainImpl/CmPictureTests.cs index 0cd353f8..8cca0945 100644 --- a/tests/SIL.LCModel.Tests/DomainImpl/CmPictureTests.cs +++ b/tests/SIL.LCModel.Tests/DomainImpl/CmPictureTests.cs @@ -343,6 +343,22 @@ public void CmPicture_GetTextRepOfPicture() Assert.AreEqual("MyRef", figParams[6], "Picture reference should be exported."); } + /// ------------------------------------------------------------------------------------- + /// + /// Test ability to get license of a picture. + /// + /// ------------------------------------------------------------------------------------- + [Test] + public void CmPicture_GetLicenseInfoForPicture() + { + //MetadataCore metadata = new MetadataCore(); + //metadata.Creator = "test"; + //metadata.CopyrightNotice = "test copyright"; + //metadata.Write(m_pict.PictureFileRA.InternalPath, true); + + CmPicture pic = (CmPicture) m_pict; + Assert.IsNull(pic.LicenseTSS); + } /// ------------------------------------------------------------------------------------- /// /// Test ability to update the properties of a picture, given a file, folder, etc. From 0872672bc955f677c62e9f733abcfb2527ac0974 Mon Sep 17 00:00:00 2001 From: Ariel Rorabaugh Date: Wed, 10 Dec 2025 13:37:52 -0500 Subject: [PATCH 2/4] Add default localizer for CmPicture LicenseTSS --- src/SIL.LCModel.Core/SIL.LCModel.Core.csproj | 2 +- src/SIL.LCModel/DomainImpl/CmPicture.cs | 8 +++++- src/SIL.LCModel/SIL.LCModel.csproj | 1 + .../DomainImpl/CmPictureTests.cs | 28 +++++++++++++------ 4 files changed, 28 insertions(+), 11 deletions(-) diff --git a/src/SIL.LCModel.Core/SIL.LCModel.Core.csproj b/src/SIL.LCModel.Core/SIL.LCModel.Core.csproj index 20f83c0c..5043a6fd 100644 --- a/src/SIL.LCModel.Core/SIL.LCModel.Core.csproj +++ b/src/SIL.LCModel.Core/SIL.LCModel.Core.csproj @@ -1,4 +1,4 @@ - + netstandard2.0;net462;net8.0 diff --git a/src/SIL.LCModel/DomainImpl/CmPicture.cs b/src/SIL.LCModel/DomainImpl/CmPicture.cs index cfacb512..242fae8a 100644 --- a/src/SIL.LCModel/DomainImpl/CmPicture.cs +++ b/src/SIL.LCModel/DomainImpl/CmPicture.cs @@ -18,7 +18,6 @@ using System.IO; using System.Linq; using System.Security; -using SIL.Core; using SIL.LCModel.Core.Cellar; using SIL.LCModel.Core.KernelInterfaces; using SIL.LCModel.Core.Text; @@ -342,11 +341,18 @@ public ITsString LicenseTSS var analWs = m_cache.WritingSystemFactory.GetStrFromWs(m_cache.DefaultAnalWs); var vernWs = m_cache.WritingSystemFactory.GetStrFromWs(m_cache.DefaultVernWs); + // Set Localizer.Default to use L10NSharpLocalizer to localize the license. + ILocalizer oldLocalizer = Localizer.Default; + Localizer.Default = new SIL.Core.Desktop.i18n.L10NSharpLocalizer(); + // Get the license in first analysis writing system if available, otherwise first vernacular ws, otherwise English. var license = metadata.License?.GetMinimalFormForCredits(new[] { analWs, vernWs, "en" }, out _); if (string.IsNullOrEmpty(metadata.CopyrightNotice) && string.IsNullOrEmpty(license)) return null; + // Reset Localizer default. + Localizer.Default = oldLocalizer; + // We want the short copyright notice, but it isn't safe to ask for if CopyrightNotice is null. var copyright = string.IsNullOrEmpty(metadata.CopyrightNotice) ? string.Empty diff --git a/src/SIL.LCModel/SIL.LCModel.csproj b/src/SIL.LCModel/SIL.LCModel.csproj index a1a1487f..23d12593 100644 --- a/src/SIL.LCModel/SIL.LCModel.csproj +++ b/src/SIL.LCModel/SIL.LCModel.csproj @@ -22,6 +22,7 @@ + diff --git a/tests/SIL.LCModel.Tests/DomainImpl/CmPictureTests.cs b/tests/SIL.LCModel.Tests/DomainImpl/CmPictureTests.cs index 8cca0945..7d53881f 100644 --- a/tests/SIL.LCModel.Tests/DomainImpl/CmPictureTests.cs +++ b/tests/SIL.LCModel.Tests/DomainImpl/CmPictureTests.cs @@ -7,6 +7,7 @@ using System.IO; using System.Text; using NUnit.Framework; +using SIL.Core.ClearShare; using SIL.LCModel.Core.KernelInterfaces; using SIL.LCModel.Core.Text; using SIL.LCModel.Core.WritingSystems; @@ -28,7 +29,7 @@ public class CmPictureTests: MemoryOnlyBackendProviderRestoredForEachTestTestBas private MockFileOS m_fileOs; private ICmPictureFactory m_pictureFactory; private ICmPicture m_pict; - private string m_internalPath = Path.DirectorySeparatorChar + Path.GetRandomFileName(); + private string m_internalPath; private CoreWritingSystemDefinition m_wsGerman; private CoreWritingSystemDefinition m_wsSpanish; #endregion @@ -43,6 +44,10 @@ public override void FixtureSetup() base.FixtureSetup(); Cache.ServiceLocator.WritingSystemManager.GetOrSet("de", out m_wsGerman); Cache.ServiceLocator.WritingSystemManager.GetOrSet("es", out m_wsSpanish); + // Initialize internalPath inside a temp folder + // .jpg file extension is needed for the test CmPicture_GetCreatorAndLicenseForPicture + string tempFolder = Path.GetTempPath(); + m_internalPath = Path.Combine(tempFolder, Guid.NewGuid().ToString() + ".jpg"); } /// ------------------------------------------------------------------------------------- @@ -74,6 +79,8 @@ protected override void CreateTestData() public override void TestTearDown() { FileUtils.Manager.Reset(); + if (File.Exists(m_internalPath)) + File.Delete(m_internalPath); base.TestTearDown(); } @@ -345,19 +352,22 @@ public void CmPicture_GetTextRepOfPicture() /// ------------------------------------------------------------------------------------- /// - /// Test ability to get license of a picture. + /// Test ability to get creator and license of a picture. /// /// ------------------------------------------------------------------------------------- [Test] - public void CmPicture_GetLicenseInfoForPicture() + public void CmPicture_GetCreatorAndLicenseForPicture() { - //MetadataCore metadata = new MetadataCore(); - //metadata.Creator = "test"; - //metadata.CopyrightNotice = "test copyright"; - //metadata.Write(m_pict.PictureFileRA.InternalPath, true); + // Copy the test image penguin.jpg from TestData to a temp file located at m_internalPath + string penguinPath = Path.Combine(TestDirectoryFinder.TestDataDirectory, "penguin.jpg"); + File.Copy(penguinPath, m_internalPath, overwrite: true); - CmPicture pic = (CmPicture) m_pict; - Assert.IsNull(pic.LicenseTSS); + MetadataCore metadata = new MetadataCore(); + metadata.Creator = "test creator"; + metadata.CopyrightNotice = "test copyright"; + metadata.Write(m_pict.PictureFileRA.AbsoluteInternalPath, false); + Assert.AreEqual("test creator", ((CmPicture)m_pict).CreatorTSS.ToString()); + Assert.AreEqual("test copyright",((CmPicture)m_pict).LicenseTSS.ToString()); } /// ------------------------------------------------------------------------------------- /// From fdc1f521859f51435eb45b96aef6f282a74cd2b0 Mon Sep 17 00:00:00 2001 From: Ariel Rorabaugh Date: Wed, 10 Dec 2025 13:46:19 -0500 Subject: [PATCH 3/4] Remove unused variable declaration --- src/SIL.LCModel/DomainImpl/CmPicture.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SIL.LCModel/DomainImpl/CmPicture.cs b/src/SIL.LCModel/DomainImpl/CmPicture.cs index 242fae8a..b91e9e7b 100644 --- a/src/SIL.LCModel/DomainImpl/CmPicture.cs +++ b/src/SIL.LCModel/DomainImpl/CmPicture.cs @@ -329,7 +329,7 @@ public ITsString LicenseTSS { metadata = SIL.Core.ClearShare.MetadataCore.CreateMetadataCoreFromFile(path); } - catch (Exception e) + catch { // Error getting metadata from path metadata = null; @@ -377,7 +377,7 @@ public ITsString CreatorTSS { metadata = SIL.Core.ClearShare.MetadataCore.CreateMetadataCoreFromFile(path); } - catch (Exception e) + catch { // Error getting metadata from path metadata = null; From fa430e8bf3408ffa82bf76d9e31c19b603a394f4 Mon Sep 17 00:00:00 2001 From: Ariel Rorabaugh Date: Wed, 10 Dec 2025 14:05:17 -0500 Subject: [PATCH 4/4] Escape metadata creator string --- src/SIL.LCModel/DomainImpl/CmPicture.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SIL.LCModel/DomainImpl/CmPicture.cs b/src/SIL.LCModel/DomainImpl/CmPicture.cs index b91e9e7b..255ce8ff 100644 --- a/src/SIL.LCModel/DomainImpl/CmPicture.cs +++ b/src/SIL.LCModel/DomainImpl/CmPicture.cs @@ -385,8 +385,8 @@ public ITsString CreatorTSS if (metadata == null || metadata.Creator == null) return null; - - return m_cache.MakeUserTss(metadata.Creator); + + return m_cache.MakeUserTss(SecurityElement.Escape(metadata.Creator)); } }