November Happy Hour will be moved to Thursday December 5th.

DefaultContentAreaLoader does not load blocks from content area not having English language

Vote:
 

In one of the projects we took over recently there is a scheduled job which is indexing all the pages for search engine (not Find).

One of new features requested by customer was to have a page type strongly built with wide range of content blocks, so to be able to use values from blocks we had to pull some of the data from blocks inserted into content areas when indexing.

After initial release we noticed that sometimes the content is not loaded properly, then I started look into the extension method used there and it seems that DefaultContentAreaLoader have issues load blocks when called from scheduled job.

Steps to reproduce with Alloy site:

  1. Make sure to have enabled at least 2 languages (e.g. English and Swedish) and create start pages for each
  2. Make sure English start page have inserted into Large content area 3 blocks of type TeaserBlock
  3. Create for one of the Teaser blocks the Swedish language version and add it to Swedish start page
  4. Create 2 new Teaser blocks only in Swedish and add to Swedish start page
  5. Add class with extension methods

    	public static class ContentLoaderExtensions
    	{
    		public static IEnumerable<T> GetItemsWithContentLoader<T>(this IContentLoader contentLoader, ContentArea contentArea, CultureInfo language) where T : ContentData
    		{
    			if(contentLoader == null || contentArea == null)
    			{
    				return new List<T>();
    			}
    
    			return contentArea.FilteredItems
    				.Select(item => contentLoader.Get<ContentData>(item.ContentLink, language))
    				.Select(icontent => icontent as T)
    				.Where(result => result != null);
    		}
    
    		public static IEnumerable<T> GetItemsWithGetContentExtension<T>(this ContentArea contentArea) where T : ContentData
    		{
    			if (contentArea == null)
    			{
    				return new List<T>();
    			}
    
    			return contentArea.FilteredItems
    				.Select(item => item.GetContent())
    				.Select(icontent => icontent as T)
    				.Where(result => result != null);
    		}
    	}
  6. Add scheduled job

    	[ScheduledPlugIn(DisplayName = "Blocks loader job")]
    	public class BlocksLoaderJob : ScheduledJobBase
    	{
    		private readonly IContentLoader _contentLoader;
    		private readonly ILanguageBranchRepository _languageBranchRepository;
    		public BlocksLoaderJob(IContentLoader contentLoader, ILanguageBranchRepository languageBranchRepository)
    		{
    			_contentLoader = contentLoader;
    			_languageBranchRepository = languageBranchRepository;
    		}
    
    		public override string Execute()
    		{
    			StringBuilder builder = new StringBuilder();
    
    			foreach (var lang in _languageBranchRepository.ListEnabled())
    			{
    				var langVer = _contentLoader.Get<StartPage>(ContentReference.StartPage, lang.Culture);
    
    				if (langVer != null)
    				{
    					var blocks = _contentLoader.GetItemsWithContentLoader<TeaserBlock>(langVer.MainContentArea, lang.Culture).ToList();
    					var blocksLoadedByExt = langVer.MainContentArea.GetItemsWithGetContentExtension<TeaserBlock>().ToList();
    
    					builder.AppendLine($"Language {lang.Name}: <br /> Blocks with content loader: {blocks.Count()} | Blocks with content AREA loader: {blocksLoadedByExt.Count()} <br />");
    				}
    			}
    
    			return builder.ToString();
    		}
    	}
  7. Run the scheduled job
  8. Result is

    Language English:
    Blocks with content loader: 3 | Blocks with content AREA loader: 3
    Language svenska:
    Blocks with content loader: 3 | Blocks with content AREA loader: 1 ​

Simply we can see that when we call from scheduled job the way to load blocks with IContent GetContent(this ContentAreaItem item) extension blocks missing English language version are not loaded (only 1 block loaded for Swedish).
When instead of that we use the IContentLoader then it works correctly

I started looking at the extension and internally Episerver calls inside the GetContent()  method  IContent Get(ContentAreaItem contentAreaItem) from DefaultContentAreaLoader which internally uses language from IContentLanguageAccessor _languageAccessor and that seems to be always English when called from scheduled job.

Note: it works just fine when called e.g. when publishing page from publish content event.
As an effect in our solution every time we run the scheduled job blocks data was incomplete, but when called manual publish from CMS it was fixing problem (until next run from scheduled job).
It seems that calling ContentLoader.Service.Get<ContentData>(item.ContentLink) fixed all issues, but seems that implementation of the IContent GetContent(this ContentAreaItem item) have some bug?

#215780
Edited, Jan 13, 2020 21:00
Vote:
 

Hi Greg, check what is the language of the returned block when you use the item.GetContent(). When you say that you get only one block in Swedish language branch - are you sure that the returned block is actually the Swedish version and not the English version (as only one item exists in both Swedish and English).

#215783
Jan 14, 2020 5:12
Vote:
 

Hi Greg, seems something happened in the forum - my previous post disappeared :D

So I wrote that previously that check the language of the returned block(s), it might be that you are getting the english version blocks only when using the item.GetContent() (its actually using the language from the context and in scheduled jobs the *-host is used and if that is defined as english version, then that is used)

So I assumme that when you are iteraring the Swedish language branch blocks you are actually getting back the English version as master language fallback and in the Swedish version there is only the one block that is in Swedish and English. But have a look at the returned blocks language in debugger.

#215784
Jan 14, 2020 6:45
- Jan 14, 2020 6:47
Seems that adding this post my previous post re-appeared :D
Vote:
 

Thanks Antti good comment.
So actually it is so that item.GetContent() is able to return only items in English (so when called on Swedish it returns only blocks having both EN and SV, but the block returned is still English)
With IContentLoader it is also not solved fully at the end, when called on Swedish it returns correctly blocks having only Swedish version, but when block has both it is returned ... but still English

#215786
Jan 14, 2020 6:57
Vote:
 

Pass the language branches language also to the GetItemsWithContentLoader method and use that language in that method to create the language loaderoption - I think that should fix the issue.

Uncle Valdis will soon come here and comment that the extension method should be done against the IContentLoader so that you don't need to use that injected ContentLoader property in the extension method ;-) (besides you already have the IContentLoader in the scheduled job AND scheduled jobs support nowadays constructor injection so you don't even need to use the injected property in the scheduled job either.

#215787
Jan 14, 2020 7:04
Vote:
 

Updated the original code, changed the extension so it runs against the ContentLoader and also added language parameter (otherwise it still was loading English for blocks being with more language versions)

#215793
Edited, Jan 14, 2020 9:08
Vote:
 

Hi Grzegorz, Thanks for letting us know about the changes you made. Has that fixed the original problem?

#215806
Jan 14, 2020 16:40
Vote:
 

Hi Bob,
Well we workaround the problem with replacing the calls for extension method with ContentLoader (with language parameter version). But still how about the extension method for ContentAreaItem which is calling the method from DefaultContentAreaLoader? Shouldn't it still allow to load blocks also in other languages than default when called from scheduled job?

#215807
Jan 14, 2020 17:17
* 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.