Skip to content

Commit 4d4aff4

Browse files
Apply changes from #11805 and #11806 to v9 (#11904)
* Apply changes from #11805 and #11806 to v9 * Update documentation and cleanup code styling
1 parent 72533d2 commit 4d4aff4

16 files changed

+159
-150
lines changed

src/Umbraco.Core/Models/PropertyTagsExtensions.cs

Lines changed: 4 additions & 4 deletions
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 Microsoft.Extensions.Logging;
@@ -75,8 +75,7 @@ private static void AssignTags(this IProperty property, IEnumerable<string> tags
7575
var updatedTags = currentTags.Union(trimmedTags).ToArray();
7676
var updatedValue = updatedTags.Length == 0 ? null : serializer.Serialize(updatedTags);
7777
property.SetValue(updatedValue, culture); // json array
78-
break;
79-
property.SetValue(serializer.Serialize(currentTags.Union(trimmedTags).ToArray()), culture); // json array
78+
break;
8079
}
8180
}
8281
else
@@ -88,7 +87,8 @@ private static void AssignTags(this IProperty property, IEnumerable<string> tags
8887
break;
8988

9089
case TagsStorageType.Json:
91-
property.SetValue(serializer.Serialize(trimmedTags), culture); // json array
90+
var updatedValue = trimmedTags.Length == 0 ? null : serializer.Serialize(trimmedTags);
91+
property.SetValue(updatedValue, culture); // json array
9292
break;
9393
}
9494
}

src/Umbraco.Core/PropertyEditors/DataValueEditor.cs

