Skip to content

Commit 80a1676

Browse files
jericyuenJeavonAndyButland
authored
Added DTGE cell value connector
* feat: Added in code for the DTGE Cell value connector * Fixes to a RTE, Textstring, Form Picker in DTGE connector * Fixes to Checkbox values within NestedContent Connector which breaks when you restore * Removing Nested Content Value Connector as that's supported by Deploy now * Revert "Removing Nested Content Value Connector as that's supported by Deploy now" This reverts commit 3d3f644. * Reset Co-authored-by: Jeavon Leopold <[email protected]> Co-authored-by: Andy Butland <[email protected]>
1 parent a6d8238 commit 80a1676

File tree

2 files changed

+315
-1
lines changed

2 files changed

+315
-1
lines changed
Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,314 @@
1+

2+
namespace Umbraco.Deploy.Contrib.Connectors.GridCellValueConnectors
3+
{
4+
using Deploy.Connectors.ValueConnectors.Services;
5+
using Newtonsoft.Json;
6+
using Newtonsoft.Json.Linq;
7+
using System;
8+
using System.Collections.Generic;
9+
using Umbraco.Core;
10+
using Umbraco.Core.Deploy;
11+
using Umbraco.Core.Logging;
12+
using Umbraco.Core.Models;
13+
using Umbraco.Core.Services;
14+
15+
public class DocTypeGridEditorCellValueConnector : IGridCellValueConnector
16+
{
17+
private readonly ILogger _logger;
18+
private readonly IContentTypeService _contentTypeService;
19+
private readonly Lazy<ValueConnectorCollection> _valueConnectorsLazy;
20+
private ValueConnectorCollection ValueConnectors => _valueConnectorsLazy.Value;
21+
22+
public DocTypeGridEditorCellValueConnector(ILogger logger, IContentTypeService contentTypeService, Lazy<ValueConnectorCollection> valueConnectors)
23+
{
24+
this._logger = logger;
25+
this._contentTypeService = contentTypeService ?? throw new ArgumentNullException(nameof(contentTypeService));
26+
this._valueConnectorsLazy = valueConnectors ?? throw new ArgumentNullException(nameof(valueConnectors));
27+
}
28+
29+
public bool IsConnector(string view)
30+
{
31+
return !string.IsNullOrWhiteSpace(view) && view.Contains("doctypegrideditor");
32+
}
33+
34+
public string GetValue(GridValue.GridControl gridControl, ICollection<ArtifactDependency> dependencies)
35+
{
36+
// cancel if there's no values
37+
if (gridControl.Value == null || gridControl.Value.HasValues == false) return null;
38+
39+
this._logger.Debug<DocTypeGridEditorCellValueConnector>($"GetValue - Grid Values: {gridControl.Value}");
40+
41+
var docTypeGridEditorContent = JsonConvert.DeserializeObject<DocTypeGridEditorValue>(gridControl.Value.ToString());
42+
43+
44+
// if an 'empty' dtge item has been added - it has no ContentTypeAlias set .. just return and don't throw.
45+
if (docTypeGridEditorContent == null || string.IsNullOrWhiteSpace(docTypeGridEditorContent.ContentTypeAlias))
46+
{
47+
this._logger.Debug<DocTypeGridEditorCellValueConnector>("GetValue - DTGE Empty without ContentTypeAlias");
48+
return null;
49+
}
50+
51+
this._logger.Debug<DocTypeGridEditorCellValueConnector>($"GetValue - ContentTypeAlias - {docTypeGridEditorContent.ContentTypeAlias}");
52+
53+
// check if the doc type exist - else abort packaging
54+
var contentType = this._contentTypeService.Get(docTypeGridEditorContent.ContentTypeAlias);
55+
56+
if (contentType == null)
57+
{
58+
this._logger.Debug<DocTypeGridEditorCellValueConnector>("GetValue - Missing ContentType");
59+
throw new InvalidOperationException($"Could not resolve the Content Type for the Doc Type Grid Editor property: {docTypeGridEditorContent.ContentTypeAlias}");
60+
}
61+
62+
// add content type as a dependency
63+
dependencies.Add(new ArtifactDependency(contentType.GetUdi(), false, ArtifactDependencyMode.Match));
64+
65+
// find all properties
66+
var propertyTypes = contentType.CompositionPropertyTypes;
67+
68+
foreach (var propertyType in propertyTypes)
69+
{
70+
this._logger.Debug<DocTypeGridEditorCellValueConnector>($"GetValue - PropertyTypeAlias - {propertyType.Alias}");
71+
72+
// test if there's a value for the given property
73+
if (this.IsValueNull(docTypeGridEditorContent, propertyType, out var value)) continue;
74+
75+
// if the value is an Udi then add it as a dependency
76+
if (this.AddUdiDependency(dependencies, value)) continue;
77+
78+
JToken jtokenValue = null;
79+
var parsedValue = string.Empty;
80+
81+
// throws if not found - no need for a null check
82+
var propValueConnector = this.ValueConnectors.Get(propertyType);
83+
84+
this._logger.Debug<DocTypeGridEditorCellValueConnector>($"GetValue - PropertyValueConnectorAlias - {propValueConnector.PropertyEditorAliases}");
85+
this._logger.Debug<DocTypeGridEditorCellValueConnector>($"GetValue - propertyTypeValue - {value}");
86+
87+
//properties like MUP / Nested Content seems to be in json and it breaks, so pass it back as jtokenValue right away
88+
if (this.IsJson(value))
89+
{
90+
jtokenValue = this.GetJTokenValue(value);
91+
this._logger.Debug<DocTypeGridEditorCellValueConnector>($"GetValue - Inner JtokenValue - Eg MUP/NestedContent {jtokenValue}");
92+
}
93+
else
94+
{
95+
parsedValue = propValueConnector.ToArtifact(value, propertyType, dependencies);
96+
97+
this._logger.Debug<DocTypeGridEditorCellValueConnector>($"GetValue - ParsedValue - {parsedValue}");
98+
99+
if (this.IsJson(parsedValue))
100+
{
101+
// if that's the case we'll need to add it as a json object instead of string to avoid it being escaped
102+
jtokenValue = this.GetJTokenValue(parsedValue);
103+
}
104+
}
105+
106+
if (jtokenValue != null)
107+
{
108+
this._logger.Debug<DocTypeGridEditorCellValueConnector>($"GetValue - JtokenValue - {jtokenValue}");
109+
docTypeGridEditorContent.Value[propertyType.Alias] = jtokenValue;
110+
}
111+
else
112+
{
113+
docTypeGridEditorContent.Value[propertyType.Alias] = parsedValue;
114+
}
115+
}
116+
117+
var resolvedValue = JsonConvert.SerializeObject(docTypeGridEditorContent);
118+
119+
this._logger.Debug<DocTypeGridEditorCellValueConnector>($"GetValue - ResolvedValue - {resolvedValue}");
120+
121+
return resolvedValue;
122+
}
123+
124+
public void SetValue(GridValue.GridControl gridControl)
125+
{
126+
// cancel if there's no values
127+
if (string.IsNullOrWhiteSpace(gridControl.Value.ToString())) return;
128+
129+
// For some reason the control value isn't properly parsed so we need this extra step to parse it into a JToken
130+
gridControl.Value = JToken.Parse(gridControl.Value.ToString());
131+
132+
this._logger.Debug<DocTypeGridEditorCellValueConnector>($"SetValue - GridControlValue - {gridControl.Value}");
133+
134+
var docTypeGridEditorContent = JsonConvert.DeserializeObject<DocTypeGridEditorValue>(gridControl.Value.ToString());
135+
136+
if (docTypeGridEditorContent == null) return;
137+
138+
this._logger.Debug<DocTypeGridEditorCellValueConnector>($"SetValue - ContentTypeAlias - {docTypeGridEditorContent.ContentTypeAlias}");
139+
140+
// check if the doc type exist - else abort packaging
141+
var contentType = this._contentTypeService.Get(docTypeGridEditorContent.ContentTypeAlias);
142+
143+
if (contentType == null)
144+
throw new InvalidOperationException($"Could not resolve the Content Type for the Doc Type Grid Editor property: {docTypeGridEditorContent.ContentTypeAlias}");
145+
146+
147+
this._logger.Debug<DocTypeGridEditorCellValueConnector>($"SetValue - ContentType - {contentType}");
148+
149+
150+
// find all properties
151+
var propertyTypes = contentType.CompositionPropertyTypes;
152+
153+
foreach (var propertyType in propertyTypes)
154+
{
155+
// test if there's a value for the given property
156+
object value;
157+
object convertedValue;
158+
JToken jtokenValue = null;
159+
160+
this._logger.Debug<DocTypeGridEditorCellValueConnector>($"SetValue - PropertyEditorAlias -- {propertyType.PropertyEditorAlias}");
161+
162+
if (!docTypeGridEditorContent.Value.TryGetValue(propertyType.Alias, out value) || value == null)
163+
{
164+
this._logger.Debug<DocTypeGridEditorCellValueConnector>("SetValue - Value Is Null");
165+
continue;
166+
}
167+
168+
// throws if not found - no need for a null check
169+
var propValueConnector = this.ValueConnectors.Get(propertyType);
170+
propValueConnector.FromArtifact(value.ToString(), propertyType, "");
171+
172+
this._logger.Debug<DocTypeGridEditorCellValueConnector>($"SetValue - PropertyValueConnecterType - {propValueConnector.GetType()}");
173+
this._logger.Debug<DocTypeGridEditorCellValueConnector>($"SetValue - Value - {value}");
174+
175+
//don't convert if it's json (MUP/Nested) / udi (Content/Media/Multi Pickers) / guid (form picker) / rte / textstring values
176+
if (this.IsJson(value) || this.IsUdi(value) || this.IsGuid(value) || this.IsText(propertyType.PropertyEditorAlias))
177+
{
178+
this._logger.Debug<DocTypeGridEditorCellValueConnector>($"SetValue - IsJsonValue / IsUdiValue / IsGuidValue / IsTextValue - {value}");
179+
convertedValue = this.CleanValue(propertyType.PropertyEditorAlias, value);
180+
}
181+
else
182+
{
183+
//using mockContent to get the converted values
184+
var mockProperty = new Property(propertyType);
185+
var mockContent = new Content("mockContentGrid", -1, new ContentType(-1), new PropertyCollection(new List<Property> { mockProperty }));
186+
convertedValue = mockContent.GetValue(mockProperty.Alias);
187+
this._logger.Debug<DocTypeGridEditorCellValueConnector>($"SetValue - ConvertedValue - Before - {convertedValue}");
188+
}
189+
190+
// integers needs to be converted into strings for DTGE to work
191+
if (convertedValue is int)
192+
{
193+
docTypeGridEditorContent.Value[propertyType.Alias] = convertedValue.ToString();
194+
}
195+
else if (convertedValue == null)
196+
{
197+
//Assign the null back - otherwise the check for JSON will fail as we cant convert a null to a string
198+
//NOTE: LinkPicker2 for example if no link set is returning a null as opposed to empty string
199+
docTypeGridEditorContent.Value[propertyType.Alias] = null;
200+
}
201+
else
202+
{
203+
this._logger.Debug<DocTypeGridEditorCellValueConnector>($"SetValue - ConvertedValue - After - {convertedValue}");
204+
205+
if (this.IsJson(convertedValue))
206+
{
207+
// test if the value is a json object (thus could be a nested complex editor)
208+
// if that's the case we'll need to add it as a json object instead of string to avoid it being escaped
209+
jtokenValue = this.GetJTokenValue(convertedValue);
210+
}
211+
212+
if (jtokenValue != null)
213+
{
214+
this._logger.Debug<DocTypeGridEditorCellValueConnector>($"SetValue - jtokenValue - {jtokenValue}");
215+
docTypeGridEditorContent.Value[propertyType.Alias] = jtokenValue;
216+
}
217+
else
218+
{
219+
docTypeGridEditorContent.Value[propertyType.Alias] = convertedValue;
220+
}
221+
}
222+
}
223+
224+
var jtokenObj = JToken.FromObject(docTypeGridEditorContent);
225+
this._logger.Debug<DocTypeGridEditorCellValueConnector>($"SetValue - jtokenObject - {jtokenObj}");
226+
gridControl.Value = jtokenObj;
227+
}
228+
229+
#region Private Methods
230+
231+
private JToken GetJTokenValue(object value)
232+
{
233+
return value != null && this.IsJson(value) ? JToken.Parse(value.ToString()) : null;
234+
}
235+
236+
private bool IsJson(object value)
237+
{
238+
return value != null && value.ToString().DetectIsJson();
239+
}
240+
241+
private bool IsGuid(object value)
242+
{
243+
return Guid.TryParse(value.ToString(), out var guidID);
244+
}
245+
246+
private bool IsUdi(object value)
247+
{
248+
//for picker like content/media either single or multi, it comes with udi values
249+
return value.ToString().Contains("umb://");
250+
}
251+
252+
private bool IsText(string editorAlias)
253+
{
254+
//if it's either RTE / Textstring data type
255+
return this.IsRichtext(editorAlias) || this.IsTextstring(editorAlias);
256+
}
257+
258+
private bool IsRichtext(string editorAlias)
259+
{
260+
return editorAlias.InvariantEquals("Umbraco.TinyMCE");
261+
}
262+
263+
private bool IsTextstring(string editorAlias)
264+
{
265+
return editorAlias.InvariantEquals("Umbraco.TextBox");
266+
}
267+
268+
private string CleanValue(string editorAlias, object value)
269+
{
270+
//can't tell why textstring have got a weird character 's' in front coming from deploy? so removing first char if that's the case
271+
return this.IsTextstring(editorAlias) ? value.ToString().Substring(1) : value.ToString();
272+
}
273+
274+
private bool AddUdiDependency(ICollection<ArtifactDependency> dependencies, object value)
275+
{
276+
if (Udi.TryParse(value.ToString(), out var udi))
277+
{
278+
this._logger.Debug<DocTypeGridEditorCellValueConnector>($"GetValue - Udi Dependency - {udi}");
279+
dependencies.Add(new ArtifactDependency(udi, false, ArtifactDependencyMode.Match));
280+
return true;
281+
}
282+
283+
return false;
284+
}
285+
286+
private bool IsValueNull(DocTypeGridEditorValue docTypeGridEditorContent, PropertyType propertyType, out object objVal)
287+
{
288+
if (!docTypeGridEditorContent.Value.TryGetValue(propertyType.Alias, out objVal) || objVal == null)
289+
{
290+
this._logger.Debug<DocTypeGridEditorCellValueConnector>("GetValue - Value is null");
291+
return true;
292+
}
293+
294+
return false;
295+
}
296+
297+
#endregion
298+
299+
#region DocTypeGridEditor model
300+
private class DocTypeGridEditorValue
301+
{
302+
[JsonProperty("id")]
303+
public Guid Id { get; set; }
304+
305+
[JsonProperty("dtgeContentTypeAlias")]
306+
public string ContentTypeAlias { get; set; }
307+
308+
[JsonProperty("value")]
309+
public Dictionary<string, object> Value { get; set; }
310+
}
311+
312+
#endregion
313+
}
314+
}

src/Umbraco.Deploy.Contrib.Connectors/ValueConnectors/NestedContentValueConnector.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22
using System.Collections.Generic;
33
using System.Linq;
44
using Newtonsoft.Json;

0 commit comments

Comments
 (0)