Merge branch 'main' into v17/dev

# Conflicts:
#	src/Umbraco.Core/Services/DataTypeService.cs
This commit is contained in:
Andy Butland
2025-09-15 13:36:37 +02:00
80 changed files with 4314 additions and 485 deletions

View File

@@ -81,6 +81,9 @@ internal sealed class PublishContentTypeFactoryTest : UmbracoIntegrationTest
{
var dataType = new DataTypeBuilder()
.WithId(0)
.AddEditor()
.WithAlias(Constants.PropertyEditors.Aliases.TextBox)
.Done()
.Build();
dataType.EditorUiAlias = "NotUpdated";
var dataTypeCreateResult = await DataTypeService.CreateAsync(dataType, Constants.Security.SuperUserKey);

View File

@@ -690,7 +690,7 @@ internal sealed class ContentServiceTests : UmbracoIntegrationTestWithContent
}
[Test]
public void Can_Get_Scheduled_Content_Keys()
public void Can_Get_Content_Schedules_By_Keys()
{
// Arrange
var root = ContentService.GetById(Textpage.Id);
@@ -701,11 +701,12 @@ internal sealed class ContentServiceTests : UmbracoIntegrationTestWithContent
ContentService.Publish(content, content.AvailableCultures.ToArray());
// Act
var keys = ContentService.GetScheduledContentKeys([Textpage.Key, Subpage.Key, Subpage2.Key]).ToList();
var keys = ContentService.GetContentSchedulesByIds([Textpage.Key, Subpage.Key, Subpage2.Key]).ToList();
// Assert
Assert.AreEqual(1, keys.Count);
Assert.AreEqual(Subpage.Key, keys.First());
Assert.AreEqual(keys[0].Key, Subpage.Id);
Assert.AreEqual(keys[0].Value.First().Id, contentSchedule.FullSchedule.First().Id);
}
[Test]

View File