Lines changed: 85 additions & 72 deletions
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.ComponentModel.DataAnnotations;
44
using System.Globalization;
@@ -149,84 +149,90 @@ public IEnumerable<ValidationResult> Validate(object value, bool required, strin
149149
public virtual bool IsReadOnly => false;
150150

151151
/// <summary>
152-
/// Used to try to convert the string value to the correct CLR type based on the DatabaseDataType specified for this value editor
152+
/// Used to try to convert the string value to the correct CLR type based on the <see cref="ValueType" /> specified for this value editor.
153153
/// </summary>
154-
/// <param name="value"></param>
155-
/// <returns></returns>
154+
/// <param name="value">The value.</param>
155+
/// <returns>
156+
/// The result of the conversion attempt.
157+
/// </returns>
158+
/// <exception cref="System.ArgumentOutOfRangeException">ValueType was out of range.</exception>
156159
internal Attempt<object> TryConvertValueToCrlType(object value)
157160
{
158-
// if (value is JValue)
159-
// value = value.ToString();
160-
161-
//this is a custom check to avoid any errors, if it's a string and it's empty just make it null
161+
// Ensure empty string values are converted to null
162162
if (value is string s && string.IsNullOrWhiteSpace(s))
163+
{
163164
value = null;
165+
}
164166

167+
// Ensure JSON is serialized properly (without indentation or converted to null when empty)
168+
if (value is not null && ValueType.InvariantEquals(ValueTypes.Json))
169+
{
170+
var jsonValue = _jsonSerializer.Serialize(value);
171+
172+
if (jsonValue.DetectIsEmptyJson())
173+
{
174+
value = null;
175+
}
176+
else
177+
{
178+
value = jsonValue;
179+
}
180+
}
181+
182+
// Convert the string to a known type
165183
Type valueType;
166-
//convert the string to a known type
167184
switch (ValueTypes.ToStorageType(ValueType))
168185
{
169186
case ValueStorageType.Ntext:
170187
case ValueStorageType.Nvarchar:
171188
valueType = typeof(string);
172189
break;
173-
case ValueStorageType.Integer:
174-
//ensure these are nullable so we can return a null if required
175-
//NOTE: This is allowing type of 'long' because I think json.net will deserialize a numerical value as long
176-
// instead of int. Even though our db will not support this (will get truncated), we'll at least parse to this.
177190

191+
case ValueStorageType.Integer:
192+
// Ensure these are nullable so we can return a null if required
193+
// NOTE: This is allowing type of 'long' because I think JSON.NEt will deserialize a numerical value as long instead of int
194+
// Even though our DB will not support this (will get truncated), we'll at least parse to this
178195
valueType = typeof(long?);
179196

180-
//if parsing is successful, we need to return as an Int, we're only dealing with long's here because of json.net, we actually
181-
//don't support long values and if we return a long value it will get set as a 'long' on the Property.Value (object) and then
182-
//when we compare the values for dirty tracking we'll be comparing an int -> long and they will not match.
197+
// If parsing is successful, we need to return as an int, we're only dealing with long's here because of JSON.NET,
198+
// we actually don't support long values and if we return a long value, it will get set as a 'long' on the Property.Value (object) and then
199+
// when we compare the values for dirty tracking we'll be comparing an int -> long and they will not match.
183200
var result = value.TryConvertTo(valueType);
201+
184202
return result.Success && result.Result != null
185203
? Attempt<object>.Succeed((int)(long)result.Result)
186204
: result;
187205

188206
case ValueStorageType.Decimal:
189-
//ensure these are nullable so we can return a null if required
207+
// Ensure these are nullable so we can return a null if required
190208
valueType = typeof(decimal?);
191209
break;
192210

193211
case ValueStorageType.Date:
194-
//ensure these are nullable so we can return a null if required
212+
// Ensure these are nullable so we can return a null if required
195213
valueType = typeof(DateTime?);
196214
break;
215+
197216
default:
198-
throw new ArgumentOutOfRangeException();
217+
throw new ArgumentOutOfRangeException("ValueType was out of range.");
199218
}
200219

201220
return value.TryConvertTo(valueType);
202221
}
203222

204-
/// <summary>
205-
/// A method to deserialize the string value that has been saved in the content editor
206-
/// to an object to be stored in the database.
207-
/// </summary>
208-
/// <param name="editorValue"></param>
209-
/// <param name="currentValue">
210-
/// The current value that has been persisted to the database for this editor. This value may be useful for
211-
/// how the value then get's deserialized again to be re-persisted. In most cases it will probably not be used.
212-
/// </param>
213-
/// <param name="languageId"></param>
214-
/// <param name="segment"></param>
215-
/// <returns></returns>
216-
/// <remarks>
217-
/// By default this will attempt to automatically convert the string value to the value type supplied by ValueType.
218-
///
219-
/// If overridden then the object returned must match the type supplied in the ValueType, otherwise persisting the
220-
/// value to the DB will fail when it tries to validate the value type.
221-
/// </remarks>
223+
/// <summary>
224+
/// A method to deserialize the string value that has been saved in the content editor to an object to be stored in the database.
225+
/// </summary>
226+
/// <param name="editorValue">The value returned by the editor.</param>
227+
/// <param name="currentValue">The current value that has been persisted to the database for this editor. This value may be useful for how the value then get's deserialized again to be re-persisted. In most cases it will probably not be used.</param>
228+
/// <returns>The value that gets persisted to the database.</returns>
229+
/// <remarks>
230+
/// By default this will attempt to automatically convert the string value to the value type supplied by ValueType.
231+
/// If overridden then the object returned must match the type supplied in the ValueType, otherwise persisting the
232+
/// value to the DB will fail when it tries to validate the value type.
233+
/// </remarks>
222234
public virtual object FromEditor(ContentPropertyData editorValue, object currentValue)
223235
{
224-
//if it's json but it's empty json, then return null
225-
if (ValueType.InvariantEquals(ValueTypes.Json) && editorValue.Value != null && editorValue.Value.ToString().DetectIsEmptyJson())
226-
{
227-
return null;
228-
}
229-
230236
var result = TryConvertValueToCrlType(editorValue.Value);
231237
if (result.Success == false)
232238
{
@@ -238,64 +244,71 @@ public virtual object FromEditor(ContentPropertyData editorValue, object current
238244
}
239245

240246
/// <summary>
241-
/// A method used to format the database value to a value that can be used by the editor
247+
/// A method used to format the database value to a value that can be used by the editor.
242248
/// </summary>
243-
/// <param name="property"></param>
244-
/// <param name="dataTypeService"></param>
245-
/// <param name="culture"></param>
246-
/// <param name="segment"></param>
249+
/// <param name="property">The property.</param>
250+
/// <param name="culture">The culture.</param>
251+
/// <param name="segment">The segment.</param>
247252
/// <returns></returns>
253+
/// <exception cref="System.ArgumentOutOfRangeException">ValueType was out of range.</exception>
248254
/// <remarks>
249-
/// The object returned will automatically be serialized into json notation. For most property editors
250-
/// the value returned is probably just a string but in some cases a json structure will be returned.
255+
/// The object returned will automatically be serialized into JSON notation. For most property editors
256+
/// the value returned is probably just a string, but in some cases a JSON structure will be returned.
251257
/// </remarks>
252258
public virtual object ToEditor(IProperty property, string culture = null, string segment = null)
253259
{
254-
var val = property.GetValue(culture, segment);
255-
if (val == null) return string.Empty;
260+
var value = property.GetValue(culture, segment);
261+
if (value == null)
262+
{
263+
return string.Empty;
264+
}
256265

257266
switch (ValueTypes.ToStorageType(ValueType))
258267
{
259268
case ValueStorageType.Ntext:
260269
case ValueStorageType.Nvarchar:
261-
//if it is a string type, we will attempt to see if it is json stored data, if it is we'll try to convert
262-
//to a real json object so we can pass the true json object directly to angular!
263-
var asString = val.ToString();
264-
if (asString.DetectIsJson())
270+
// If it is a string type, we will attempt to see if it is JSON stored data, if it is we'll try to convert
271+
// to a real JSON object so we can pass the true JSON object directly to Angular!
272+
var stringValue = value as string ?? value.ToString();
273+
if (stringValue.DetectIsJson())
265274
{
266275
try
267276
{
268-
var json = _jsonSerializer.Deserialize<dynamic>(asString);
269-
return json;
277+
return _jsonSerializer.Deserialize<dynamic>(stringValue);
270278
}
271279
catch
272280
{
273-
//swallow this exception, we thought it was json but it really isn't so continue returning a string
281+
// Swallow this exception, we thought it was JSON but it really isn't so continue returning a string
274282
}
275283
}
276-
return asString;
284+
285+
return stringValue;
286+
277287
case ValueStorageType.Integer:
278288
case ValueStorageType.Decimal:
279-
//Decimals need to be formatted with invariant culture (dots, not commas)
280-
//Anything else falls back to ToString()
281-
var decim = val.TryConvertTo<decimal>();
282-
return decim.Success
283-
? decim.Result.ToString(NumberFormatInfo.InvariantInfo)
284-
: val.ToString();
289+
// Decimals need to be formatted with invariant culture (dots, not commas)
290+
// Anything else falls back to ToString()
291+
var decimalValue = value.TryConvertTo<decimal>();
292+
293+
return decimalValue.Success
294+
? decimalValue.Result.ToString(NumberFormatInfo.InvariantInfo)
295+
: value.ToString();
296+
285297
case ValueStorageType.Date:
286-
var date = val.TryConvertTo<DateTime?>();
287-
if (date.Success == false || date.Result == null)
298+
var dateValue = value.TryConvertTo<DateTime?>();
299+
if (dateValue.Success == false || dateValue.Result == null)
288300
{
289301
return string.Empty;
290302
}
291-
//Dates will be formatted as yyyy-MM-dd HH:mm:ss
292-
return date.Result.Value.ToIsoString();
303+
304+
// Dates will be formatted as yyyy-MM-dd HH:mm:ss
305+
return dateValue.Result.Value.ToIsoString();
306+
293307
default:
294-
throw new ArgumentOutOfRangeException();
308+
throw new ArgumentOutOfRangeException("ValueType was out of range.");
295309
}
296310
}
297311

298-
299312
// TODO: the methods below should be replaced by proper property value convert ToXPath usage!
300313

301314
/// <summary>

src/Umbraco.Infrastructure/PropertyEditors/GridPropertyEditor.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
1-
// Copyright (c) Umbraco.
1+
// Copyright (c) Umbraco.
22
// See LICENSE for more details.
33

44
using System;
55
using System.Collections.Generic;
66
using System.Linq;
7-
using Microsoft.Extensions.Logging;
87
using Newtonsoft.Json;
9-
using Umbraco.Cms.Core.Hosting;
108
using Umbraco.Cms.Core.IO;
119
using Umbraco.Cms.Core.Media;
1210
using Umbraco.Cms.Core.Models;
@@ -153,7 +151,8 @@ public override object FromEditor(ContentPropertyData editorValue, object curren
153151
public override object ToEditor(IProperty property, string culture = null, string segment = null)
154152
{
155153
var val = property.GetValue(culture, segment)?.ToString();
156-
if (val.IsNullOrWhiteSpace()) return string.Empty;
154+
if (val.IsNullOrWhiteSpace())
155+
return string.Empty;
157156

158157
var grid = DeserializeGridValue(val, out var rtes, out _);
159158

@@ -199,7 +198,7 @@ public IEnumerable<UmbracoEntityReference> GetReferences(object value)
199198
_richTextPropertyValueEditor.GetReferences(x.Value)))
200199
yield return umbracoEntityReference;
201200

202-
foreach (var umbracoEntityReference in mediaValues.Where(x=>x.Value.HasValues)
201+
foreach (var umbracoEntityReference in mediaValues.Where(x => x.Value.HasValues)
203202
.SelectMany(x => _mediaPickerPropertyValueEditor.GetReferences(x.Value["udi"])))
204203
yield return umbracoEntityReference;
205204
}

src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ public void Handle(ContentCopiedNotification notification)
208208
{
209209
continue;
210210
}
211+
211212
var sourcePath = _mediaFileManager.FileSystem.GetRelativePath(src);
212213
var copyPath = _mediaFileManager.CopyFile(notification.Copy, property.PropertyType, sourcePath);
213214
jo["src"] = _mediaFileManager.FileSystem.GetUrl(copyPath);
@@ -273,10 +274,8 @@ private void AutoFillProperties(IContentBase model)
273274
// the property value will be the file source eg '/media/23454/hello.jpg' and we
274275
// are fixing that anomaly here - does not make any sense at all but... bah...
275276
src = svalue;
276-
property.SetValue(JsonConvert.SerializeObject(new
277-
{
278-
src = svalue
279-
}, Formatting.None), pvalue.Culture, pvalue.Segment);
277+
278+
property.SetValue(JsonConvert.SerializeObject(new { src = svalue }, Formatting.None), pvalue.Culture, pvalue.Segment);
280279
}
281280
else
282281
{

0 commit comments

Comments
 (0)