How to get content from block list elements before node save and insert/save to a node property?
How to get content from block list elements before node save and insert/save to a node property?
On my page nodes I have a property 'ribbons' of type Umbraco.BlockList. I also have a text property 'browserTitle'.
When an editor saves the document I want to traverse the ribbons and look for the first ribbon element where property 'healine' is not empty and put that value into the node's 'browserTitleResult' property just before the node is saved.
(use case is that if 'browserTitle' is empty I can use 'browserTitleResult' for page title instead)
I believe that you have to implement custom ContentSaving notification handler. It will be triggered before content saved and you can modify properties of your page in a way you want (parse existing and fill-in empty).
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Notifications;
namespace MartinRud.ContentService;
public class SeoAutomations : INotificationHandler<ContentPublishingNotification>
{
public void Handle(ContentPublishingNotification notification)
{
foreach (var node in notification.PublishedEntities)
{
if (node.ContentType.Alias.Contains("page"))
{
Console.WriteLine("Test for node " + node.Name);
node.SetValue("browserTitleResult", "Testing...");
}
}
}
}
The Console.WriteLine() shows up fine in the terminal, but in Umbraco backend I get:
> Received an error from the server An error occurred Variation
> "<null>,<null>" is not supported by the property type. Exception
> Details System.NotSupportedException, System.Private.CoreLib,
> Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e:
> Variation "<null>,<null>" is not supported by the property type.
> Stacktrace at Umbraco.Cms.Core.Models.Property.SetValue(Object value,
> String culture, String segment) at
> Umbraco.Cms.Core.Models.ContentBase.SetValue(String propertyTypeAlias,
> Object value, String culture, String segment) at
> MartinRud.ContentService.SeoAutomations.Handle(ContentPublishingNotification
> notification) in
> C:\MartinRud-CloudRoot\Firma\Code\Dev.Baseline\Umbraco.Baseline\Umbraco.Baseline.Cms\App_MartinRud\MartinRud.ContentService
> .cs:line 16 at
> Umbraco.Cms.Core.Events.INotificationHandler`1.Handle(IEnumerable`1
> notifications) at
> Umbraco.Cms.Core.Events.NotificationHandlerWrapperImpl`1.<>c__DisplayClass0_0`2.<Handle>b__2(IEnumerable`1
> handlerNotifications) at
> Umbraco.Cms.Core.Events.EventAggregator.PublishCore[TNotification](IEnumerable`1
> allHandlers, IEnumerable`1 notifications) at
> Umbraco.Cms.Core.Events.NotificationHandlerWrapperImpl`1.Handle[TNotification,TNotificationHandler](IEnumerable`1
> notifications, ServiceFactory serviceFactory, Action`2 publish) at
> Umbraco.Cms.Core.Events.EventAggregator.PublishNotifications[TNotification,TNotificationHandler](IEnumerable`1
> notifications) at
> Umbraco.Cms.Core.Events.EventAggregator.Publish[TNotification,TNotificationHandler](IEnumerable`1
> notifications) at
> Umbraco.Cms.Core.Events.EventAggregator.Publish[TNotification](TNotification
> notification) at
> Umbraco.Cms.Core.Events.ScopedNotificationPublisher`1.PublishCancelable(ICancelableNotification
> notification) at
> Umbraco.Cms.Core.Services.ContentService.StrategyCanPublish(ICoreScope
> scope, IContent content, Boolean checkPath, IReadOnlyList`1
> culturesPublishing, IReadOnlyCollection`1 culturesUnpublishing,
> EventMessages evtMsgs, IReadOnlyCollection`1 allLangs, IDictionary`2
> notificationState) at
> Umbraco.Cms.Core.Services.ContentService.CommitDocumentChangesInternal(ICoreScope
> scope, IContent content, EventMessages eventMessages,
> IReadOnlyCollection`1 allLangs, IDictionary`2 notificationState, Int32
> userId, Boolean branchOne, Boolean branchRoot) at
> Umbraco.Cms.Core.Services.ContentService.SaveAndPublish(IContent
> content, String[] cultures, Int32 userId) at
> Umbraco.Cms.Web.BackOffice.Controllers.ContentController.PublishInternal(ContentItemSave
> contentItem, String defaultCulture, String cultureForInvariantErrors,
> Boolean& wasCancelled, String[]& successfulCultures) at
> Umbraco.Cms.Web.BackOffice.Controllers.ContentController.PostSaveInternal[TVariant](ContentItemSave
> contentItem, Func`3 saveMethod, Func`2 mapToDisplay) at
> Umbraco.Cms.Web.BackOffice.Controllers.ContentController.PostSave(ContentItemSave
> contentItem) at lambda_method976(Closure, Object) at
> Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(ActionContext
> actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor
> executor, Object controller, Object[] arguments) at
> Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker
> invoker, ValueTask`1 actionResultValueTask) at
> Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker
> invoker, Task lastTask, State next, Scope scope, Object state, Boolean
> isCompleted) at
> Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed
> context) at
> Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State&
> next, Scope& scope, Object& state, Boolean& isCompleted) at
> Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
> --- End of stack trace from previous location --- at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextExceptionFilterAsync>g__Awaited|26_0(ResourceInvoker
> invoker, Task lastTask, State next, Scope scope, Object state, Boolean
> isCompleted)
You used ContentPublishingNotification so, your handler will be triggered right before content will be published. I believe you want to make a change on content saving:
public class SavingAutomationAction : INotificationHandler<ContentSavingNotification>
{
private readonly IContentService _contentService;
private readonly ISqlContext _sqlContext;
public SavingAutomationAction (IContentService contentService, ISqlContext sqlContext)
{
_contentService = contentService;
_sqlContext = sqlContext;
}
public void Handle(ContentSavingNotification notification)
{
foreach (var node in notification.SavedEntities)
{
if (node.HasIdentity is false) // When entity is newly created
{
foreach (var property in node.Properties) // Iterate all props
{
var currentProp = node.GetValue(property.Alias);
node.SetValue(property.Alias, currentProp + "TEST");
}
}
}
}
}
Received an error from the server
An error occurred
Variation "<null>,<null>" is not supported by the property type.
Exception Details
System.NotSupportedException, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e: Variation "<null>,<null>" is not supported by the property type.
Stacktrace
at Umbraco.Cms.Core.Models.Property.SetValue(Object value, String culture, String segment)
at Umbraco.Cms.Core.Models.ContentBase.SetValue(String propertyTypeAlias, Object value, String culture, String segment)
at MartinRud.ContentService.SavingAutomationAction.Handle(ContentSavingNotification notification) in C:\MartinRud-CloudRoot\Firma\Code\Dev.Baseline\Umbraco.Baseline\Umbraco.Baseline.Cms\App_MartinRud\MartinRud.ContentService .cs:line 29
at Umbraco.Cms.Core.Events.INotificationHandler`1.Handle(IEnumerable`1 notifications)
at Umbraco.Cms.Core.Events.NotificationHandlerWrapperImpl`1.<>c__DisplayClass0_0`2.<Handle>b__2(IEnumerable`1 handlerNotifications)
at Umbraco.Cms.Core.Events.EventAggregator.PublishCore[TNotification](IEnumerable`1 allHandlers, IEnumerable`1 notifications)
at Umbraco.Cms.Core.Events.NotificationHandlerWrapperImpl`1.Handle[TNotification,TNotificationHandler](IEnumerable`1 notifications, ServiceFactory serviceFactory, Action`2 publish)
at Umbraco.Cms.Core.Events.EventAggregator.PublishNotifications[TNotification,TNotificationHandler](IEnumerable`1 notifications)
at Umbraco.Cms.Core.Events.EventAggregator.Publish[TNotification,TNotificationHandler](IEnumerable`1 notifications)
at Umbraco.Cms.Core.Events.EventAggregator.Publish[TNotification](TNotification notification)
at Umbraco.Cms.Core.Events.ScopedNotificationPublisher`1.PublishCancelable(ICancelableNotification notification)
at Umbraco.Cms.Core.Services.ContentService.SaveAndPublish(IContent content, String[] cultures, Int32 userId)
at Umbraco.Cms.Web.BackOffice.Controllers.ContentController.PublishInternal(ContentItemSave contentItem, String defaultCulture, String cultureForInvariantErrors, Boolean& wasCancelled, String[]& successfulCultures)
at Umbraco.Cms.Web.BackOffice.Controllers.ContentController.PostSaveInternal[TVariant](ContentItemSave contentItem, Func`3 saveMethod, Func`2 mapToDisplay)
at Umbraco.Cms.Web.BackOffice.Controllers.ContentController.PostSave(ContentItemSave contentItem)
at lambda_method53(Closure, Object)
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextExceptionFilterAsync>g__Awaited|26_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
Not sure why you're getting empty values - just tested on U13 site - works like a charm. Maybe try to set breakpoint at the beginning of Handle method and debug.
P.S> assuming that you added following line to umbraco builder somewhere between .AddComposers() and .Build() at Program.cs (or Startup.cs):
How to get content from block list elements before node save and insert/save to a node property?
How to get content from block list elements before node save and insert/save to a node property?
On my page nodes I have a property 'ribbons' of type Umbraco.BlockList. I also have a text property 'browserTitle'.
When an editor saves the document I want to traverse the ribbons and look for the first ribbon element where property 'healine' is not empty and put that value into the node's 'browserTitleResult' property just before the node is saved.
(use case is that if 'browserTitle' is empty I can use 'browserTitleResult' for page title instead)
I have tried using examples from https://docs.umbraco.com/umbraco-cms/fundamentals/code/umbraco-services#access-in-a-custom-class-via-dependency-injection but I cant figure it out.
Umbraco 13.3.0
Hi, Martin!
I believe that you have to implement custom ContentSaving notification handler. It will be triggered before content saved and you can modify properties of your page in a way you want (parse existing and fill-in empty).
Documentation: https://docs.umbraco.com/umbraco-cms/reference/notifications/contentservice-notifications
I have made this PoC now:
The Console.WriteLine() shows up fine in the terminal, but in Umbraco backend I get:
Martin,
You used ContentPublishingNotification so, your handler will be triggered right before content will be published. I believe you want to make a change on content saving:
I have tried this:
public class SavingAutomationAction : INotificationHandler
}
In the terminal I get empty values only (even though some of them are NOT empty):
If I uncomment this line:
I get:
Not sure why you're getting empty values - just tested on U13 site - works like a charm. Maybe try to set breakpoint at the beginning of Handle method and debug.
P.S> assuming that you added following line to umbraco builder somewhere between .AddComposers() and .Build() at Program.cs (or Startup.cs):
is working on a reply...