2020-09-21 09:52:58 +02:00
|
|
|
using Microsoft.Extensions.Logging;
|
2021-02-09 10:22:42 +01:00
|
|
|
using Umbraco.Cms.Core.Models.Entities;
|
2021-02-12 13:36:50 +01:00
|
|
|
using Umbraco.Cms.Infrastructure.Persistence.Dtos;
|
2021-02-09 11:26:22 +01:00
|
|
|
using Umbraco.Extensions;
|
2018-01-15 11:32:30 +01:00
|
|
|
|
2022-06-02 08:18:31 +02:00
|
|
|
namespace Umbraco.Cms.Core.Models;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Provides extension methods for path validation.
|
|
|
|
|
/// </summary>
|
|
|
|
|
internal static class PathValidationExtensions
|
2018-01-15 11:32:30 +01:00
|
|
|
{
|
|
|
|
|
/// <summary>
|
2022-06-02 08:18:31 +02:00
|
|
|
/// Does a quick check on the entity's set path to ensure that it's valid and consistent
|
2018-01-15 11:32:30 +01:00
|
|
|
/// </summary>
|
2022-06-02 08:18:31 +02:00
|
|
|
/// <param name="entity"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public static void ValidatePathWithException(this NodeDto entity)
|
2018-01-15 11:32:30 +01:00
|
|
|
{
|
2022-06-02 08:18:31 +02:00
|
|
|
// don't validate if it's empty and it has no id
|
|
|
|
|
if (entity.NodeId == default && entity.Path.IsNullOrWhiteSpace())
|
2018-01-15 11:32:30 +01:00
|
|
|
{
|
2022-06-02 08:18:31 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2018-01-15 11:32:30 +01:00
|
|
|
|
2022-06-02 08:18:31 +02:00
|
|
|
if (entity.Path.IsNullOrWhiteSpace())
|
|
|
|
|
{
|
|
|
|
|
throw new InvalidDataException(
|
|
|
|
|
$"The content item {entity.NodeId} has an empty path: {entity.Path} with parentID: {entity.ParentId}");
|
|
|
|
|
}
|
2018-01-15 11:32:30 +01:00
|
|
|
|
2022-06-02 08:18:31 +02:00
|
|
|
var pathParts = entity.Path.Split(Constants.CharArrays.Comma, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
|
if (pathParts.Length < 2)
|
|
|
|
|
{
|
|
|
|
|
// a path cannot be less than 2 parts, at a minimum it must be root (-1) and it's own id
|
|
|
|
|
throw new InvalidDataException(
|
|
|
|
|
$"The content item {entity.NodeId} has an invalid path: {entity.Path} with parentID: {entity.ParentId}");
|
|
|
|
|
}
|
2018-01-15 11:32:30 +01:00
|
|
|
|
2022-06-02 08:18:31 +02:00
|
|
|
if (entity.ParentId != default && pathParts[^2] != entity.ParentId.ToInvariantString())
|
|
|
|
|
{
|
|
|
|
|
// the 2nd last id in the path must be it's parent id
|
|
|
|
|
throw new InvalidDataException(
|
|
|
|
|
$"The content item {entity.NodeId} has an invalid path: {entity.Path} with parentID: {entity.ParentId}");
|
2018-01-15 11:32:30 +01:00
|
|
|
}
|
2022-06-02 08:18:31 +02:00
|
|
|
}
|
2018-01-15 11:32:30 +01:00
|
|
|
|
2022-06-02 08:18:31 +02:00
|
|
|
/// <summary>
|
|
|
|
|
/// Does a quick check on the entity's set path to ensure that it's valid and consistent
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="entity"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public static bool ValidatePath(this IUmbracoEntity entity)
|
|
|
|
|
{
|
|
|
|
|
// don't validate if it's empty and it has no id
|
|
|
|
|
if (entity.HasIdentity == false && entity.Path.IsNullOrWhiteSpace())
|
2018-01-15 11:32:30 +01:00
|
|
|
{
|
2022-06-02 08:18:31 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
2018-01-15 11:32:30 +01:00
|
|
|
|
2022-06-02 08:18:31 +02:00
|
|
|
if (entity.Path.IsNullOrWhiteSpace())
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2018-01-15 11:32:30 +01:00
|
|
|
|
2022-06-02 08:18:31 +02:00
|
|
|
var pathParts = entity.Path.Split(Constants.CharArrays.Comma, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
|
if (pathParts.Length < 2)
|
|
|
|
|
{
|
|
|
|
|
// a path cannot be less than 2 parts, at a minimum it must be root (-1) and it's own id
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2018-01-15 11:32:30 +01:00
|
|
|
|
2022-06-02 08:18:31 +02:00
|
|
|
if (entity.ParentId != default && pathParts[^2] != entity.ParentId.ToInvariantString())
|
|
|
|
|
{
|
|
|
|
|
// the 2nd last id in the path must be it's parent id
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2018-01-15 11:32:30 +01:00
|
|
|
|
2022-06-02 08:18:31 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// This will validate the entity's path and if it's invalid it will fix it, if fixing is required it will recursively
|
|
|
|
|
/// check and fix all ancestors if required.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="entity"></param>
|
|
|
|
|
/// <param name="logger"></param>
|
|
|
|
|
/// <param name="getParent">A callback specified to retrieve the parent entity of the entity</param>
|
|
|
|
|
/// <param name="update">A callback specified to update a fixed entity</param>
|
|
|
|
|
public static void EnsureValidPath<T>(
|
|
|
|
|
this T entity,
|
|
|
|
|
ILogger<T> logger,
|
|
|
|
|
Func<T, T> getParent,
|
|
|
|
|
Action<T> update)
|
|
|
|
|
where T : IUmbracoEntity
|
|
|
|
|
{
|
|
|
|
|
if (entity.HasIdentity == false)
|
|
|
|
|
{
|
|
|
|
|
throw new InvalidOperationException(
|
|
|
|
|
"Could not ensure the entity path, the entity has not been assigned an identity");
|
2018-01-15 11:32:30 +01:00
|
|
|
}
|
|
|
|
|
|
2022-06-02 08:18:31 +02:00
|
|
|
if (entity.ValidatePath() == false)
|
2018-01-15 11:32:30 +01:00
|
|
|
{
|
2022-06-02 08:18:31 +02:00
|
|
|
logger.LogWarning(
|
|
|
|
|
"The content item {EntityId} has an invalid path: {EntityPath} with parentID: {EntityParentId}",
|
|
|
|
|
entity.Id, entity.Path, entity.ParentId);
|
|
|
|
|
if (entity.ParentId == -1)
|
|
|
|
|
{
|
|
|
|
|
entity.Path = string.Concat("-1,", entity.Id);
|
2018-01-15 11:32:30 +01:00
|
|
|
|
2022-06-02 08:18:31 +02:00
|
|
|
// path changed, update it
|
|
|
|
|
update(entity);
|
|
|
|
|
}
|
|
|
|
|
else
|
2018-01-15 11:32:30 +01:00
|
|
|
{
|
2022-06-02 08:18:31 +02:00
|
|
|
T? parent = getParent(entity);
|
|
|
|
|
if (parent == null)
|
2018-01-15 11:32:30 +01:00
|
|
|
{
|
2022-06-02 08:18:31 +02:00
|
|
|
throw new NullReferenceException("Could not ensure path for entity " + entity.Id +
|
|
|
|
|
" could not resolve it's parent " + entity.ParentId);
|
2018-01-15 11:32:30 +01:00
|
|
|
}
|
|
|
|
|
|
2022-06-02 08:18:31 +02:00
|
|
|
// the parent must also be valid!
|
|
|
|
|
parent.EnsureValidPath(logger, getParent, update);
|
2018-01-15 11:32:30 +01:00
|
|
|
|
2022-06-02 08:18:31 +02:00
|
|
|
entity.Path = string.Concat(parent.Path, ",", entity.Id);
|
|
|
|
|
|
|
|
|
|
// path changed, update it
|
|
|
|
|
update(entity);
|
2018-01-15 11:32:30 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-16 12:42:21 +01:00
|
|
|
}
|