Skip to content

Commit 3e5e8b9

Browse files
author
Claus
committed
Merge branch 'umco-feature/propertylist-connector' into dev
2 parents 6cf4b82 + a36b94c commit 3e5e8b9

File tree

5 files changed

+313
-0
lines changed

5 files changed

+313
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ This project offers Umbraco Deploy connectors for the following community packag
2020
- [nuPickers](https://our.umbraco.org/projects/backoffice-extensions/nupickers/)
2121
- [Archetype](https://our.umbraco.org/projects/backoffice-extensions/archetype/)
2222
- [Content List](https://github.com/umco/umbraco-content-list)
23+
- [Property List](https://github.com/umco/umbraco-property-list)
2324

2425
---
2526

src/Umbraco.Deploy.Contrib.Connectors/Umbraco.Deploy.Contrib.Connectors.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@
240240
<Compile Include="ValueConnectors\ArchetypeValueConnector.cs" />
241241
<Compile Include="ValueConnectors\MultiUrlPickerValueConnector.cs" />
242242
<Compile Include="ValueConnectors\NuPickersValueConnector.cs" />
243+
<Compile Include="ValueConnectors\PropertyListValueConnector.cs" />
243244
<Compile Include="ValueConnectors\RelatedLinksValueConnector.cs" />
244245
<Compile Include="ValueConnectors\RelatedLinks2ValueConnector.cs" />
245246
<Compile Include="ValueConnectors\ContentListConnector.cs" />
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Newtonsoft.Json;
4+
using Newtonsoft.Json.Linq;
5+
using Umbraco.Core;
6+
using Umbraco.Core.Deploy;
7+
using Umbraco.Core.Models;
8+
using Umbraco.Core.Services;
9+
using Umbraco.Deploy.ValueConnectors;
10+
11+
namespace Umbraco.Deploy.Contrib.Connectors.ValueConnectors
12+
{
13+
public class PropertyListValueConnector : IValueConnector
14+
{
15+
private readonly IDataTypeService _dataTypeService;
16+
17+
private readonly Lazy<ValueConnectorCollection> _valueConnectorsLazy;
18+
19+
public PropertyListValueConnector(IDataTypeService dataTypeService, Lazy<ValueConnectorCollection> valueConnectors)
20+
{
21+
Mandate.ParameterNotNull(dataTypeService, nameof(dataTypeService));
22+
Mandate.ParameterNotNull(valueConnectors, nameof(valueConnectors));
23+
24+
_dataTypeService = dataTypeService;
25+
_valueConnectorsLazy = valueConnectors;
26+
}
27+
28+
public IEnumerable<string> PropertyEditorAliases => new[] { "Our.Umbraco.PropertyList" };
29+
30+
private ValueConnectorCollection ValueConnectors => _valueConnectorsLazy.Value;
31+
32+
public string GetValue(Property property, ICollection<ArtifactDependency> dependencies)
33+
{
34+
// get the property value
35+
var value = property.Value?.ToString();
36+
if (string.IsNullOrWhiteSpace(value))
37+
return null;
38+
39+
// deserialize it
40+
var model = JsonConvert.DeserializeObject<PropertyListValue>(value);
41+
if (model == null)
42+
return null;
43+
44+
// get the selected data-type (and ensure it exists)
45+
var dataType = _dataTypeService.GetDataTypeDefinitionById(model.DataTypeGuid);
46+
47+
if (dataType == null)
48+
throw new InvalidOperationException($"Could not resolve the data-type used by the Property List value for: {property.Alias}");
49+
50+
// add the selected data-type as a dependency
51+
dependencies.Add(new ArtifactDependency(dataType.GetUdi(), false, ArtifactDependencyMode.Match));
52+
53+
// make a property-type to use in a mocked Property
54+
// and get the value-connector needed to parse values (outside the loop, as it's the same for all iterations)
55+
var propertyType = new PropertyType(dataType);
56+
var valueConnector = ValueConnectors.Get(propertyType);
57+
58+
// loop through each value
59+
for (int i = 0; i < model.Values.Count; i++)
60+
{
61+
// pass it to its own value-connector
62+
// set the parsed value back onto the original object, (it may be a string representing more json. which is fine)
63+
model.Values[i] = valueConnector.GetValue(new Property(propertyType, model.Values[i]), dependencies);
64+
}
65+
66+
return JsonConvert.SerializeObject(model);
67+
}
68+
69+
public void SetValue(IContentBase content, string alias, string value)
70+
{
71+
// take the value
72+
if (string.IsNullOrWhiteSpace(value))
73+
return;
74+
75+
// deserialize it
76+
var model = JsonConvert.DeserializeObject<PropertyListValue>(value);
77+
if (model == null)
78+
return;
79+
80+
// get the selected data-type (and ensure it exists)
81+
var dataType = _dataTypeService.GetDataTypeDefinitionById(model.DataTypeGuid);
82+
83+
if (dataType == null)
84+
throw new InvalidOperationException($"Could not resolve the data-type used by the Property List value for: {alias}");
85+
86+
// make a property-type to use in a mocked Property
87+
// and get the value-connector needed to parse values (outside the loop, as it's the same for all iterations)
88+
var propertyType = new PropertyType(dataType, "mockPropertyListAlias");
89+
var valueConnector = ValueConnectors.Get(propertyType);
90+
91+
// loop through each value
92+
for (int i = 0; i < model.Values.Count; i++)
93+
{
94+
var item = model.Values[i];
95+
96+
var mockProperty = new Property(propertyType);
97+
var mockContent = new Content("mockContent", -1, new ContentType(-1), new PropertyCollection(new List<Property> { mockProperty }));
98+
99+
// pass it to its own value-connector
100+
// NOTE: due to how ValueConnector.SetValue() works, we have to pass the mock item
101+
// through to the connector to have it do its work on parsing the value on the item itself.
102+
valueConnector.SetValue(mockContent, mockProperty.Alias, item?.ToString());
103+
104+
// get the value back and assign
105+
model.Values[i] = mockContent.GetValue(mockProperty.Alias);
106+
}
107+
108+
// serialize the JSON values
109+
content.SetValue(alias, JObject.FromObject(model).ToString(Formatting.None));
110+
}
111+
112+
public class PropertyListValue
113+
{
114+
[JsonProperty("dtd")]
115+
public Guid DataTypeGuid { get; set; }
116+
117+
[JsonProperty("values")]
118+
public List<object> Values { get; set; }
119+
}
120+
}
121+
}
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using Moq;
5+
using NUnit.Framework;
6+
using Umbraco.Core;
7+
using Umbraco.Core.Configuration;
8+
using Umbraco.Core.Configuration.UmbracoSettings;
9+
using Umbraco.Core.Deploy;
10+
using Umbraco.Core.Models;
11+
using Umbraco.Core.Services;
12+
using Umbraco.Deploy.Contrib.Connectors.ValueConnectors;
13+
using Umbraco.Deploy.ValueConnectors;
14+
15+
namespace Umbraco.Deploy.Contrib.Tests.Connectors
16+
{
17+
[TestFixture]
18+
public class PropertyListValueConnectorTests
19+
{
20+
[Test]
21+
public void GetValueTest()
22+
{
23+
var dataTypeService = Mock.Of<IDataTypeService>();
24+
25+
var propListDataType = new DataTypeDefinition("propListEditorAlias")
26+
{
27+
Id = 1,
28+
Key = Guid.Parse("74AFF355-537A-4443-9801-C131FE83FF1F"),
29+
DatabaseType = DataTypeDatabaseType.Ntext
30+
};
31+
32+
var innerDataType = new DataTypeDefinition("innerEditorAlias")
33+
{
34+
Id = 2,
35+
Key = Guid.Parse("D21BA417-98AC-4D05-8EF9-0ED3D75A8C0D"),
36+
DatabaseType = DataTypeDatabaseType.Integer // for true/false
37+
};
38+
39+
var dataTypes = new[] { propListDataType, innerDataType };
40+
41+
Mock.Get(dataTypeService)
42+
.Setup(x => x.GetDataTypeDefinitionById(It.IsAny<Guid>()))
43+
.Returns<Guid>(id => dataTypes.FirstOrDefault(x => x.Key == id));
44+
45+
var preValues = new Dictionary<int, PreValueCollection>
46+
{
47+
{ 1, new PreValueCollection(new Dictionary<string, PreValue>
48+
{
49+
{ "dataType", new PreValue(innerDataType.Key.ToString()) }
50+
})
51+
}
52+
};
53+
54+
Mock.Get(dataTypeService)
55+
.Setup(x => x.GetPreValuesCollectionByDataTypeId(It.IsAny<int>()))
56+
.Returns<int>(id => preValues.TryGetValue(id, out var collection) ? collection : null);
57+
58+
ValueConnectorCollection connectors = null;
59+
var defaultConnector = new DefaultValueConnector();
60+
var propListConnector = new PropertyListValueConnector(dataTypeService, new Lazy<ValueConnectorCollection>(() => connectors));
61+
connectors = new ValueConnectorCollection(new Dictionary<string, IValueConnector>
62+
{
63+
{ "innerEditorAlias", defaultConnector },
64+
{ "propListEditorAlias", propListConnector }
65+
});
66+
67+
var input = $"{{\"dtd\":\"{innerDataType.Key}\",\"values\":[0]}}";
68+
69+
var propertyType = new PropertyType(propListDataType);
70+
var property = new Property(propertyType, input);
71+
var dependencies = new List<ArtifactDependency>();
72+
var output = propListConnector.GetValue(property, dependencies);
73+
74+
Console.WriteLine(output);
75+
76+
var expected = $"{{\"dtd\":\"{innerDataType.Key}\",\"values\":[\"i0\"]}}";
77+
Assert.AreEqual(expected, output);
78+
}
79+
80+
[Test]
81+
public void SetValueTest()
82+
{
83+
var dataTypeService = Mock.Of<IDataTypeService>();
84+
85+
var propListDataType = new DataTypeDefinition("propListEditorAlias")
86+
{
87+
Id = 1,
88+
Key = Guid.Parse("74AFF355-537A-4443-9801-C131FE83FF1F"),
89+
DatabaseType = DataTypeDatabaseType.Ntext
90+
};
91+
92+
var innerDataType = new DataTypeDefinition("innerEditorAlias")
93+
{
94+
Id = 2,
95+
Key = Guid.Parse("D21BA417-98AC-4D05-8EF9-0ED3D75A8C0D"),
96+
DatabaseType = DataTypeDatabaseType.Integer // for true/false
97+
};
98+
99+
var dataTypes = new[] { propListDataType, innerDataType };
100+
101+
Mock.Get(dataTypeService)
102+
.Setup(x => x.GetDataTypeDefinitionById(It.IsAny<Guid>()))
103+
.Returns<Guid>(id => dataTypes.FirstOrDefault(x => x.Key == id));
104+
105+
var preValues = new Dictionary<int, PreValueCollection>
106+
{
107+
{ 1, new PreValueCollection(new Dictionary<string, PreValue>
108+
{
109+
{ "dataType", new PreValue(innerDataType.Key.ToString()) }
110+
})
111+
}
112+
};
113+
114+
Mock.Get(dataTypeService)
115+
.Setup(x => x.GetPreValuesCollectionByDataTypeId(It.IsAny<int>()))
116+
.Returns<int>(id => preValues.TryGetValue(id, out var collection) ? collection : null);
117+
118+
ValueConnectorCollection connectors = null;
119+
var defaultConnector = new DefaultValueConnector();
120+
var propListConnector = new PropertyListValueConnector(dataTypeService, new Lazy<ValueConnectorCollection>(() => connectors));
121+
connectors = new ValueConnectorCollection(new Dictionary<string, IValueConnector>
122+
{
123+
{ "innerEditorAlias", defaultConnector },
124+
{ "propListEditorAlias", propListConnector }
125+
});
126+
127+
var input = $"{{\"dtd\":\"{innerDataType.Key}\",\"values\":[\"i0\"]}}";
128+
129+
UmbracoConfig.For.SetUmbracoSettings(GenerateMockSettings());
130+
131+
var propListPropertyType = new PropertyType(propListDataType, "propListProperty");
132+
var propListProperty = new Property(propListPropertyType, null); // value is going to be replaced
133+
var propListContent = new Content("mockContent", -1, new ContentType(-1), new PropertyCollection(new List<Property> { propListProperty }));
134+
propListConnector.SetValue(propListContent, "propListProperty", input);
135+
136+
var output = propListContent.GetValue("propListProperty");
137+
138+
Assert.IsInstanceOf<string>(output);
139+
140+
Console.WriteLine(output);
141+
142+
var expected = $"{{\"dtd\":\"{innerDataType.Key}\",\"values\":[0]}}";
143+
Assert.AreEqual(expected, output);
144+
}
145+
146+
public static IUmbracoSettingsSection GenerateMockSettings()
147+
{
148+
var settings = new Mock<IUmbracoSettingsSection>();
149+
150+
var content = new Mock<IContentSection>();
151+
var security = new Mock<ISecuritySection>();
152+
var requestHandler = new Mock<IRequestHandlerSection>();
153+
var templates = new Mock<ITemplatesSection>();
154+
var dev = new Mock<IDeveloperSection>();
155+
var logging = new Mock<ILoggingSection>();
156+
var tasks = new Mock<IScheduledTasksSection>();
157+
var distCall = new Mock<IDistributedCallSection>();
158+
var repos = new Mock<IRepositoriesSection>();
159+
var providers = new Mock<IProvidersSection>();
160+
var routing = new Mock<IWebRoutingSection>();
161+
162+
settings.Setup(x => x.Content).Returns(content.Object);
163+
settings.Setup(x => x.Security).Returns(security.Object);
164+
settings.Setup(x => x.RequestHandler).Returns(requestHandler.Object);
165+
settings.Setup(x => x.Templates).Returns(templates.Object);
166+
settings.Setup(x => x.Developer).Returns(dev.Object);
167+
settings.Setup(x => x.Logging).Returns(logging.Object);
168+
settings.Setup(x => x.ScheduledTasks).Returns(tasks.Object);
169+
settings.Setup(x => x.DistributedCall).Returns(distCall.Object);
170+
settings.Setup(x => x.PackageRepositories).Returns(repos.Object);
171+
settings.Setup(x => x.Providers).Returns(providers.Object);
172+
settings.Setup(x => x.WebRouting).Returns(routing.Object);
173+
174+
//Now configure some defaults - the defaults in the config section classes do NOT pertain to the mocked data!!
175+
settings.Setup(x => x.Content.ForceSafeAliases).Returns(true);
176+
//settings.Setup(x => x.Content.ImageAutoFillProperties).Returns(ContentImagingElement.GetDefaultImageAutoFillProperties());
177+
//settings.Setup(x => x.Content.ImageFileTypes).Returns(ContentImagingElement.GetDefaultImageFileTypes());
178+
settings.Setup(x => x.RequestHandler.AddTrailingSlash).Returns(true);
179+
settings.Setup(x => x.RequestHandler.UseDomainPrefixes).Returns(false);
180+
//settings.Setup(x => x.RequestHandler.CharCollection).Returns(RequestHandlerElement.GetDefaultCharReplacements());
181+
settings.Setup(x => x.Content.UmbracoLibraryCacheDuration).Returns(1800);
182+
settings.Setup(x => x.WebRouting.UrlProviderMode).Returns("AutoLegacy");
183+
settings.Setup(x => x.Templates.DefaultRenderingEngine).Returns(RenderingEngine.Mvc);
184+
settings.Setup(x => x.Providers.DefaultBackOfficeUserProvider).Returns("UsersMembershipProvider");
185+
186+
return settings.Object;
187+
}
188+
}
189+
}

src/Umbraco.Deploy.Contrib.Tests/Umbraco.Deploy.Contrib.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@
260260
<Compile Include="Connectors\LeBlenderGridCellValueConnectorTests.cs" />
261261
<Compile Include="Connectors\NestedContentValueConnectorTests.cs" />
262262
<Compile Include="Connectors\UrlPickerValueConnectorTests.cs" />
263+
<Compile Include="Connectors\PropertyListValueConnectorTests.cs" />
263264
<Compile Include="Connectors\VortoValueConnectorTests.cs" />
264265
<Compile Include="Properties\AssemblyInfo.cs" />
265266
<Compile Include="TestHelpers\MemoryFileTypeCollection.cs" />

0 commit comments

Comments
 (0)