@@ -1,3 +1,4 @@
using System.Text.Json.Nodes;
using NUnit.Framework;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Models;
@@ -19,29 +20,41 @@ internal sealed class MediaEditingServiceTests : UmbracoIntegrationTest
protected IMediaType ImageMediaType { get; set; }
protected IMediaType ArticleMediaType { get; set; }
[SetUp]
public async Task Setup()
{
ImageMediaType = MediaTypeService.Get(Constants.Conventions.MediaTypes.Image);
ArticleMediaType = MediaTypeService.Get(Constants.Conventions.MediaTypes.ArticleAlias);
}
[Test]
public async Task Cannot_Create_Media_With_Mandatory_Property()
public async Task Cannot_Create_Media_With_Mandatory_File_Property_Without_Providing_File()
{
var imageModel = CreateMediaCreateModel("Image", new Guid(), ImageMediaType.Key);
var imageModel = CreateMediaCreateModel("Image", Guid.NewGuid(), ImageMediaType.Key);
var imageCreateAttempt = await MediaEditingService.CreateAsync(imageModel, Constants.Security.SuperUserKey);
// Assert
Assert.IsFalse(imageCreateAttempt.Success);
Assert.AreEqual(ContentEditingOperationStatus.PropertyValidationError, imageCreateAttempt.Status);
}
[Test]
public async Task Can_Create_Media_Without_Mandatory_Property()
public async Task Can_Create_Media_With_Mandatory_File_Property_With_File_Provided()
{
ImageMediaType.PropertyTypes.First(x => x.Alias == "umbracoFile").Mandatory = false;
var imageModel = CreateMediaCreateModelWithFile("Image", Guid.NewGuid(), ArticleMediaType.Key);
var imageCreateAttempt = await MediaEditingService.CreateAsync(imageModel, Constants.Security.SuperUserKey);
Assert.IsTrue(imageCreateAttempt.Success);
Assert.AreEqual(ContentEditingOperationStatus.Success, imageCreateAttempt.Status);
}
[Test]
public async Task Can_Create_Media_With_Optional_File_Property_Without_Providing_File()
{
ImageMediaType.PropertyTypes.First(x => x.Alias == Constants.Conventions.Media.File).Mandatory = false;
MediaTypeService.Save(ImageMediaType);
var imageModel = CreateMediaCreateModel("Image", new Guid(), ImageMediaType.Key);
var imageModel = CreateMediaCreateModel("Image", Guid.NewGuid(), ImageMediaType.Key);
var imageCreateAttempt = await MediaEditingService.CreateAsync(imageModel, Constants.Security.SuperUserKey);
// Assert
@@ -49,12 +62,57 @@ internal sealed class MediaEditingServiceTests : UmbracoIntegrationTest
Assert.AreEqual(ContentEditingOperationStatus.Success, imageCreateAttempt.Status);
}
private MediaCreateModel CreateMediaCreateModel(string name, Guid key, Guid mediaTypeKey)
[Test]
public async Task Can_Update_Media_With_Mandatory_File_Property_With_File_Provided()
{
Guid articleKey = Guid.NewGuid();
var articleModel = CreateMediaCreateModelWithFile("Article", articleKey, ArticleMediaType.Key);
var articleCreateAttempt = await MediaEditingService.CreateAsync(articleModel, Constants.Security.SuperUserKey);
Assert.IsTrue(articleCreateAttempt.Success);
var updateModel = new MediaUpdateModel
{
Properties =
[
new PropertyValueModel
{
Alias = Constants.Conventions.Media.File,
Value = new JsonObject
{
{ "src", string.Empty },
},
}
],
Variants = articleModel.Variants,
};
var articleUpdateAttempt = await MediaEditingService.ValidateUpdateAsync(articleKey, updateModel);
Assert.IsFalse(articleUpdateAttempt.Success);
Assert.AreEqual(ContentEditingOperationStatus.PropertyValidationError, articleUpdateAttempt.Status);
}
private static MediaCreateModel CreateMediaCreateModel(string name, Guid key, Guid mediaTypeKey)
=> new()
{
ContentTypeKey = mediaTypeKey,
ParentKey = Constants.System.RootKey,
Variants = [new () { Name = name }],
Variants = [new() { Name = name }],
Key = key,
};
private static MediaCreateModel CreateMediaCreateModelWithFile(string name, Guid key, Guid mediaTypeKey)
{
var model = CreateMediaCreateModel(name, key, mediaTypeKey);
model.Properties = [
new PropertyValueModel
{
Alias = Constants.Conventions.Media.File,
Value = new JsonObject
{
{ "src", "reference-to-file" },
},
}
];
return model;
}
}

View File

@@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
@@ -103,7 +104,14 @@ internal sealed class DocumentRepositoryTest : UmbracoIntegrationTest
var ctRepository = CreateRepository(scopeAccessor, out contentTypeRepository, out TemplateRepository tr);
var editors = new PropertyEditorCollection(new DataEditorCollection(() => Enumerable.Empty<IDataEditor>()));
dtdRepository = new DataTypeRepository(scopeAccessor, appCaches, editors, LoggerFactory.CreateLogger<DataTypeRepository>(), LoggerFactory, ConfigurationEditorJsonSerializer);
dtdRepository = new DataTypeRepository(
scopeAccessor,
appCaches,
editors,
LoggerFactory.CreateLogger<DataTypeRepository>(),
LoggerFactory,
ConfigurationEditorJsonSerializer,
Services.GetRequiredService<IDataValueEditorFactory>());
return ctRepository;
}

View File

