From 4d4eab39e8c4c3ce8ba9cc1cf9ac35a3fa1689f7 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Wed, 4 Nov 2015 15:43:39 +0000 Subject: [PATCH 1/2] ArchetypeModel to IPublishedContent Adds extension methods for `ArchetypeModel` and `ArchetypeFieldsetModel` for converting to Umbraco's `IPublishedContent`. Included unit-tests to support the deserialization and verify property-value retrieval. Modifies `ArchetypePropertyModelExtensions.CreateDummyPropertyType` extension method, to test if the `PropertyValueConvertersResolver` is available, (otherwise the unit-tests throw an exception from Umbraco's `PublishedPropertyType`). --- .../Archetype.Tests/Archetype.Tests.csproj | 1 + .../ArchetypePublishedContentTests.cs | 95 ++++++++ .../Archetype.Umbraco.csproj | 5 + .../ArchetypeFieldsetModelExtensions.cs | 13 ++ .../Extensions/ArchetypeModelExtensions.cs | 14 ++ .../ArchetypePropertyModelExtensions.cs | 6 + .../Models/ArchetypePublishedContent.cs | 206 ++++++++++++++++++ .../Models/ArchetypePublishedContentSet.cs | 36 +++ .../Models/ArchetypePublishedProperty.cs | 102 +++++++++ 9 files changed, 478 insertions(+) create mode 100644 app/Umbraco/Archetype.Tests/PublishedContent/ArchetypePublishedContentTests.cs create mode 100644 app/Umbraco/Umbraco.Archetype/Extensions/ArchetypeFieldsetModelExtensions.cs create mode 100644 app/Umbraco/Umbraco.Archetype/Extensions/ArchetypeModelExtensions.cs create mode 100644 app/Umbraco/Umbraco.Archetype/Models/ArchetypePublishedContent.cs create mode 100644 app/Umbraco/Umbraco.Archetype/Models/ArchetypePublishedContentSet.cs create mode 100644 app/Umbraco/Umbraco.Archetype/Models/ArchetypePublishedProperty.cs diff --git a/app/Umbraco/Archetype.Tests/Archetype.Tests.csproj b/app/Umbraco/Archetype.Tests/Archetype.Tests.csproj index 47ff9e0..19264d5 100644 --- a/app/Umbraco/Archetype.Tests/Archetype.Tests.csproj +++ b/app/Umbraco/Archetype.Tests/Archetype.Tests.csproj @@ -261,6 +261,7 @@ + diff --git a/app/Umbraco/Archetype.Tests/PublishedContent/ArchetypePublishedContentTests.cs b/app/Umbraco/Archetype.Tests/PublishedContent/ArchetypePublishedContentTests.cs new file mode 100644 index 0000000..27198cf --- /dev/null +++ b/app/Umbraco/Archetype.Tests/PublishedContent/ArchetypePublishedContentTests.cs @@ -0,0 +1,95 @@ +using System; +using System.Linq; +using Archetype.Extensions; +using Archetype.Models; +using Archetype.PropertyConverters; +using NUnit.Framework; +using Umbraco.Core.Models; +using Umbraco.Web; + +namespace Archetype.Tests.PublishedContent +{ + [TestFixture] + public class ArchetypePublishedContentTests + { + private ArchetypeModel _archetype; + + [TestFixtureSetUp] + public void Init() + { + var archetypeJson = System.IO.File.ReadAllText("..\\..\\Data\\sample-1.json"); + var converter = new ArchetypeValueConverter(); + + _archetype = (ArchetypeModel)converter.ConvertDataToSource(null, archetypeJson, false); + } + + [Test] + public void ArchetypeModel_Initialized() + { + Assert.IsNotNull(_archetype); + Assert.IsNotEmpty(_archetype.Fieldsets); + } + + [Test] + public void ArchetypeModel_To_PublishedContentSet() + { + var contentSet = _archetype.ToPublishedContentSet(); + + Assert.That(contentSet, Is.Not.Null); + Assert.That(contentSet, Is.Not.Empty); + Assert.That(contentSet, Is.All.InstanceOf()); + } + + [Test] + public void ArchetypeFieldsetModel_To_PublishedContent() + { + var fieldset = _archetype.Fieldsets.FirstOrDefault(); + var content = fieldset.ToPublishedContent(); + + Assert.That(content, Is.Not.Null); + Assert.That(content, Is.InstanceOf()); + Assert.That(content.Properties, Is.Not.Empty); + Assert.That(content.Properties, Is.All.InstanceOf()); + } + + [TestCase("boxHeadline", "Box 1 Title")] + [TestCase("link", "3175")] + [TestCase("link", 3175)] + [TestCase("show", "1")] + [TestCase("show", true)] + [TestCase("show", 1)] + public void ArchetypePublishedContent_Typed_Properties(string propertyAlias, T expected) + { + var fieldset = _archetype.Fieldsets.FirstOrDefault(); + var content = fieldset.ToPublishedContent(); + + var actual = content.GetPropertyValue(propertyAlias); + + Assert.AreEqual(actual, expected); + } + + [Test] + public void Null_ArchetypeModel_Throws_Exception() + { + TestDelegate code = () => + { + ArchetypeModel archetype = null; + archetype.ToPublishedContentSet(); + }; + + Assert.Throws(code); + } + + [Test] + public void Null_ArchetypeFieldsetModel_Throws_Exception() + { + TestDelegate code = () => + { + ArchetypeFieldsetModel fieldset = null; + fieldset.ToPublishedContent(); + }; + + Assert.Throws(code); + } + } +} \ No newline at end of file diff --git a/app/Umbraco/Umbraco.Archetype/Archetype.Umbraco.csproj b/app/Umbraco/Umbraco.Archetype/Archetype.Umbraco.csproj index 0a7417f..498d76e 100644 --- a/app/Umbraco/Umbraco.Archetype/Archetype.Umbraco.csproj +++ b/app/Umbraco/Umbraco.Archetype/Archetype.Umbraco.csproj @@ -231,7 +231,9 @@ + + @@ -243,6 +245,9 @@ + + + diff --git a/app/Umbraco/Umbraco.Archetype/Extensions/ArchetypeFieldsetModelExtensions.cs b/app/Umbraco/Umbraco.Archetype/Extensions/ArchetypeFieldsetModelExtensions.cs new file mode 100644 index 0000000..1cf6933 --- /dev/null +++ b/app/Umbraco/Umbraco.Archetype/Extensions/ArchetypeFieldsetModelExtensions.cs @@ -0,0 +1,13 @@ +using Archetype.Models; +using Umbraco.Core.Models; + +namespace Archetype.Extensions +{ + public static class ArchetypeFieldsetModelExtensions + { + public static IPublishedContent ToPublishedContent(this ArchetypeFieldsetModel fieldset) + { + return new ArchetypePublishedContent(fieldset); + } + } +} \ No newline at end of file diff --git a/app/Umbraco/Umbraco.Archetype/Extensions/ArchetypeModelExtensions.cs b/app/Umbraco/Umbraco.Archetype/Extensions/ArchetypeModelExtensions.cs new file mode 100644 index 0000000..f5ce7ef --- /dev/null +++ b/app/Umbraco/Umbraco.Archetype/Extensions/ArchetypeModelExtensions.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using Archetype.Models; +using Umbraco.Core.Models; + +namespace Archetype.Extensions +{ + public static class ArchetypeModelExtensions + { + public static IEnumerable ToPublishedContentSet(this ArchetypeModel archetype) + { + return new ArchetypePublishedContentSet(archetype); + } + } +} \ No newline at end of file diff --git a/app/Umbraco/Umbraco.Archetype/Extensions/ArchetypePropertyModelExtensions.cs b/app/Umbraco/Umbraco.Archetype/Extensions/ArchetypePropertyModelExtensions.cs index 2e8532b..c019495 100644 --- a/app/Umbraco/Umbraco.Archetype/Extensions/ArchetypePropertyModelExtensions.cs +++ b/app/Umbraco/Umbraco.Archetype/Extensions/ArchetypePropertyModelExtensions.cs @@ -2,6 +2,7 @@ using Archetype.Models; using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.PropertyEditors; namespace Archetype.Extensions { @@ -27,6 +28,11 @@ namespace Archetype.Extensions /// internal static PublishedPropertyType CreateDummyPropertyType(this ArchetypePropertyModel prop) { + // We need to check if `PropertyValueConvertersResolver` exists, + // otherwise `PublishedPropertyType` will throw an exception outside of the Umbraco context.; e.g. unit-tests. + if (!PropertyValueConvertersResolver.HasCurrent) + return null; + return new PublishedPropertyType(prop.HostContentType, new PropertyType(new DataTypeDefinition(-1, prop.PropertyEditorAlias) { Id = prop.DataTypeId })); } } diff --git a/app/Umbraco/Umbraco.Archetype/Models/ArchetypePublishedContent.cs b/app/Umbraco/Umbraco.Archetype/Models/ArchetypePublishedContent.cs new file mode 100644 index 0000000..793534e --- /dev/null +++ b/app/Umbraco/Umbraco.Archetype/Models/ArchetypePublishedContent.cs @@ -0,0 +1,206 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; + +namespace Archetype.Models +{ + public class ArchetypePublishedContent : IPublishedContent + { + private ArchetypeFieldsetModel _fieldset; + + private bool _initialized; + + private IPublishedProperty[] _properties; + + public ArchetypePublishedContent(ArchetypeFieldsetModel fieldset) + { + if (fieldset == null) + throw new ArgumentNullException("fieldset"); + + _fieldset = fieldset; + + this.Initialize(); + } + + internal ArchetypeFieldsetModel ArchetypeFieldset + { + get { return _fieldset; } + } + + public IEnumerable Children + { + get { return Enumerable.Empty(); } + } + + public IEnumerable ContentSet + { + get { return Enumerable.Empty(); } + } + + public PublishedContentType ContentType + { + get { return default(PublishedContentType); } + } + + public DateTime CreateDate + { + get { return DateTime.MinValue; } + } + + public int CreatorId + { + get { return default(int); } + } + + public string CreatorName + { + get { return default(string); } + } + + public string DocumentTypeAlias + { + get { return _fieldset.Alias; } + } + + public int DocumentTypeId + { + get { return default(int); } + } + + public int GetIndex() + { + return default(int); + } + + public IPublishedProperty GetProperty(string alias, bool recurse) + { + return Properties.FirstOrDefault(x => x.PropertyTypeAlias.InvariantEquals(alias)); + } + + public IPublishedProperty GetProperty(string alias) + { + return this.GetProperty(alias, false); + } + + public int Id + { + get { return default(int); } + } + + public bool IsDraft + { + get { return _fieldset.Disabled; } + } + + public PublishedItemType ItemType + { + get { return PublishedItemType.Content; } + } + + public int Level + { + get { return default(int); } + } + + public string Name + { + get { return default(string); } + } + + public IPublishedContent Parent + { + get { return default(IPublishedContent); } + } + + public string Path + { + get { return default(string); } + } + + public ICollection Properties + { + get + { + if (_initialized == false) + { + this.Initialize(); + } + + return _properties; + } + } + + public int SortOrder + { + get { return default(int); } + } + + public int TemplateId + { + get { return default(int); } + } + + public DateTime UpdateDate + { + get { return default(DateTime); } + } + + public string Url + { + get { return default(string); } + } + + public string UrlName + { + get { return default(string); } + } + + public Guid Version + { + get { return Guid.Empty; } + } + + public int WriterId + { + get { return default(int); } + } + + public string WriterName + { + get { return default(string); } + } + + public object this[string alias] + { + get + { + var property = this.GetProperty(alias); + + return property == null + ? null + : property.Value; + } + } + + private void Initialize() + { + if (_fieldset == null) + { + return; + } + + if (_fieldset.Properties != null) + { + _properties = _fieldset.Properties + .Select(x => new ArchetypePublishedProperty(x)) + .Cast() + .ToArray(); + } + + _initialized = true; + } + } +} \ No newline at end of file diff --git a/app/Umbraco/Umbraco.Archetype/Models/ArchetypePublishedContentSet.cs b/app/Umbraco/Umbraco.Archetype/Models/ArchetypePublishedContentSet.cs new file mode 100644 index 0000000..5a529d9 --- /dev/null +++ b/app/Umbraco/Umbraco.Archetype/Models/ArchetypePublishedContentSet.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace Archetype.Models +{ + public class ArchetypePublishedContentSet : IEnumerable + { + private IEnumerable _items { get; set; } + + public ArchetypePublishedContentSet(ArchetypeModel archetype) + { + if (archetype == null) + throw new ArgumentNullException("archetype"); + + this.ArchetypeModel = archetype; + + _items = archetype.Fieldsets + .Where(x => x.Disabled == false) + .Select(x => new ArchetypePublishedContent(x)); + } + + internal ArchetypeModel ArchetypeModel { get; private set; } + + public IEnumerator GetEnumerator() + { + return _items.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/app/Umbraco/Umbraco.Archetype/Models/ArchetypePublishedProperty.cs b/app/Umbraco/Umbraco.Archetype/Models/ArchetypePublishedProperty.cs new file mode 100644 index 0000000..cada797 --- /dev/null +++ b/app/Umbraco/Umbraco.Archetype/Models/ArchetypePublishedProperty.cs @@ -0,0 +1,102 @@ +using System; +using Archetype.Extensions; +using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; + +namespace Archetype.Models +{ + public class ArchetypePublishedProperty : IPublishedProperty + { + private readonly object _rawValue; + private readonly Lazy _sourceValue; + private readonly Lazy _objectValue; + private readonly Lazy _xpathValue; + private readonly ArchetypePropertyModel _property; + private readonly PublishedPropertyType _propertyType; + + public ArchetypePublishedProperty(ArchetypePropertyModel property) + { + if (property == null) + throw new ArgumentNullException("property"); + + var preview = false; + + _property = property; + _rawValue = property.Value; + + _propertyType = property.CreateDummyPropertyType(); + + if (_propertyType != null) + { + _sourceValue = new Lazy(() => _propertyType.ConvertDataToSource(_rawValue, preview)); + _objectValue = new Lazy(() => _propertyType.ConvertSourceToObject(_sourceValue.Value, preview)); + _xpathValue = new Lazy(() => _propertyType.ConvertSourceToXPath(_sourceValue.Value, preview)); + } + } + + internal ArchetypePropertyModel ArchetypeProperty + { + get { return _property; } + } + + public object DataValue + { + get + { + return _sourceValue != null + ? _sourceValue.Value + : _rawValue; + } + } + + public bool HasValue + { + get + { + if (_property == null || _rawValue == null) + { + return false; + } + + return !string.IsNullOrEmpty(_rawValue.ToString()); + } + } + + public string PropertyTypeAlias + { + get + { + if (_propertyType != null && !string.IsNullOrWhiteSpace(_propertyType.PropertyTypeAlias)) + { + return _propertyType.PropertyTypeAlias; + } + else if (_property != null) + { + return _property.Alias; + } + + return null; + } + } + + public object Value + { + get + { + return _objectValue != null + ? _objectValue.Value + : _rawValue; + } + } + + public object XPathValue + { + get + { + return _xpathValue != null + ? _xpathValue.Value + : _rawValue; + } + } + } +} \ No newline at end of file From b0dcefaf0681892b4ba7bccf727a8d68ff43c5d1 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Thu, 5 Nov 2015 11:58:45 +0000 Subject: [PATCH 2/2] Refactored the PublishedContent/Property classes. Streamlines how the properties are stored and retrieved internally, (dictionary is faster than linq loops). Removed the `Initialize` method, as we can do the set-up within the constructor. --- .../Models/ArchetypePublishedContent.cs | 41 ++++--------------- .../Models/ArchetypePublishedProperty.cs | 21 +--------- 2 files changed, 11 insertions(+), 51 deletions(-) diff --git a/app/Umbraco/Umbraco.Archetype/Models/ArchetypePublishedContent.cs b/app/Umbraco/Umbraco.Archetype/Models/ArchetypePublishedContent.cs index 793534e..b691431 100644 --- a/app/Umbraco/Umbraco.Archetype/Models/ArchetypePublishedContent.cs +++ b/app/Umbraco/Umbraco.Archetype/Models/ArchetypePublishedContent.cs @@ -11,9 +11,7 @@ namespace Archetype.Models { private ArchetypeFieldsetModel _fieldset; - private bool _initialized; - - private IPublishedProperty[] _properties; + private readonly Dictionary _properties; public ArchetypePublishedContent(ArchetypeFieldsetModel fieldset) { @@ -22,7 +20,11 @@ namespace Archetype.Models _fieldset = fieldset; - this.Initialize(); + _properties = fieldset.Properties + .ToDictionary( + x => x.Alias, + x => new ArchetypePublishedProperty(x) as IPublishedProperty, + StringComparer.InvariantCultureIgnoreCase); } internal ArchetypeFieldsetModel ArchetypeFieldset @@ -77,7 +79,8 @@ namespace Archetype.Models public IPublishedProperty GetProperty(string alias, bool recurse) { - return Properties.FirstOrDefault(x => x.PropertyTypeAlias.InvariantEquals(alias)); + IPublishedProperty property; + return _properties.TryGetValue(alias, out property) ? property : null; } public IPublishedProperty GetProperty(string alias) @@ -122,15 +125,7 @@ namespace Archetype.Models public ICollection Properties { - get - { - if (_initialized == false) - { - this.Initialize(); - } - - return _properties; - } + get { return _properties.Values; } } public int SortOrder @@ -184,23 +179,5 @@ namespace Archetype.Models : property.Value; } } - - private void Initialize() - { - if (_fieldset == null) - { - return; - } - - if (_fieldset.Properties != null) - { - _properties = _fieldset.Properties - .Select(x => new ArchetypePublishedProperty(x)) - .Cast() - .ToArray(); - } - - _initialized = true; - } } } \ No newline at end of file diff --git a/app/Umbraco/Umbraco.Archetype/Models/ArchetypePublishedProperty.cs b/app/Umbraco/Umbraco.Archetype/Models/ArchetypePublishedProperty.cs index cada797..010a9ba 100644 --- a/app/Umbraco/Umbraco.Archetype/Models/ArchetypePublishedProperty.cs +++ b/app/Umbraco/Umbraco.Archetype/Models/ArchetypePublishedProperty.cs @@ -53,30 +53,13 @@ namespace Archetype.Models { get { - if (_property == null || _rawValue == null) - { - return false; - } - - return !string.IsNullOrEmpty(_rawValue.ToString()); + return _rawValue != null && !string.IsNullOrEmpty(_rawValue.ToString()); } } public string PropertyTypeAlias { - get - { - if (_propertyType != null && !string.IsNullOrWhiteSpace(_propertyType.PropertyTypeAlias)) - { - return _propertyType.PropertyTypeAlias; - } - else if (_property != null) - { - return _property.Alias; - } - - return null; - } + get { return _property.Alias; } } public object Value