Sounds like this might be related to the new way that blocks are handled https://world.optimizely.com/blogs/bartosz-sekula/dates/2023/5/inline-blocks-in-contentarea/ maybe there's a bug. Perhaps there's a big in this, might be worth commenting on the post and linking to this!
Well it seem to work in a completely empty site, so im still trying to figure out if anyone else had a similar issue.
But it sure would be nice to have a way of disabling this new feature as the way the editors work in our sites, they are not using local blocks or "for this xxx" at all due to their apparent limitations, so this is a completely useless feature we been forced upon us with no way of opting out.
There is no error logged when it saves the inline block the first time, it just displays with "null" as name after saving (and also marked as a "Shared" block). When trying to edit it, the above error dialog is shown with this stack trace, which looks like its just the CMS trying to edit a block with no content reference:
2023-07-03 18:11:03.143 +02:00 [ERR] An unhandled exception has occurred while executing the request.
EPiServer.Core.ContentNotFoundException: Content with id - was not found
at EPiServer.Core.Internal.DefaultContentLoader.Get[T](ContentReference contentLink, LoaderOptions loaderOptions)
at EPiServer.Core.Internal.ProviderAwareContentVersionRepository.LoadCommonDraft(ContentReference contentLink, String language)
at EPiServer.Cms.Shell.UI.Rest.Internal.LatestContentVersionResolver.GetDraftLink(ContentReference id)
at EPiServer.Cms.Shell.UI.Rest.Internal.LatestContentVersionResolver.GetLatestVersion(ContentReference id, NameValueCollection queryString)
at EPiServer.Cms.Shell.UI.Rest.Internal.LatestContentVersionStore.Get(ContentReference id, Boolean keepversion)
at lambda_method648(Closure , Object , Object[] )
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeActionMethodAsync()
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeNextActionFilterAsync()
--- End of stack trace from previous location ---
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.<InvokeNextResourceFilter>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.Authorization.Policy.AuthorizationMiddlewareResultHandler.HandleAsync(RequestDelegate next, HttpContext context, AuthorizationPolicy policy, PolicyAuthorizationResult authorizeResult)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
It is strange that the “null” block that is supposedly an inline block is labled “Shared” in the screen dump. We have consistently seen the expected labeling.
However, we have seen another possibly similar bug in 12.21.0 and 12.22.0 that we are currently waiting for the support team to try to reproduce.
That bug can happen whenever a website has a block type that has a content area. (A minimal addition to the Alloy Template website is sufficient to get the prerequisites.) If such a block is added to a page (inline) and the content area of the block is filled with an inline block and a shared block, all it then takes is to reorder the blocks (using drag and drop, followed by publishing the page), which will make the shared block effectively vanish and be replaced by a copy of the inline block, and then to delete the last block.
This will put the page in a state where it is impossible to display it in edit mode.
An errror message box similar to the one shown above will appear, but with the text: “Unable to connect to the web server. What would you like to do?”
The server simultaneously outputs error like this:
EPiServer.Framework.Cache.ObjectInstanceCacheExtensions[0]
Failed to Read cacheKey = 'EPContentVersion:102_109'
System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')
at System.Collections.Generic.List`1.get_Item(Int32 index)
at EPiServer.SpecializedProperties.PropertyContentArea.LoadData(Object value)
at EPiServer.DataAccess.Internal.ContentAreaPropertyValueConverter.SetValue(PropertyContentArea property, PropertyDataRecord dataRecord, PropertyValueConverterContext context)
at EPiServer.DataAccess.Internal.ContentDB.ReadPropertyData(PropertyData prop, DbDataReader reader, ContentReference contentLink, CultureInfo language)
at EPiServer.DataAccess.Internal.ContentLoadDB.LoadContentInternal(ContentReference contentLink, Int32 languageBranchId, DbDataReader reader)
at EPiServer.DataAccess.Internal.ContentLoadDB.<>c__DisplayClass4_0.<LoadVersion>b__0()
Interesting so it could be UI related.
But when I first noticed this I had put a couple of blocks on a settings page, where the blocks were referenced from another component and used to render a list. When accessing the content area, no blocks were found, so they were effectively not there at all despite being rendered as "null" in the UI.
In my case there still seem to be something either in the dabase as its an upgrade, or in the code. I checked our use of IContentEvents, and could not find anything that just modifies blocks on save, so im still in the dark on the reason for this bug.
This is exactly why I never use new Epi features as they are immediately released...
Erik, Otto - we hear you. We will make it possible to opt-out from this change. This feature was very big and it seems we did not anticipate all the possible edge cases.
We would really appreciate if you could try to somehow reproduce your issues in Alloy or in any other test site so that we can improve inline blocks to work better.
Hi Bartosz, I think the ideal way of opting out would be if it could be done for individual content areas, through an attribute or similar. The new user interface is indeed nice, but we would prefer to only enable it where it is sure not to cause trouble or confusion related to cultures/translations.
I can confirm that this breaks permissions and the Welcome (CMP DAM) integration with a custom content area renderer.
I think I have found my issue at least. We have an interceptor for IContentAreaLoader that was disabled was not doing anything in itself, and just called the base class for the interface implementation, this is the effective (while not actual) code, and I can reproduce the issue 100% using this exact code:
public class BreakInlineBlocks : IContentAreaLoader
{
protected readonly IContentAreaLoader _baseLoader;
public BreakInlineBlocks(IContentAreaLoader baseLoader)
{
_baseLoader = baseLoader;
}
public IContent Get(ContentAreaItem contentAreaItem)
{
var content = _baseLoader.Get(contentAreaItem);
return content;
}
public DisplayOption LoadDisplayOption(ContentAreaItem contentAreaItem)
{
return _baseLoader.LoadDisplayOption(contentAreaItem);
}
}
public static class ModuleExtensions
{
/// <summary>
/// aspnet core startup
/// </summary>
public static IServiceCollection AddInlineBlockBreaker(this IServiceCollection services)
{
return services.Intercept<IContentAreaLoader>((provider, baseService) => {
return new BreakInlineBlocks(baseService);
});
}
}
Now, if we would modify our breaker class and add the final method in the IContentAreaLoader interface, everything works as it should:
public class BreakInlineBlocks : IContentAreaLoader
{
protected readonly IContentAreaLoader _baseLoader;
public BreakInlineBlocks(IContentAreaLoader baseLoader)
{
_baseLoader = baseLoader;
}
public IContent Get(ContentAreaItem contentAreaItem)
{
var content = _baseLoader.Get(contentAreaItem);
return content;
}
public IContentData LoadContent(ContentAreaItem contentAreaItem)
{
var content = _baseLoader.LoadContent(contentAreaItem);
return content;
}
public DisplayOption LoadDisplayOption(ContentAreaItem contentAreaItem)
{
return _baseLoader.LoadDisplayOption(contentAreaItem);
}
}
I can not test this myself, but I would first look at the default implementation of LoadContent in the interface:
public interface IContentAreaLoader
{
[Obsolete("Use LoadContent instead")]
IContent Get(ContentAreaItem contentAreaItem);
IContentData LoadContent(ContentAreaItem contentAreaItem)
{
return Get(contentAreaItem);
}
DisplayOption LoadDisplayOption(ContentAreaItem contentAreaItem);
}
If you compare the interface to the DefaultContentAreaLoader, you can see they do a whole lot of different things, and its a likely possibility it breaks as the default calls an obsolete method. I'm guessing this default implementation was done in an effort to avoid creating a breaking change?
EPiServer.Web.Internal.DefaultContentAreaLoader relevant methods for comparison:
public IContentData LoadContent(ContentAreaItem contentAreaItem)
{
return Load(contentAreaItem, _contextModeResolver.CurrentMode);
}
private IContentData Load(ContentAreaItem item, ContextMode contextMode)
{
if (item.InlineBlock != null) {
return item.InlineBlock;
}
bool flag = contextMode.EditOrPreview();
CultureInfo language = _languageAccessor.Language;
LoaderOptions loaderOptions = new LoaderOptions { flag ? LanguageLoaderOption.FallbackWithMaster(language) : LanguageLoaderOption.Fallback(language) };
IContent content = Get(item, loaderOptions);
if (contextMode != ContextMode.Preview || !IsMasterFallback(content, language)) {
return content;
}
return null;
}
Erik, the early exit in case of ContentAreaItem.InlineBlock is expected. And yes, the default implementation is our internal class that is not supposed to be changed. Of course with the IoC you can intercept and change our internal implementation but I'm not sure why you need this?
Yes, this feature is really killing our way of working. We are trying to upgrade to latest version but:
How has this been approved for general availability? Can we please have an option to get back to for this page blocks until this feature has been developed properly?
We hear you guys. A new version of CMS is coming soon, new inline blocks feature will be opt-in and will remain that way until we fix all the issues you all mentioned and submitted. Everything will work the same way as pre 12.21.0. A migration tool which converts inline blocks back to shared blocks is past QA and will be officially released today. It took us a while to realize we planned a very powerful feature in a very poor way. Very sorry for trouble. All future features which are so intruisive will be opt-in by default and will be consulted some time before the release to get feedback and allow you to plan to adjust delivery to new edit mode features accordingly.
I believe it's episerver/content-app-labs-block-enhancements: The Block Enhancements Lab Addon for CMS (.NET 6) (github.com)
I recently upgraded a website from cms 11 to 12. Site works fine using CMS and CMS.UI version 12.7.0.
After upgrading it to CMS 12.22.0 and CMS.UI 12.15.1, The Inline block system is breaking all content areas if used.
First creating an inline block seem to be working, it shows up in the content area.
After publishing the page however, when the UI reloads the edit interface, the following can be seen, with the inline block as null in the bottom:
If it try to edit this block from the ... menu, i get this:
So somehow the system doesnt recognise the concept of an inline block after saving it the first time and tries to load a block without any content link attached and obviosuly fails.
Any idea where to start debugging this?