@@ -1,7 +1,6 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
using System.Linq;
using NUnit.Framework;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Models;
@@ -487,4 +486,36 @@ internal sealed class DataTypeServiceTests : UmbracoIntegrationTest
Assert.AreEqual("bodyText", secondResult.NodeAlias);
Assert.AreEqual("Body text", secondResult.NodeName);
}
[Test]
public async Task Gets_MissingPropertyEditor_When_Editor_NotFound()
{
// Arrange
IDataType? dataType = (await DataTypeService.CreateAsync(
new DataType(new TestEditor(DataValueEditorFactory), ConfigurationEditorJsonSerializer)
{
Name = "Test Missing Editor",
DatabaseType = ValueStorageType.Ntext,
},
Constants.Security.SuperUserKey)).Result;
Assert.IsNotNull(dataType);
// Act
IDataType? actual = await DataTypeService.GetAsync(dataType.Key);
// Assert
Assert.NotNull(actual);
Assert.AreEqual(dataType.Key, actual.Key);
Assert.IsAssignableFrom(typeof(MissingPropertyEditor), actual.Editor);
Assert.AreEqual("Test Editor", actual.EditorAlias, "The alias should be the same as the original editor");
Assert.AreEqual("Umb.PropertyEditorUi.Missing", actual.EditorUiAlias, "The editor UI alias should be the Missing Editor UI");
}
private class TestEditor : DataEditor
{
public TestEditor(IDataValueEditorFactory dataValueEditorFactory)
: base(dataValueEditorFactory) =>
Alias = "Test Editor";
}
}

View File

@@ -1,16 +1,15 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Notifications;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Infrastructure.Persistence.Relations;
using Umbraco.Cms.Tests.Common.Builders;
using Umbraco.Cms.Tests.Common.Testing;
using Umbraco.Cms.Tests.Integration.Testing;
using Umbraco.Extensions;
namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services;
@@ -32,6 +31,13 @@ internal sealed class RelationServiceTests : UmbracoIntegrationTest
private IRelationService RelationService => GetRequiredService<IRelationService>();
protected override void CustomTestSetup(IUmbracoBuilder builder)
{
base.CustomTestSetup(builder);
builder
.AddNotificationHandler<ContentSavedNotification, ContentRelationsUpdate>();
}
[Test]
public void Get_Paged_Relations_By_Relation_Type()
{

View File

@@ -3,7 +3,9 @@ using NUnit.Framework;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Notifications;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Infrastructure.Persistence.Relations;
using Umbraco.Cms.Tests.Common.Attributes;
using Umbraco.Cms.Tests.Common.Builders;
using Umbraco.Cms.Tests.Common.Testing;
@@ -29,11 +31,12 @@ internal sealed class TrackRelationsTests : UmbracoIntegrationTestWithContent
private IRelationService RelationService => GetRequiredService<IRelationService>();
// protected override void CustomTestSetup(IUmbracoBuilder builder)
// {
// base.CustomTestSetup(builder);
// builder.AddNuCache();
// }
protected override void CustomTestSetup(IUmbracoBuilder builder)
{
base.CustomTestSetup(builder);
builder
.AddNotificationHandler<ContentSavedNotification, ContentRelationsUpdate>();
}
[Test]
[LongRunning]
@@ -89,6 +92,5 @@ internal sealed class TrackRelationsTests : UmbracoIntegrationTestWithContent
Assert.AreEqual(c1.Id, relations[2].ChildId);
Assert.AreEqual(Constants.Conventions.RelationTypes.RelatedMemberAlias, relations[3].RelationType.Alias);
Assert.AreEqual(member.Id, relations[3].ChildId);
}
}

View File

@@ -3,7 +3,9 @@
using NUnit.Framework;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Notifications;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Infrastructure.Persistence.Relations;
using Umbraco.Cms.Tests.Common.Builders;
using Umbraco.Cms.Tests.Common.Builders.Extensions;
using Umbraco.Cms.Tests.Common.Testing;
@@ -25,6 +27,13 @@ internal class TrackedReferencesServiceTests : UmbracoIntegrationTest
private IContentType ContentType { get; set; }
protected override void CustomTestSetup(IUmbracoBuilder builder)
{
base.CustomTestSetup(builder);
builder
.AddNotificationHandler<ContentSavedNotification, ContentRelationsUpdate>();
}
[SetUp]
public void Setup() => CreateTestData();