Inline block feature breaking content areas in (upgraded) site

Vote:
 

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?

#304507
Jul 03, 2023 9:51
Paul McGann (Netcel) - Jul 03, 2023 15:49
Is there any errors logged to file system, browser window, etc?
Vote:
 

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!

#304514
Jul 03, 2023 13:31
Vote:
 

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.

#304515
Edited, Jul 03, 2023 13:40
Vote:
 

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)
#304540
Edited, Jul 03, 2023 16:13
Vote:
 

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()
#304542
Edited, Jul 03, 2023 16:42
Vote:
 

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...

#304543
Jul 03, 2023 16:48
Vote:
 

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.

#304544
Jul 03, 2023 22:11
Erik Täck - Jul 06, 2023 6:56
I would really like to see an option to select the default behavior of "Add new block" button in content areas - either the default "old" way, or this new inline block.
Vote:
 

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.

#304580
Jul 03, 2023 23:27
Vote:
 

I can confirm that this breaks permissions and the Welcome (CMP DAM) integration with a custom content area renderer.

#304741
Jul 06, 2023 0:23
Vote:
 

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;
        }
#304742
Edited, Jul 06, 2023 6:53
Vote:
 

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?

#304792
Jul 07, 2023 8:50
Erik Täck - Jul 07, 2023 11:54
Your'e missing the point:
If someone roll their own IContentAreaLoader, it is highly likely the default implementation of the method Load in the interface would not be noticed as code implementing the interface will compile after the upgrade even without adding the new method due to the default (incorrect) call to the obsolete Get method in the interface itself.
Vote:
 

Yes, this feature is really killing our way of working. We are trying to upgrade to latest version but:

  • When converting a block to inline, the name is discarded, but it's needed to distinguish between different blocks on the page. We might for example have multiple Carroussel blocks, but with different content.
  • In the latest version, on creating a block from a block content area, you are immediately making an inline block. With the aforementioned issue. Users are not used to this. In the previous release, this behavior was still adding a block "for this page". 
  • We have no option to turn this behavior off currently. So we can't update to the latest version, although it corrects the problem stated above creating inline blocks that kill the entire page.

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? 

#304997
Jul 11, 2023 15:11
Mark Stott - Jul 21, 2023 8:03
A lot of this same feedback is on the original blog post announcing this.
Vote:
 

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.

#306030
Aug 01, 2023 6:52
Vote:
 

Hi Bartosz, where can I find the migration tool that you mentioned?

#306684
Aug 12, 2023 14:25
This topic was created over six months ago and has been resolved. If you have a similar question, please create a new topic and refer to this one.
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.