<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">Blog posts by Daniel Ekeblad</title><link href="http://world.optimizely.com" /><updated>2025-03-21T18:34:21.0000000Z</updated><id>https://world.optimizely.com/blogs/daniel-ekeblad/</id> <generator uri="http://world.optimizely.com" version="2.0">Optimizely World</generator> <entry><title>What is ConstructorParameterResolver?</title><link href="https://world.optimizely.com/blogs/daniel-ekeblad/dates/2025/3/what-is-iconstructorparameterresolver/" /><id>&lt;p&gt;A few weeks ago I stumbled on a little-known feature and searching for it didn&#39;t reveal much about it. I managed to figure out how it&#39;s supposed to work and now I&#39;m sharing my findings.&lt;/p&gt;
&lt;h2&gt;What is the feature?&lt;/h2&gt;
&lt;p&gt;The feature consists of two types, the interface &lt;a href=&quot;/csclasslibraries/cms/EPiServer.Construction.IConstructorParameterResolver?version=12&quot;&gt;&lt;code&gt;EPiServer.Construction.IConstructorParameterResolver&lt;/code&gt;&lt;/a&gt; and the static class &lt;a href=&quot;/csclasslibraries/cms/EPiServer.Construction.ConstructorParameterResolver?version=12&quot;&gt;&lt;code&gt;EPiServer.Construction.ConstructorParameterResolver&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The type names should give you an idea about what they do, something related to dependency injection via constructors. To be exact: You need to use this feature to enable dependency injection with constructors on Content Types (like pages and blocks).&lt;/p&gt;
&lt;h2&gt;Constructors, on Content Types?&lt;/h2&gt;
&lt;p&gt;If you had asked me before discovering this feature I would never considered adding a constructor on a something like a page or block type and attempting to inject a service. I mean, they are data classes, they should have little or no logic. If I had to use a service I would have picked using ServiceLocator or Injected&amp;lt;&amp;gt;.&lt;/p&gt;
&lt;p&gt;I got the idea of trying to add a constructor not via services but when thinking about default values for properties. As you should know, you can&#39;t set default values for properties on content types with property initialization:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-C#&quot;&gt;public class ExamplePage : PageData
{
	// Does not work
	public virtual string Heading { get; set; } = &quot;Default Heading&quot;;

	// Do this instead
	public virtual string Heading { get; set; }
	public override void SetDefaultValues(ContentType contentType)
	{
		Heading = &quot;Default Value&quot;;
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I thought: &quot;If property initialization does not work, then initializing them inside a constructor probably does not work either.&quot; quickly followed by &quot;Constructors, on Content Types?&quot; before arriving at &quot;I assume they don&#39;t support dependency injection&quot;.&lt;/p&gt;
&lt;p&gt;Instead of just stopping there I tried to see what happens. What happens if I try to inject, say, IContentLoader into the constructor of &lt;code&gt;ExamplePage&lt;/code&gt;?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-C#&quot;&gt;public class ExamplePage : PageData
{
	private readonly IContentLoader _contentLoader;
	public ArticlePage(IContentLoader contentLoader)
	{
		_contentLoader = contentLoader;
	}

	public virtual string Heading { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Starting the site and trying to load a page of this types throws, as I expected, an exception. But I did not see the last part of the exception message coming: &lt;code&gt;InvalidOperationException: Type &#39;SomeSite.Models.Pages.ExamplePage&#39; does not have any public default constructor and no IConstructorParameterResolver that handles it&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;How do I use IConstructorParameterResolver?&lt;/h2&gt;
&lt;p&gt;Upon seeing the above message I tried searching for the type on the Internet. Except for finding the CsClassLibraries page and it being mentioned in the middle of a stacktrace of an otherwise unrelated question here on Optimizely World I ended up empty-handed.&lt;/p&gt;
&lt;p&gt;From the CsClassLibraries page I found the static class ConstructorParameterResolver. From these two pages and some help from Visual Studio I arrived at the following implementation:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-C#&quot;&gt;public class MyConstructorParameterResolver : IConstructorParameterResolver
{
	private readonly IServiceScopeFactory _serviceScopeFactory;

	public MyConstructorParameterResolver(IServiceScopeFactory serviceScopeFactory)
	{
		_serviceScopeFactory = serviceScopeFactory;
	}

	public bool TryResolveArguments(Type typeToConstruct, Type[] constructorArguments, out object[] instances)
	{
		using var scope = _serviceScopeFactory.CreateScope();
		instances = new object[constructorArguments.Length];
		for (int i = 0; i &amp;lt; constructorArguments.Length; i++)
		{
			var serviceInstance = scope.ServiceProvider.GetRequiredService(constructorArguments[i]);
			instances[i] = serviceInstance;
		}

		return true;
	}
}

[ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
public class MyInitModule : IInitializableModule
{
	public void Initialize(InitializationEngine context)
	{
		var scopeFactory = context.Locate.Advanced.GetRequiredService&amp;lt;IServiceScopeFactory&amp;gt;();
		var paramResolver = new MyConstructorParameterResolver(scopeFactory);
		// Note: If you content types area spread out over multiple
		// projects/assemblies then you need to register them all one by one.
		ConstructorParameterResolver.Instance.RegisterResolver(paramResolver, typeof(ExamplePage).Assembly);
	}

	public void Uninitialize(InitializationEngine context)
	{
	}
}
&lt;/code&gt;    &lt;/pre&gt;
&lt;p&gt;Assuming I haven&#39;t done a copy-paste-edit mistake this compiles and the site starts.&lt;/p&gt;
&lt;h2&gt;Can I use it?&lt;/h2&gt;
&lt;p&gt;According to CsClassLibraries these classes have existed since CMS 7, however...&lt;/p&gt;
&lt;p&gt;When I tried to load a page of the &lt;code&gt;ExamplePage&lt;/code&gt; type again to test the code above I got a different exception: &lt;code&gt;MissingMethodException: Constructor on type &#39;SomeSite.Models.Pages.ExamplePage_DynamicProxy&#39; not found.&lt;/code&gt; The constructor does not exist on the generated DynamicProxy class?&lt;/p&gt;
&lt;p&gt;I asked the support and they replied the implementation above should work, this is considered a bug and has been assigned id &lt;a href=&quot;/support/Bug-list/bug/CMS-39932&quot;&gt;CMS-39932&lt;/a&gt; and has existed since at least episerver.cms.core 12.19.0. (The earliest 12.x version I have tested with.)&lt;/p&gt;
&lt;h2&gt;Should I use it?&lt;/h2&gt;
&lt;p&gt;Personally, I will continue using ServiceLocator, Injected&amp;lt;&amp;gt; or providing a service as a method parameter in the rare cases I want one inside a content type. I would avoid constructor injection, it requires this mysterious IConstructorParameterResolver to be implemented and registered somewhere. Not only does constructors (at least to me) look strange on content types, something that looks normal and should just work ends up throwing exceptions for content types specifically.&lt;/p&gt;
&lt;p&gt;However, I can also imagine there being scenarios where a ConstructorParameterResolver is needed to get a very special thing to work the way you want. In the test implementation above I just try resolving all requested types from a service scope but there may be cases where you can&#39;t register some types for DI or that you want to resolve the service and apply some special configuration to it before handing the instance to the content type. That kind of logic can be placed in this class.&lt;/p&gt;
&lt;p&gt;Even if you will use it or not, you probably just learned about a new feature in Optimizely CMS. If you used &lt;code&gt;IConstructorParameterResolver&lt;/code&gt; before, feel free to share how you used it in the comments.&lt;/p&gt;</id><updated>2025-03-21T18:34:21.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Avoid Nesting Blocks – Flatten Into a Single Content Area</title><link href="https://world.optimizely.com/blogs/daniel-ekeblad/dates/2024/1/avoid-nesting-blocks--flatten-into-a-single-content-area/" /><id>&lt;p&gt;Blocks inside blocks is an easy way to structure data in the CMS that depends on other data. Easy for us developers that is, it only takes two or three levels of nesting before it becomes difficult for editors to work with and all the stepping in and out of blocks will quickly make them forget where in the structure they are and they feel lost.&lt;/p&gt;
&lt;p&gt;After taking a quick look at what the CMS offers today to make this easier I will show a solution we have come up with that puts everything in one content area to give a easy overview while at the same time keeping the nested relationship clear.&lt;/p&gt;
&lt;h1&gt;What the CMS offers&lt;/h1&gt;
&lt;p&gt;Here are three features offered by Optimizely CMS that can be used to make it easier to work with nested content, but in the end is it still nested blocks.&lt;/p&gt;
&lt;h2&gt;Block properties&lt;/h2&gt;
&lt;p&gt;Oldest of the three features is the ability to use block types as properties on other content types. This type of properties render the properties of a block like they are a part of the content type the property is in. While more often used by developers to avoid repeating themselves, block properties are like an alternative to using a content reference to a block.&lt;/p&gt;
&lt;h2&gt;Quick edit&lt;/h2&gt;
&lt;p&gt;Quick edit helps to reduce the confusion of nesting blocks by making it possible to edit blocks in a content area inside a pop-up. (Added in CMS UI 11.32.0, released in January 2021)&lt;/p&gt;
&lt;h2&gt;List properties&lt;/h2&gt;
&lt;p&gt;List properties are not new but in 12.18.0 (released in March 2023) &lt;a href=&quot;/link/8ba16a78985e4446bf36669a38308eb4.aspx&quot;&gt;it got a big update&lt;/a&gt;. You can now add a list of blocks to a content type and edit them all as a part of the page. If you have not seen this feature yet it&#39;s like a content area but it&#39;s content is rendered like block properties. It&#39;s a very nice feature, but not ideal for nesting. When I experimented with this feature a couple of months ago I placed a block list property inside a block I used as a block list property and quickly found myself unsure of what outer block I was adding inner blocks to and scrolling a lot up and down in the editing interface. I also encountered and reported a bug that caused problems with saving when editing inner blocks in this kind of setup.&lt;/p&gt;
&lt;h1&gt;What we built&lt;/h1&gt;
&lt;p&gt;The built in features listed above have their use cases and limitations, for example can you not reuse block- and list-properties. We have come up with a design where we flatten a three level structure into a single content area using special purpose blocks. We use this technique on pages with a flexible design where the page is rendered with one or more sections, sections containing one or more columns and all blocks fall into one of the columns.&lt;/p&gt;
&lt;p&gt;Take a look at the image below showing a drawing how one of our customers use this system. A section of a page with two columns, the first with a width of 2/3 and the second 1/3. The wider column has four big links to popular information pages and the smaller column have smaller has a single block with links to various sections and services on the site.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/dfb619e35b1c48479e0eea214714cdc9.aspx&quot; width=&quot;800&quot; alt=&quot;&quot; height=&quot;415&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Many sites probably implements this by adding a content area to section blocks for column blocks and a content area in column blocks for other blocks. The content area on this page looks like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Section block&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Column block (2/3)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Information page 1&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Information page 2&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Information page 3&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Information page 4&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Column block (1/3)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Quick links block&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This puts everything in-front of the editors, no need to step in and out of blocks to find where a certain block is located and moving blocks between columns or sections is as easy as reordering them inside the content area.&lt;/p&gt;
&lt;p&gt;When it&#39;s time to render the page we have a method that loads the content area items and puts blocks inside their column and columns inside their section. In our case we deliver JSON to our frontend and the agreed contract says it&#39;s a three level structure. If you render in razor views or something else you will probably find that converting from a flat structure to a three level one at some point will make the rendering easier.&lt;/p&gt;
&lt;p&gt;However, even this solution has it&#39;s drawbacks. I have seen a page on the previously mentioned site with a content area that has a height greater than two times of my monitor. If an editor is looking for, say, the 5th block in the 1st column of the 4th section they have to slowly scan the content area to locate the start of the 4th section and then count to the 6th block. (1 column block and then another 5 blocks.)&lt;/p&gt;
&lt;p&gt;When we began building a new site a couple of months ago I decided to look into how to make large content areas easier to overlook and found that styling content area items to be easy and effective. In the image below it&#39;s with a quick glance clear what is a section, a column or another kind of block. Looking for the beginning of the nth section? Just count the darker colored blocks.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/b78d086c3f8842d3a84d7def651418d1.aspx&quot; width=&quot;658&quot; alt=&quot;&quot; height=&quot;558&quot; /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;After the section in the image you could for example add one with a single full-width column and after that a section with two half-width.&lt;/p&gt;
&lt;h1&gt;Implementation&lt;/h1&gt;
&lt;p&gt;To create the effect above you need to do four things.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Define the content types, in our scenario for section and column blocks&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Assign an IconClass to section and column blocks using UI descriptors.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Write CSS rules targeting content area items with the specific icon classes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Register the CSS file for Optimizely to load when an editor is working in the CMS, if the project does not already have one, and add the CSS rules to said file.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Defining section and column blocks&lt;/h2&gt;
&lt;p&gt;These are two very basic type of blocks and don&amp;rsquo;t need a lot of properties. Sections could have a background color option and columns need a width selection. We use content events to automatically add a columns selected width to it&amp;rsquo;s name to show editors the most important information without they having to step into (or quick edit) the block.&lt;/p&gt;
&lt;h2&gt;Adding icon classes&lt;/h2&gt;
&lt;p&gt;Adding icons to content types makes them easier to distinguish from each other and is one of the few ways to add developer-controlled data to the html of content area properties in the CMS. Icons are added using &lt;a href=&quot;https://docs.developers.optimizely.com/content-management-system/docs/custom-editing-preview-for-media#enable-on-page-edit-view&quot;&gt;UIDescriptor&lt;/a&gt;s:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;using EpiServer.Shell;
using MySite.Blocks;

namespace MySite.Business.UIDescriptors;

[UIDescriptorRegistration]
public class SectionUIDescriptor : UIDescriptor&amp;lt;SectionBlock&amp;gt;
{
    public SectionUIDescriptor() : base(&quot;content-icon-section&quot;)
    {}
}

// Repeat for ColumnBlock&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Creating CSS rules&lt;/h2&gt;
&lt;p&gt;The CSS used to create what you saw in the image above can be seen below. The screenshot was taken on a site running Episerver.CMS 12.24 and has not been tested on early 12.x or prior mayor versions.&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code&gt;/* &amp;lt;projectRoot&amp;gt;/wwwroot/Styles/editmode.css */

/* Give the special marker blocks (section and column) a custom background in content areas */

span:has(.content-icon-section) {
    background-color: #ccd7ff!important;
}

span:has(.content-icon-column) {
    background-color: #f0f3ff!important
}

/* Make it look like columns are a level below sections and all other blocks are a level below columns (in content areas) */

.epi-content-area-editor .dijitIcon.dijitTreeIcon.dijitLeaf.epi-objectIcon {
    margin-left: 34px;
}

.epi-content-area-editor .dijitIcon.dijitTreeIcon.dijitLeaf.content-icon-section.epi-objectIcon {
    margin-left: 0px;
}

.epi-content-area-editor .dijitIcon.dijitTreeIcon.dijitLeaf.content-icon-column.epi-objectIcon {
    margin-left: 17px;
}

/* Custom content icons (appearing to the left in the page tree, assets pane, content area items) */
/* Optimizely uses &#39;Font Awesome 6 Pro&#39; for some of their icons, no need to add that font explicitly */

.content-icon-section::before {
    padding-right: 5px;
    content: &quot;\f2fc&quot;;
    font-family: &#39;Font Awesome 6 Pro&#39;;
}

.content-icon-column::before {
    padding-right: 5px;
    content: &quot;\e361&quot;;
    font-family: &#39;Font Awesome 6 Pro&#39;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Registering a CSS file&lt;/h2&gt;
&lt;p&gt;&lt;span&gt;To tell Optimizely CMS that we have a CSS file we want to be loaded by the CMS we have to register it in &lt;a href=&quot;https://docs.developers.optimizely.com/content-management-system/docs/configuring-moduleconfig&quot;&gt;module.config&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;language-markup&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&amp;gt;
&amp;lt;!-- &amp;lt;projectRoot&amp;gt;/module.config --&amp;gt;
&amp;lt;module&amp;gt;
    &amp;lt;clientResources&amp;gt;
        &amp;lt;add name=&quot;epi-cms.widgets.base&quot; path=&quot;/Styles/editmode.css&quot; resourceType=&quot;Style&quot;/&amp;gt;
    &amp;lt;/clientResources&amp;gt;
&amp;lt;/module&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Finishing up&lt;/h1&gt;
&lt;p&gt;As I quickly mentioned in the section &amp;ldquo;Adding Icon classes&amp;rdquo; are content areas rather restricted. They are powerful and can do a lot, but the implementation is classified as internal and difficult to extend without resorting to ugly or hacky solutions that could break at any time without notice. Thanks to the icons we got something easily added to target in CSS and because both the coloring and the icons depends on the content type it&#39;s perfect for this scenario. I was worried that the coloring or indendation would appear in the assets pane or page tree but that was not a problem. The icon is applied everywhere but the CSS earlier only sets background color and indentation for items in content areas.&lt;/p&gt;
&lt;p&gt;Have you ever felt lost in heavily nested blocks? Have you tried searching for a solution? If you know of another interesting way to solve this problem please share it in the comments.&lt;/p&gt;
&lt;p&gt;Someone has pointed out that compared to nested blocks you can not quickly change order of columns or sections, you have to move one block at a time. I have begun looking into it a little and if I find something interesting then maybe I will write a blog post about it.&lt;/p&gt;</id><updated>2024-01-02T14:13:05.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Include permissions (ACL) when importing content</title><link href="https://world.optimizely.com/blogs/daniel-ekeblad/dates/2022/9/include-permissions-acl-when-importing-content/" /><id>&lt;p&gt;I recently had to import about 500 pages all with their own specific settings on what roles have edit persmission. After trying a couple of different search terms I found an old blog post by Johan Bj&amp;ouml;rnfot &lt;a href=&quot;/link/d2781edca75d4faf9239376c1e185abc.aspx&quot;&gt;https://world.optimizely.com/Blogs/Johan-Bjornfot/Dates1/2011/3/Include-ACL-during-Import-Mirroring-and-Copy/&lt;/a&gt; that had a code snippet that hooks into the built in import feature and sets the access rights, the only downside being it was written for Episerver CMS 5 and 6 and can&#39;t just be copy-pasted.&lt;/p&gt;
&lt;p&gt;While it did have a lot of errors when added to a project with Episerver 11 it offered a great starting point. The upgraded version (see below) was actually easy to create. Thanks to the Optimizely developers including helpful &quot;please dont ues this, use that instead&quot;-warnings as well as providing the events that makes this possible!&lt;/p&gt;
&lt;p&gt;If you want to know what more can be done with these events you can take a look at this blog post by Antti Alasvuo &lt;a href=&quot;/link/2df42d901326489c96cf1a401e6ab546.aspx&quot;&gt;https://world.optimizely.com/blogs/Antti-Alasvuo/Dates/2021/1/migrating-episerver-content-with-custom-import-processing/&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;using EPiServer.Core;
using EPiServer.Core.Transfer;
using EPiServer.DataAbstraction;
using EPiServer.Enterprise;
using EPiServer.Enterprise.Transfer;
using EPiServer.Framework;
using EPiServer.Framework.Initialization;
using EPiServer.Security;
using EPiServer.ServiceLocation;
using EPiServer.Web;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Plugins.AclImporter
{
	/// &amp;lt;summary&amp;gt;
	/// &amp;lt;para&amp;gt;
	/// Registers event handlers for when content is imported with the built in import data admin tool.
	/// This module extends the import to include ACL (permissions) for imported content.
	/// This is not handled by default and is the same as the parent content.
	/// &amp;lt;/para&amp;gt;
	/// &amp;lt;para&amp;gt;
	/// This code is an update version of code written by Johan Bj&amp;ouml;rnfot for Episerver 5 and 6.
	/// Source: https://world.optimizely.com/Blogs/Johan-Bjornfot/Dates1/2011/3/Include-ACL-during-Import-Mirroring-and-Copy/
	/// &amp;lt;/para&amp;gt;
	/// &amp;lt;/summary&amp;gt;
	[InitializableModule]
	public class AclImporterModule : IInitializableModule
	{
		//The module will be a singleton so it needs to be thread safe
		private readonly object _lockObject = new object();
		private readonly Dictionary&amp;lt;ITransferContext, AclImporter&amp;gt; _aclImporters =
			new Dictionary&amp;lt;ITransferContext, AclImporter&amp;gt;();

		public void Initialize(InitializationEngine context)
		{
			var dataImportEvents = context.Locate.Advanced.GetInstance&amp;lt;IDataImportEvents&amp;gt;();

			dataImportEvents.Starting += new ImportingEventHandler(DataImportEvents_Starting);
			dataImportEvents.Completed += new ImportingEventHandler(DataImportEvents_Completed);
			dataImportEvents.ContentImporting += DataImportEvents_ContentImporting;
			dataImportEvents.ContentImported += DataImportEvents_ContentImported;
		}

		public void Preload(string[] parameters)
		{
		}

		public void Uninitialize(InitializationEngine context)
		{
			var dataImportEvents = context.Locate.Advanced.GetInstance&amp;lt;IDataImportEvents&amp;gt;();

			dataImportEvents.Starting -= new ImportingEventHandler(DataImportEvents_Starting);
			dataImportEvents.Completed -= new ImportingEventHandler(DataImportEvents_Completed);
			dataImportEvents.ContentImporting -= DataImportEvents_ContentImporting;
			dataImportEvents.ContentImported -= DataImportEvents_ContentImported;
		}

		private void DataImportEvents_Starting(ITransferContext transferContext, DataImporterContextEventArgs e)
		{
			lock (_lockObject)
			{
				_aclImporters.Add(transferContext, new AclImporter(transferContext));
			}
		}

		private void DataImportEvents_Completed(ITransferContext transferContext, DataImporterContextEventArgs e)
		{
			lock (_lockObject)
			{
				_aclImporters.Remove(transferContext);
			}
		}

		private void DataImportEvents_ContentImporting(ITransferContext transferContext, ContentImportingEventArgs e)
		{
			AclImporter aclImporter;
			lock (_lockObject)
			{
				aclImporter = _aclImporters[transferContext];
			}

			aclImporter.ImportingContent(e.TransferContentData);
		}

		private void DataImportEvents_ContentImported(ITransferContext transferContext, ContentImportedEventArgs e)
		{
			AclImporter aclImporter;
			lock (_lockObject)
			{
				aclImporter = _aclImporters[transferContext];
			}

			aclImporter.ImportedContent(e.ContentLink);
		}
	}

	public class AclImporter
	{
		private readonly Dictionary&amp;lt;Guid, RawACE[]&amp;gt; _importedAce = new Dictionary&amp;lt;Guid, RawACE[]&amp;gt;();
		private readonly ITransferContext _context;
		private readonly IPermanentLinkMapper _mapper;
		private readonly IContentSecurityRepository _contentSecurity;

		public AclImporter(ITransferContext context)
		{
			_context = context;
			_mapper = ServiceLocator.Current.GetInstance&amp;lt;IPermanentLinkMapper&amp;gt;();
			_contentSecurity = ServiceLocator.Current.GetInstance&amp;lt;IContentSecurityRepository&amp;gt;();
		}

		internal void ImportingContent(EPiServer.Core.RawPage rawPage)
		{
			_importedAce[GetContentGuid(rawPage)] = rawPage.ACL;
		}

		internal void ImportingContent(ITransferContentData transferContentData)
		{
			var rawData = transferContentData.RawContentData;
			_importedAce[GetContentGuid(rawData)] = rawData.ACL;
		}

		internal void ImportedContent(ContentReference contentReference)
		{
			//PageData pageData = _contentLoader.Get&amp;lt;PageData&amp;gt;(contentReference);

			//Note: We can not use pageData.PageGuid here since 
			//PageData instance is not complete
			PermanentLinkMap pageMap = _mapper.Find(contentReference);
			Guid oldPageGuid = _context.LinkGuidMap.First(pair =&amp;gt; pair.Value == pageMap.Guid).Key;
			RawACE[] aces = _importedAce[oldPageGuid];

			var importedAcl = new ContentAccessControlList(aces)
			{
				ContentLink = contentReference
			};

			_contentSecurity.Save(contentReference, importedAcl, SecuritySaveType.Replace);
		}

		private Guid GetContentGuid(RawContent rawContent)
		{
			return new Guid(rawContent.Property.Single(prop =&amp;gt; prop.Name == &quot;PageGUID&quot;).Value);
		}
	}
}&lt;/code&gt;&lt;/pre&gt;</id><updated>2022-09-11T18:45:02.0000000Z</updated><summary type="html">Blog post</summary></entry></feed>