* Ensure Name and Children can never return null. * Name should be empty not null * Ensure Name and Children type queries never return null Co-authored-by: Sebastiaan Janssen <sebastiaan@umbraco.com>
This commit is contained in:
@@ -25,7 +25,7 @@ public static class PublishedContentExtensions
|
||||
/// The specific culture to get the name for. If null is used the current culture is used (Default is
|
||||
/// null).
|
||||
/// </param>
|
||||
public static string? Name(this IPublishedContent content, IVariationContextAccessor? variationContextAccessor, string? culture = null)
|
||||
public static string Name(this IPublishedContent content, IVariationContextAccessor? variationContextAccessor, string? culture = null)
|
||||
{
|
||||
if (content == null)
|
||||
{
|
||||
@@ -37,7 +37,7 @@ public static class PublishedContentExtensions
|
||||
{
|
||||
return content.Cultures.TryGetValue(string.Empty, out PublishedCultureInfo? invariantInfos)
|
||||
? invariantInfos.Name
|
||||
: null;
|
||||
: string.Empty;
|
||||
}
|
||||
|
||||
// handle context culture for variant
|
||||
@@ -49,7 +49,7 @@ public static class PublishedContentExtensions
|
||||
// get
|
||||
return culture != string.Empty && content.Cultures.TryGetValue(culture, out PublishedCultureInfo? infos)
|
||||
? infos.Name
|
||||
: null;
|
||||
: string.Empty;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -683,8 +683,8 @@ public static class PublishedContentExtensions
|
||||
/// <param name="maxLevel">The level.</param>
|
||||
/// <returns>The content or its nearest (in down-top order) ancestor, at a level lesser or equal to the specified level.</returns>
|
||||
/// <remarks>May or may not return the content itself depending on its level. May return <c>null</c>.</remarks>
|
||||
public static IPublishedContent? AncestorOrSelf(this IPublishedContent content, int maxLevel) =>
|
||||
content.EnumerateAncestors(true).FirstOrDefault(x => x.Level <= maxLevel);
|
||||
public static IPublishedContent AncestorOrSelf(this IPublishedContent content, int maxLevel) =>
|
||||
content.EnumerateAncestors(true).FirstOrDefault(x => x.Level <= maxLevel) ?? content;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the content or its nearest ancestor, of a specified content type.
|
||||
@@ -693,8 +693,8 @@ public static class PublishedContentExtensions
|
||||
/// <param name="contentTypeAlias">The content type.</param>
|
||||
/// <returns>The content or its nearest (in down-top order) ancestor, of the specified content type.</returns>
|
||||
/// <remarks>May or may not return the content itself depending on its content type. May return <c>null</c>.</remarks>
|
||||
public static IPublishedContent? AncestorOrSelf(this IPublishedContent content, string contentTypeAlias) => content
|
||||
.EnumerateAncestors(true).FirstOrDefault(x => x.ContentType.Alias.InvariantEquals(contentTypeAlias));
|
||||
public static IPublishedContent AncestorOrSelf(this IPublishedContent content, string contentTypeAlias) => content
|
||||
.EnumerateAncestors(true).FirstOrDefault(x => x.ContentType.Alias.InvariantEquals(contentTypeAlias)) ?? content;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the content or its nearest ancestor, of a specified content type.
|
||||
@@ -1012,7 +1012,7 @@ public static class PublishedContentExtensions
|
||||
/// However, if an empty string is specified only invariant children are returned.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public static IEnumerable<IPublishedContent>? Children(this IPublishedContent content, IVariationContextAccessor? variationContextAccessor, string? culture = null)
|
||||
public static IEnumerable<IPublishedContent> Children(this IPublishedContent content, IVariationContextAccessor? variationContextAccessor, string? culture = null)
|
||||
{
|
||||
// handle context culture for variant
|
||||
if (culture == null)
|
||||
@@ -1021,9 +1021,9 @@ public static class PublishedContentExtensions
|
||||
}
|
||||
|
||||
IEnumerable<IPublishedContent>? children = content.ChildrenForAllCultures;
|
||||
return culture == "*"
|
||||
? children
|
||||
: children?.Where(x => x.IsInvariantOrHasCulture(culture));
|
||||
return (culture == "*"
|
||||
? children : children?.Where(x => x.IsInvariantOrHasCulture(culture)))
|
||||
?? Enumerable.Empty<IPublishedContent>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1040,12 +1040,12 @@ public static class PublishedContentExtensions
|
||||
/// <remarks>
|
||||
/// <para>Children are sorted by their sortOrder.</para>
|
||||
/// </remarks>
|
||||
public static IEnumerable<IPublishedContent>? Children(
|
||||
public static IEnumerable<IPublishedContent> Children(
|
||||
this IPublishedContent content,
|
||||
IVariationContextAccessor variationContextAccessor,
|
||||
Func<IPublishedContent, bool> predicate,
|
||||
string? culture = null) =>
|
||||
content.Children(variationContextAccessor, culture)?.Where(predicate);
|
||||
content.Children(variationContextAccessor, culture).Where(predicate);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the children of the content, of any of the specified types.
|
||||
@@ -1058,7 +1058,7 @@ public static class PublishedContentExtensions
|
||||
/// </param>
|
||||
/// <param name="contentTypeAlias">The content type alias.</param>
|
||||
/// <returns>The children of the content, of any of the specified types.</returns>
|
||||
public static IEnumerable<IPublishedContent>? ChildrenOfType(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string? contentTypeAlias, string? culture = null) =>
|
||||
public static IEnumerable<IPublishedContent> ChildrenOfType(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string? contentTypeAlias, string? culture = null) =>
|
||||
content.Children(variationContextAccessor, x => x.ContentType.Alias.InvariantEquals(contentTypeAlias), culture);
|
||||
|
||||
/// <summary>
|
||||
@@ -1075,9 +1075,9 @@ public static class PublishedContentExtensions
|
||||
/// <remarks>
|
||||
/// <para>Children are sorted by their sortOrder.</para>
|
||||
/// </remarks>
|
||||
public static IEnumerable<T>? Children<T>(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string? culture = null)
|
||||
public static IEnumerable<T> Children<T>(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string? culture = null)
|
||||
where T : class, IPublishedContent =>
|
||||
content.Children(variationContextAccessor, culture)?.OfType<T>();
|
||||
content.Children(variationContextAccessor, culture).OfType<T>();
|
||||
|
||||
public static IPublishedContent? FirstChild(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string? culture = null) =>
|
||||
content.Children(variationContextAccessor, culture)?.FirstOrDefault();
|
||||
@@ -1119,12 +1119,13 @@ public static class PublishedContentExtensions
|
||||
/// <remarks>
|
||||
/// <para>Note that in V7 this method also return the content node self.</para>
|
||||
/// </remarks>
|
||||
public static IEnumerable<IPublishedContent>? Siblings(
|
||||
public static IEnumerable<IPublishedContent> Siblings(
|
||||
this IPublishedContent content,
|
||||
IPublishedSnapshot? publishedSnapshot,
|
||||
IVariationContextAccessor variationContextAccessor,
|
||||
string? culture = null) =>
|
||||
SiblingsAndSelf(content, publishedSnapshot, variationContextAccessor, culture)?.Where(x => x.Id != content.Id);
|
||||
SiblingsAndSelf(content, publishedSnapshot, variationContextAccessor, culture)
|
||||
?.Where(x => x.Id != content.Id) ?? Enumerable.Empty<IPublishedContent>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the siblings of the content, of a given content type.
|
||||
@@ -1141,14 +1142,14 @@ public static class PublishedContentExtensions
|
||||
/// <remarks>
|
||||
/// <para>Note that in V7 this method also return the content node self.</para>
|
||||
/// </remarks>
|
||||
public static IEnumerable<IPublishedContent>? SiblingsOfType(
|
||||
public static IEnumerable<IPublishedContent> SiblingsOfType(
|
||||
this IPublishedContent content,
|
||||
IPublishedSnapshot? publishedSnapshot,
|
||||
IVariationContextAccessor variationContextAccessor,
|
||||
string contentTypeAlias,
|
||||
string? culture = null) =>
|
||||
SiblingsAndSelfOfType(content, publishedSnapshot, variationContextAccessor, contentTypeAlias, culture)
|
||||
?.Where(x => x.Id != content.Id);
|
||||
?.Where(x => x.Id != content.Id) ?? Enumerable.Empty<IPublishedContent>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the siblings of the content, of a given content type.
|
||||
@@ -1165,10 +1166,10 @@ public static class PublishedContentExtensions
|
||||
/// <remarks>
|
||||
/// <para>Note that in V7 this method also return the content node self.</para>
|
||||
/// </remarks>
|
||||
public static IEnumerable<T>? Siblings<T>(this IPublishedContent content, IPublishedSnapshot? publishedSnapshot, IVariationContextAccessor variationContextAccessor, string? culture = null)
|
||||
public static IEnumerable<T> Siblings<T>(this IPublishedContent content, IPublishedSnapshot? publishedSnapshot, IVariationContextAccessor variationContextAccessor, string? culture = null)
|
||||
where T : class, IPublishedContent =>
|
||||
SiblingsAndSelf<T>(content, publishedSnapshot, variationContextAccessor, culture)
|
||||
?.Where(x => x.Id != content.Id);
|
||||
?.Where(x => x.Id != content.Id) ?? Enumerable.Empty<T>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the siblings of the content including the node itself to indicate the position.
|
||||
@@ -1203,16 +1204,17 @@ public static class PublishedContentExtensions
|
||||
/// </param>
|
||||
/// <param name="contentTypeAlias">The content type alias.</param>
|
||||
/// <returns>The siblings of the content including the node itself, of the given content type.</returns>
|
||||
public static IEnumerable<IPublishedContent>? SiblingsAndSelfOfType(
|
||||
public static IEnumerable<IPublishedContent> SiblingsAndSelfOfType(
|
||||
this IPublishedContent content,
|
||||
IPublishedSnapshot? publishedSnapshot,
|
||||
IVariationContextAccessor variationContextAccessor,
|
||||
string contentTypeAlias,
|
||||
string? culture = null) =>
|
||||
content.Parent != null
|
||||
(content.Parent != null
|
||||
? content.Parent.ChildrenOfType(variationContextAccessor, contentTypeAlias, culture)
|
||||
: publishedSnapshot?.Content?.GetAtRoot(culture).OfTypes(contentTypeAlias)
|
||||
.WhereIsInvariantOrHasCulture(variationContextAccessor, culture);
|
||||
.WhereIsInvariantOrHasCulture(variationContextAccessor, culture))
|
||||
?? Enumerable.Empty<IPublishedContent>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the siblings of the content including the node itself to indicate the position, of a given content type.
|
||||
@@ -1226,16 +1228,17 @@ public static class PublishedContentExtensions
|
||||
/// null)
|
||||
/// </param>
|
||||
/// <returns>The siblings of the content including the node itself, of the given content type.</returns>
|
||||
public static IEnumerable<T>? SiblingsAndSelf<T>(
|
||||
public static IEnumerable<T> SiblingsAndSelf<T>(
|
||||
this IPublishedContent content,
|
||||
IPublishedSnapshot? publishedSnapshot,
|
||||
IVariationContextAccessor variationContextAccessor,
|
||||
string? culture = null)
|
||||
where T : class, IPublishedContent =>
|
||||
content.Parent != null
|
||||
(content.Parent != null
|
||||
? content.Parent.Children<T>(variationContextAccessor, culture)
|
||||
: publishedSnapshot?.Content?.GetAtRoot(culture).OfType<T>()
|
||||
.WhereIsInvariantOrHasCulture(variationContextAccessor, culture);
|
||||
.WhereIsInvariantOrHasCulture(variationContextAccessor, culture))
|
||||
?? Enumerable.Empty<T>();
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -1253,7 +1256,7 @@ public static class PublishedContentExtensions
|
||||
/// <see cref="Umbraco.Web.PublishedContentExtensions.AncestorOrSelf(IPublishedContent, int)" /> with <c>maxLevel</c>
|
||||
/// set to 1.
|
||||
/// </remarks>
|
||||
public static IPublishedContent? Root(this IPublishedContent content) => content.AncestorOrSelf(1);
|
||||
public static IPublishedContent Root(this IPublishedContent content) => content.AncestorOrSelf(1);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the root content (ancestor or self at level 1) for the specified <paramref name="content" /> if it's of the
|
||||
@@ -1278,16 +1281,16 @@ public static class PublishedContentExtensions
|
||||
|
||||
#region Writer and creator
|
||||
|
||||
public static string? GetCreatorName(this IPublishedContent content, IUserService userService)
|
||||
public static string GetCreatorName(this IPublishedContent content, IUserService userService)
|
||||
{
|
||||
IProfile? user = userService.GetProfileById(content.CreatorId);
|
||||
return user?.Name;
|
||||
return user?.Name ?? string.Empty;
|
||||
}
|
||||
|
||||
public static string? GetWriterName(this IPublishedContent content, IUserService userService)
|
||||
public static string GetWriterName(this IPublishedContent content, IUserService userService)
|
||||
{
|
||||
IProfile? user = userService.GetProfileById(content.WriterId);
|
||||
return user?.Name;
|
||||
return user?.Name ?? string.Empty;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -23,7 +23,7 @@ public interface IPublishedContent : IPublishedElement
|
||||
/// <summary>
|
||||
/// Gets the name of the content item for the current culture.
|
||||
/// </summary>
|
||||
string? Name { get; }
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the URL segment of the content item for the current culture.
|
||||
@@ -141,10 +141,10 @@ public interface IPublishedContent : IPublishedElement
|
||||
/// <summary>
|
||||
/// Gets the children of the content item that are available for the current culture.
|
||||
/// </summary>
|
||||
IEnumerable<IPublishedContent>? Children { get; }
|
||||
IEnumerable<IPublishedContent> Children { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the children of the content item, regardless of whether they are available for the current culture.
|
||||
/// </summary>
|
||||
IEnumerable<IPublishedContent>? ChildrenForAllCultures { get; }
|
||||
IEnumerable<IPublishedContent> ChildrenForAllCultures { get; }
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Umbraco.Cms.Core.Models.PublishedContent
|
||||
public abstract int Id { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual string? Name => this.Name(_variationContextAccessor);
|
||||
public virtual string Name => this.Name(_variationContextAccessor);
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual string? UrlSegment => this.UrlSegment(_variationContextAccessor);
|
||||
@@ -69,7 +69,7 @@ namespace Umbraco.Cms.Core.Models.PublishedContent
|
||||
public abstract IPublishedContent? Parent { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual IEnumerable<IPublishedContent>? Children => this.Children(_variationContextAccessor);
|
||||
public virtual IEnumerable<IPublishedContent> Children => this.Children(_variationContextAccessor);
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract IEnumerable<IPublishedContent> ChildrenForAllCultures { get; }
|
||||
|
||||
@@ -54,7 +54,7 @@ public abstract class PublishedContentWrapped : IPublishedContent
|
||||
public IPublishedContent Unwrap() => _content;
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual string? Name => _content.Name;
|
||||
public virtual string Name => _content.Name;
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual string? UrlSegment => _content.UrlSegment;
|
||||
@@ -99,10 +99,10 @@ public abstract class PublishedContentWrapped : IPublishedContent
|
||||
public virtual bool IsPublished(string? culture = null) => _content.IsPublished(culture);
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual IEnumerable<IPublishedContent>? Children => _content.Children;
|
||||
public virtual IEnumerable<IPublishedContent> Children => _content.Children;
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual IEnumerable<IPublishedContent>? ChildrenForAllCultures => _content.ChildrenForAllCultures;
|
||||
public virtual IEnumerable<IPublishedContent> ChildrenForAllCultures => _content.ChildrenForAllCultures;
|
||||
|
||||
/// <inheritdoc cref="IPublishedElement.Properties" />
|
||||
public virtual IEnumerable<IPublishedProperty> Properties => _content.Properties;
|
||||
|
||||
@@ -20,13 +20,14 @@ public sealed class InternalPublishedContent : IPublishedContent
|
||||
Path = string.Empty;
|
||||
ContentType = contentType;
|
||||
Properties = Enumerable.Empty<IPublishedProperty>();
|
||||
Name = string.Empty;
|
||||
}
|
||||
|
||||
public Guid Version { get; set; }
|
||||
|
||||
public int ParentId { get; set; }
|
||||
|
||||
public IEnumerable<int>? ChildIds { get; set; }
|
||||
public IEnumerable<int> ChildIds { get; set; } = Enumerable.Empty<int>();
|
||||
|
||||
public int Id { get; set; }
|
||||
|
||||
@@ -45,7 +46,7 @@ public sealed class InternalPublishedContent : IPublishedContent
|
||||
|
||||
public int SortOrder { get; set; }
|
||||
|
||||
public string? Name { get; set; }
|
||||
public string Name { get; set; }
|
||||
|
||||
public IReadOnlyDictionary<string, PublishedCultureInfo> Cultures => _cultures ??= GetCultures();
|
||||
|
||||
@@ -71,9 +72,9 @@ public sealed class InternalPublishedContent : IPublishedContent
|
||||
|
||||
public bool IsPublished(string? culture = null) => true;
|
||||
|
||||
public IEnumerable<IPublishedContent>? Children { get; set; }
|
||||
public IEnumerable<IPublishedContent> Children { get; set; } = Enumerable.Empty<IPublishedContent>();
|
||||
|
||||
public IEnumerable<IPublishedContent>? ChildrenForAllCultures => Children;
|
||||
public IEnumerable<IPublishedContent> ChildrenForAllCultures => Children;
|
||||
|
||||
public IPublishedContentType ContentType { get; set; }
|
||||
|
||||
|
||||
@@ -330,7 +330,7 @@ public static class FriendlyPublishedContentExtensions
|
||||
/// However, if an empty string is specified only invariant children are returned.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public static IEnumerable<IPublishedContent>? Children(this IPublishedContent content, string? culture = null)
|
||||
public static IEnumerable<IPublishedContent> Children(this IPublishedContent content, string? culture = null)
|
||||
=> content.Children(VariationContextAccessor, culture);
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -719,25 +719,25 @@ public class PublishedSnapshotServiceCollectionTests : PublishedSnapshotServiceT
|
||||
AssertDocuments(documents);
|
||||
|
||||
documents = snapshot.Content.GetById(1).Children(VariationContextAccessor, "*").ToArray();
|
||||
AssertDocuments(documents, "N4-fr-FR", null, "N6-fr-FR");
|
||||
AssertDocuments(documents, "N4-fr-FR", string.Empty, "N6-fr-FR");
|
||||
AssertDocuments("en-US", documents, "N4-en-US", "N5-en-US", "N6-en-US");
|
||||
|
||||
documents = snapshot.Content.GetById(1).Children(VariationContextAccessor, "en-US").ToArray();
|
||||
AssertDocuments(documents, "N4-fr-FR", null, "N6-fr-FR");
|
||||
AssertDocuments(documents, "N4-fr-FR", string.Empty, "N6-fr-FR");
|
||||
AssertDocuments("en-US", documents, "N4-en-US", "N5-en-US", "N6-en-US");
|
||||
|
||||
documents = snapshot.Content.GetById(1).ChildrenForAllCultures.ToArray();
|
||||
AssertDocuments(documents, "N4-fr-FR", null, "N6-fr-FR");
|
||||
AssertDocuments(documents, "N4-fr-FR", string.Empty, "N6-fr-FR");
|
||||
AssertDocuments("en-US", documents, "N4-en-US", "N5-en-US", "N6-en-US");
|
||||
|
||||
documents = snapshot.Content.GetAtRoot("*").ToArray();
|
||||
AssertDocuments(documents, "N1-fr-FR", null, "N3-fr-FR");
|
||||
AssertDocuments(documents, "N1-fr-FR", string.Empty, "N3-fr-FR");
|
||||
|
||||
documents = snapshot.Content.GetById(1).DescendantsOrSelf(VariationContextAccessor).ToArray();
|
||||
AssertDocuments(documents, "N1-fr-FR", "N4-fr-FR", "N12-fr-FR", "N6-fr-FR");
|
||||
|
||||
documents = snapshot.Content.GetById(1).DescendantsOrSelf(VariationContextAccessor, "*").ToArray();
|
||||
AssertDocuments(documents, "N1-fr-FR", "N4-fr-FR", null /*11*/, "N12-fr-FR", null /*5*/, "N6-fr-FR");
|
||||
AssertDocuments(documents, "N1-fr-FR", "N4-fr-FR", string.Empty /*11*/, "N12-fr-FR", string.Empty /*5*/, "N6-fr-FR");
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -120,7 +120,7 @@ public class PublishedSnapshotServiceContentTests : PublishedSnapshotServiceTest
|
||||
Assert.AreEqual("val-fr1", publishedContent.Value<string>(Mock.Of<IPublishedValueFallback>(), "prop", "fr-FR"));
|
||||
Assert.AreEqual("val-uk1", publishedContent.Value<string>(Mock.Of<IPublishedValueFallback>(), "prop", "en-UK"));
|
||||
|
||||
Assert.IsNull(publishedContent.Name(VariationContextAccessor)); // no invariant name for varying content
|
||||
Assert.AreEqual(publishedContent.Name(VariationContextAccessor), string.Empty); // no invariant name for varying content
|
||||
Assert.AreEqual("name-fr1", publishedContent.Name(VariationContextAccessor, "fr-FR"));
|
||||
Assert.AreEqual("name-uk1", publishedContent.Name(VariationContextAccessor, "en-UK"));
|
||||
|
||||
@@ -129,7 +129,7 @@ public class PublishedSnapshotServiceContentTests : PublishedSnapshotServiceTest
|
||||
Assert.AreEqual("val-fr2", draftContent.Value<string>(Mock.Of<IPublishedValueFallback>(), "prop", "fr-FR"));
|
||||
Assert.AreEqual("val-uk2", draftContent.Value<string>(Mock.Of<IPublishedValueFallback>(), "prop", "en-UK"));
|
||||
|
||||
Assert.IsNull(draftContent.Name(VariationContextAccessor)); // no invariant name for varying content
|
||||
Assert.AreEqual(draftContent.Name(VariationContextAccessor), string.Empty); // no invariant name for varying content
|
||||
Assert.AreEqual("name-fr2", draftContent.Name(VariationContextAccessor, "fr-FR"));
|
||||
Assert.AreEqual("name-uk2", draftContent.Name(VariationContextAccessor, "en-UK"));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user