diff --git a/src/Umbraco.Core/Notifications/ContentScaffoldedNotification.cs b/src/Umbraco.Core/Notifications/ContentScaffoldedNotification.cs
new file mode 100644
index 0000000000..47eda5468d
--- /dev/null
+++ b/src/Umbraco.Core/Notifications/ContentScaffoldedNotification.cs
@@ -0,0 +1,18 @@
+// Copyright (c) Umbraco.
+// See LICENSE for more details.
+
+using Umbraco.Cms.Core.Events;
+using Umbraco.Cms.Core.Models;
+
+namespace Umbraco.Cms.Core.Notifications;
+
+///
+/// Notification that is send out when a Content item has been scaffolded from an original item and basic cleaning has been performed
+///
+public sealed class ContentScaffoldedNotification : ScaffoldedNotification
+{
+ public ContentScaffoldedNotification(IContent original, IContent scaffold, int parentId, EventMessages messages)
+ : base(original, scaffold, parentId, messages)
+ {
+ }
+}
diff --git a/src/Umbraco.Core/Notifications/ScaffoldedNotification.cs b/src/Umbraco.Core/Notifications/ScaffoldedNotification.cs
new file mode 100644
index 0000000000..f64bfd3933
--- /dev/null
+++ b/src/Umbraco.Core/Notifications/ScaffoldedNotification.cs
@@ -0,0 +1,23 @@
+// Copyright (c) Umbraco.
+// See LICENSE for more details.
+
+using Umbraco.Cms.Core.Events;
+
+namespace Umbraco.Cms.Core.Notifications;
+
+public abstract class ScaffoldedNotification : CancelableObjectNotification
+ where T : class
+{
+ protected ScaffoldedNotification(T original, T scaffold, int parentId, EventMessages messages)
+ : base(original, messages)
+ {
+ Scaffold = scaffold;
+ ParentId = parentId;
+ }
+
+ public T Original => Target;
+
+ public T Scaffold { get; }
+
+ public int ParentId { get; }
+}
diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs
index c65e50024c..bd6f2e9b38 100644
--- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs
+++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs
@@ -358,10 +358,13 @@ public static partial class UmbracoBuilderExtensions
builder
.AddNotificationHandler()
.AddNotificationHandler()
+ .AddNotificationHandler()
.AddNotificationHandler()
.AddNotificationHandler()
+ .AddNotificationHandler()
.AddNotificationHandler()
.AddNotificationHandler()
+ .AddNotificationHandler()
.AddNotificationHandler()
.AddNotificationHandler()
.AddNotificationHandler()
diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentNotificationHandler.cs b/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentNotificationHandler.cs
index 2b4ac75042..ce867d29e4 100644
--- a/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentNotificationHandler.cs
+++ b/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentNotificationHandler.cs
@@ -10,9 +10,16 @@ using Umbraco.Extensions;
namespace Umbraco.Cms.Core.PropertyEditors;
+///
+/// Handles nested Udi keys when
+/// - saving: Empty keys get generated
+/// - copy: keys get replaced by new ones while keeping references intact
+/// - scaffolding: keys get replaced by new ones while keeping references intact
+///
public abstract class ComplexPropertyEditorContentNotificationHandler :
INotificationHandler,
- INotificationHandler
+ INotificationHandler,
+ INotificationHandler
{
protected abstract string EditorAlias { get; }
@@ -31,6 +38,12 @@ public abstract class ComplexPropertyEditorContentNotificationHandler :
}
}
+ public void Handle(ContentScaffoldedNotification notification)
+ {
+ IEnumerable props = notification.Scaffold.GetPropertiesByEditor(EditorAlias);
+ UpdatePropertyValues(props, false);
+ }
+
protected abstract string FormatPropertyValue(string rawJson, bool onlyMissingKeys);
private void UpdatePropertyValues(IEnumerable props, bool onlyMissingKeys)
diff --git a/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs b/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs
index 8beec57812..0a1a82b3eb 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs
@@ -16,6 +16,7 @@ using Umbraco.Cms.Core.Models.ContentEditing;
using Umbraco.Cms.Core.Models.Editors;
using Umbraco.Cms.Core.Models.Membership;
using Umbraco.Cms.Core.Models.Validation;
+using Umbraco.Cms.Core.Notifications;
using Umbraco.Cms.Core.Persistence.Querying;
using Umbraco.Cms.Core.PropertyEditors;
using Umbraco.Cms.Core.Routing;
@@ -623,17 +624,33 @@ public class ContentController : ContentControllerBase
[OutgoingEditorModelEvent]
public ActionResult GetEmptyBlueprint(int blueprintId, int parentId)
{
- IContent? blueprint = _contentService.GetBlueprintById(blueprintId);
- if (blueprint == null)
+ IContent? scaffold;
+ using (ICoreScope scope = _scopeProvider.CreateCoreScope())
{
- return NotFound();
+ IContent? blueprint = _contentService.GetBlueprintById(blueprintId);
+ if (blueprint is null)
+ {
+ return NotFound();
+ }
+ scaffold = (IContent)blueprint.DeepClone();
+
+ scaffold.Id = 0;
+ scaffold.Name = string.Empty;
+ scaffold.ParentId = parentId;
+
+ var scaffoldedNotification = new ContentScaffoldedNotification(blueprint, scaffold, parentId, new EventMessages());
+ if (scope.Notifications.PublishCancelable(scaffoldedNotification))
+ {
+ scope.Complete();
+ return Problem("Scaffolding was cancelled");
+ }
+
+ scope.Complete();
}
- blueprint.Id = 0;
- blueprint.Name = string.Empty;
- blueprint.ParentId = parentId;
- ContentItemDisplay? mapped = _umbracoMapper.Map(blueprint);
+
+ ContentItemDisplay? mapped = _umbracoMapper.Map(scaffold);
if (mapped is not null)
{