<?xml version="1.0" encoding="utf-8"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><language>en</language><title>Blog posts by Bartosz Sekula</title> <link>https://world.optimizely.com/blogs/bartosz-sekula/</link><description></description><ttl>60</ttl><generator>Optimizely World</generator><item> <title>Side by side editing - stable release</title>            <link>https://bartoszsekula.com/post/side-by-side-editing-stable-release</link>            <description>Combining the All Properties Mode with a Live Preview Stable Release</description>            <guid>https://bartoszsekula.com/post/side-by-side-editing-stable-release</guid>            <pubDate>Tue, 10 Jun 2025 00:00:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>Side by side editing - alternative to Optimizely On-Page Edit</title>            <link>https://bartoszsekula.com/post/side-by-side-editing</link>            <description>Combining the All Properties Mode with a self-updating preview in the Optimizely CMS</description>            <guid>https://bartoszsekula.com/post/side-by-side-editing</guid>            <pubDate>Thu, 09 Jan 2025 00:00:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>How to add an Admin Mode add-on in Optimizely CMS12</title>            <link>https://bartoszsekula.com/post/how-to-add-admin-mode-addon-in-optimizely-episerver-cms12</link>            <description>How to add a new add-on with navigation and unified stylesheet</description>            <guid>https://bartoszsekula.com/post/how-to-add-admin-mode-addon-in-optimizely-episerver-cms12</guid>            <pubDate>Thu, 02 Jan 2025 00:00:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>Useful Optimizely CMS Web Components</title>            <link>https://bartoszsekula.com/post/useful-optimizely-web-components</link>            <description>A list of useful Optimizely CMS components that can be used in add-ons.</description>            <guid>https://bartoszsekula.com/post/useful-optimizely-web-components</guid>            <pubDate>Wed, 18 Dec 2024 00:00:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>CMS + CMP + Graph integration</title>            <link>https://world.optimizely.com/blogs/bartosz-sekula/dates/2024/11/cms--cmp--graph-integration/</link>            <description>&lt;p&gt;We have just released a new package &lt;a href=&quot;https://nuget.optimizely.com/package/?id=EPiServer.Cms.WelcomeIntegration.Graph&quot;&gt;https://nuget.optimizely.com/package/?id=EPiServer.Cms.WelcomeIntegration.Graph&lt;/a&gt; which changes the way CMS fetches CMP Asset metadata.&lt;/p&gt;
&lt;p&gt;If you use the CMP it means that you also have access to Graph and can fetch metadata from it instead of relying on the CMP API.&lt;/p&gt;
&lt;p&gt;There are a few steps required in order to start using the new package&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://nuget.optimizely.com/package/?id=EPiServer.Cms.WelcomeIntegration.Graph&quot;&gt;https://nuget.optimizely.com/package/?id=EPiServer.Cms.WelcomeIntegration.Graph&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Existing EPiServer.Cms.WelcomeIntegration packages need to be updated to version 1.4.0&lt;/li&gt;
&lt;li&gt;EPiServer.Cms.WelcomeIntegration.Graph needs to be installed&lt;/li&gt;
&lt;li&gt;GraphClient needs to be configured in appsettings.json in the following way:
&lt;pre class=&quot;code-java&quot;&gt;&lt;span class=&quot;code-quote&quot;&gt;&quot;Optimizely&quot;&lt;/span&gt;: {
  &lt;span class=&quot;code-quote&quot;&gt;&quot;ContentGraph&quot;&lt;/span&gt;: {
    &lt;span class=&quot;code-quote&quot;&gt;&quot;GatewayAddress&quot;&lt;/span&gt;: &lt;span class=&quot;code-quote&quot;&gt;&quot;https:&lt;span class=&quot;code-comment&quot;&gt;//cg.optimizely.com&quot;&lt;/span&gt;,
&lt;/span&gt;    &lt;span class=&quot;code-quote&quot;&gt;&quot;SingleKey&quot;&lt;/span&gt;: &lt;span class=&quot;code-quote&quot;&gt;&quot;SINGLE_KEY_FROM_CMP&quot;&lt;/span&gt;,
    &lt;span class=&quot;code-quote&quot;&gt;&quot;QueryPath&quot;&lt;/span&gt;: &lt;span class=&quot;code-quote&quot;&gt;&quot;content/v2&quot;&lt;/span&gt;,
    &lt;span class=&quot;code-quote&quot;&gt;&quot;UseHmacKey&quot;&lt;/span&gt;: &lt;span class=&quot;code-keyword&quot;&gt;false&lt;/span&gt;
  }
}&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Make sure there is&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong&gt;services.AddDAMUi(o =&amp;gt; o.Enabled = true)&amp;nbsp;&lt;/strong&gt;call in Startup.cs&lt;/li&gt;
&lt;li&gt;Add&amp;nbsp;&lt;strong&gt;services.AddDAMGraphIntegration();&amp;nbsp;&lt;/strong&gt;call to Startup.cs&lt;/li&gt;
&lt;li&gt;&lt;span&gt;If customer already has a CMS to Graph integration then it means he already has this config in appsettings&lt;/span&gt;
&lt;pre class=&quot;code-java&quot;&gt;&lt;span class=&quot;code-quote&quot;&gt;&quot;Optimizely&quot;&lt;/span&gt;: {
&amp;nbsp; &lt;span class=&quot;code-quote&quot;&gt;&quot;ContentGraph&quot;&lt;/span&gt;: {
&amp;nbsp; &amp;nbsp; &lt;span class=&quot;code-quote&quot;&gt;&quot;GatewayAddress&quot;&lt;/span&gt;: &lt;span class=&quot;code-quote&quot;&gt;&quot;https:&lt;span class=&quot;code-comment&quot;&gt;//cg.optimizely.com&quot;&lt;/span&gt;,
&lt;/span&gt;&amp;nbsp; &amp;nbsp; &lt;span class=&quot;code-quote&quot;&gt;&quot;AppKey&quot;&lt;/span&gt;: &lt;span class=&quot;code-quote&quot;&gt;&quot;APPKEY&quot;&lt;/span&gt;,
    &lt;span class=&quot;code-quote&quot;&gt;&quot;Secret&quot;&lt;/span&gt;: &lt;span class=&quot;code-quote&quot;&gt;&quot;SECRET&quot;&lt;/span&gt;,
&amp;nbsp; &amp;nbsp; &lt;span class=&quot;code-quote&quot;&gt;&quot;QueryPath&quot;&lt;/span&gt;: &lt;span class=&quot;code-quote&quot;&gt;&quot;content/v2&quot;&lt;/span&gt;
&amp;nbsp; }
}&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Those AppKey&amp;amp;Secret allow to read&amp;amp;write to a CMS schema.&amp;nbsp;But our new CMP+Graph integration uses different schema which is now available with the CMS keys.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;The following config needs to be added to appsettings.json&lt;br /&gt;
&lt;pre class=&quot;code-java&quot;&gt;&lt;span class=&quot;code-quote&quot;&gt;&quot;EPiServer&quot;&lt;/span&gt;: {
&amp;nbsp; &lt;span class=&quot;code-quote&quot;&gt;&quot;Cms&quot;&lt;/span&gt;: {
    &lt;span class=&quot;code-quote&quot;&gt;&quot;CMPGraph&quot;&lt;/span&gt;: {
       &lt;span class=&quot;code-quote&quot;&gt;&quot;SingleKey&quot;&lt;/span&gt;: &lt;span class=&quot;code-quote&quot;&gt;&quot;SINGLE_KEY_FROM_CMP&quot;&lt;/span&gt;
    }
  }
}&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Or to Startup.cs&lt;br /&gt;
&lt;pre class=&quot;code-java&quot;&gt;services.Configure&amp;lt;CMPGraphOptions&amp;gt;(options =&amp;gt;
{
    options.SingleKey = &lt;span class=&quot;code-quote&quot;&gt;&quot;YOUR_KEY_FROM_CMP&quot;&lt;/span&gt;;
});&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span&gt;In case the new package is not installed then it would still work as before, meaning all metadata will be fetched from the CMP API.&lt;/span&gt;&lt;/p&gt;</description>            <guid>https://world.optimizely.com/blogs/bartosz-sekula/dates/2024/11/cms--cmp--graph-integration/</guid>            <pubDate>Tue, 05 Nov 2024 12:30:42 GMT</pubDate>           <category>Blog post</category></item><item> <title>How to hide Reports from the global menu in Optimizely CMS</title>            <link>https://bartoszsekula.com/post/2024/08/08/how-to-hide-reports-from-the-global-menu-in-optimizely-cms</link>            <description>&lt;p&gt;Reports section is available by default to all users who are members of the WebEditors group as the authorization policy of that MenuItem is set to&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;pre&gt;AuthorizationPolicy = CmsPolicyNames.CmsEdit,&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;So basically Reports section comes always together with the Edit Mode. However, I can imagine that sometimes reports are only useful to only a few specific editors and can be hidden from all others.&lt;/p&gt;
&lt;p&gt;Unfortunately it is not possible to configure this anywhere but thanks to Optimizely&#39;s great and powerful extensibility it is possible to accomplish that requirement with just a few lines of code:&lt;/p&gt;
&lt;pre class=&quot;brush:csharp;auto-links:false;toolbar:false&quot; contenteditable=&quot;false&quot;&gt;public class CustomMenuAssembler : MenuAssembler
{
	private readonly IPrincipalAccessor _principalAccessor;

	public CustomMenuAssembler(IEnumerable&amp;lt;IMenuProvider&amp;gt; menuProviders, IHttpContextAccessor httpContextAccessor,
		IPrincipalAccessor principalAccessor) : base(menuProviders, httpContextAccessor)
	{
		_principalAccessor = principalAccessor;
	}

	public override IEnumerable&amp;lt;MenuItem&amp;gt; GetMenuItems(string parentPath, int relativeDepth)
	{
		var menuItems = base.GetMenuItems(parentPath, relativeDepth).ToList();

		if (_principalAccessor.Principal.IsInRole(&quot;HasAccessToReportsGroup&quot;) == false)
		{
			return menuItems;
		}

		var reportItem =
			menuItems.SingleOrDefault(x =&amp;gt; x.Path.Equals(&quot;/global/cms/report&quot;, StringComparison.Ordinal));

		if (reportItem == null)
		{
			return menuItems;
		}

		// Add user group check here
		menuItems.Remove(reportItem);

		return menuItems;
	}
}&lt;/pre&gt;
&lt;p&gt;So we create a new MenuAssembler which inherits from the default one and we pass the additional IPrincipalAccessor reference to it.&lt;/p&gt;
&lt;p&gt;In my example I created a custom user group called &lt;em&gt;HasAccessToReportsGroup&lt;/em&gt; . If an editor does not belong to this group then he/she will not see the Reports menu item.&lt;/p&gt;
&lt;p&gt;Last we need to register this custom menu assembler and we need to make sure we do it after the built-in cms initialization modules are finished. Let&#39;s add a custom initializable module:&lt;/p&gt;
&lt;pre class=&quot;brush:csharp;auto-links:false;toolbar:false&quot; contenteditable=&quot;false&quot;&gt;[InitializableModule]
[ModuleDependency(typeof(InitializableModule))]
public class DependencyResolverInitialization : IConfigurableModule
{
	public void ConfigureContainer(ServiceConfigurationContext context)
	{
		context.ConfigurationComplete += (_, _) =&amp;gt;
		{
			context.Services.AddSingleton&amp;lt;MenuAssembler, CustomMenuAssembler&amp;gt;();
		};
	}

	public void Initialize(InitializationEngine context)
	{

	}

	public void Uninitialize(InitializationEngine context)
	{

	}
}&lt;/pre&gt;
&lt;p&gt;The same approach can be used to conditionally hide all other menu items, you just need to change the path.&lt;/p&gt;
&lt;p&gt;Please find the list of other paths:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;/global/cms/report&lt;/li&gt;
&lt;li&gt;/global/cms/dashboard&lt;/li&gt;
&lt;li&gt;/global/cms/visitorgroups&lt;/li&gt;
&lt;/ul&gt;</description>            <guid>https://bartoszsekula.com/post/2024/08/08/how-to-hide-reports-from-the-global-menu-in-optimizely-cms</guid>            <pubDate>Thu, 08 Aug 2024 19:47:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>How to prevent publishing a page with unpublished blocks or assets</title>            <link>https://world.optimizely.com/blogs/bartosz-sekula/dates/2024/2/validate-unpublished-page-dependencies/</link>            <description>&lt;p&gt;EPiServer has a very powerful validation engine which is easily pluggable with custom validation rules.&lt;/p&gt;
&lt;p&gt;I&#39;ve been asked by a colleague to write a validation rule which would prevent a page from being published if it referenced an unpublished dependency (block/asset/...).&lt;/p&gt;
&lt;p&gt;I thought it might be useful for some of you.&lt;/p&gt;
&lt;p&gt;We need to hook to IContentEvents.PublishingEvent. We can do that from an initializable module:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;[InitializableModule]
[ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
public class PublishEventInitializationModule : IInitializableModule
{
    private DependenciesResolver dependenciesResolver;

    public void Initialize(InitializationEngine context)
    {
        //Add initialization logic, this method is called once after CMS has been initialized
        var contentEvents = ServiceLocator.Current.GetInstance&amp;lt;IContentEvents&amp;gt;();
        contentEvents.PublishingContent += contentEvents_PublishingContent;

        dependenciesResolver = ServiceLocator.Current.GetInstance&amp;lt;DependenciesResolver&amp;gt;();
    }

    void contentEvents_PublishingContent(object sender, EPiServer.ContentEventArgs e)
    {
        var dependencies = dependenciesResolver.GetUnpublishedDependencies(e.ContentLink).ToList();
        if (!dependencies.Any()) return;

        var text = dependencies.Count == 1 ? &quot;dependency&quot; : &quot;dependencies&quot;;
        e.CancelAction = true;
        e.CancelReason =
            $&quot;You can&#39;t publish because you have {dependencies.Count} unpublished {text}. {string.Join(&#39;,&#39;, dependencies.Select(x =&amp;gt; $&quot;{x.Name} [{x.ContentLink}] &quot;))}&quot;;
    }

    public void Preload(string[] parameters)
    {
    }

    public void Uninitialize(InitializationEngine context)
    {
        //Add uninitialization logic
        var contentEvents = ServiceLocator.Current.GetInstance&amp;lt;IContentEvents&amp;gt;();
        contentEvents.PublishingContent -= contentEvents_PublishingContent;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see we calculate unpublished dependencies and cancel the operation if necessary.&lt;/p&gt;
&lt;p&gt;DependencyResolver traverses the dependencies, both direct and nested, so validation will still fail if for example a page depends on an ImageBlock which has an unpublished ImageData.&lt;/p&gt;
&lt;p&gt;Full gist is here: &lt;a href=&quot;https://gist.github.com/barteksekula/78ff1df20df2f2449497d94c218cfdad&quot;&gt;https://gist.github.com/barteksekula/78ff1df20df2f2449497d94c218cfdad&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can just copy paste the file to your project and it will work like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/268178f353684a2f8f33ef0efeb44cc9.aspx&quot; /&gt;&lt;/p&gt;</description>            <guid>https://world.optimizely.com/blogs/bartosz-sekula/dates/2024/2/validate-unpublished-page-dependencies/</guid>            <pubDate>Fri, 02 Feb 2024 16:37:19 GMT</pubDate>           <category>Blog post</category></item><item> <title>Opt-out from Inline Blocks support in ContentArea properties</title>            <link>https://world.optimizely.com/blogs/bartosz-sekula/dates/2023/7/opt-out-from-inline-blocks-support-in-contentarea-properties/</link>            <description>&lt;h1&gt;WARNING: This post is no longer up to date because from CMS UI 12.23.3 inline blocks are no longer enabled by default.&lt;br /&gt;&lt;br /&gt;&lt;/h1&gt;
&lt;h4&gt;They have to be turned on manually, more details here: &lt;a href=&quot;/link/2d33c5fb7a6a44498171901416604556.aspx&quot;&gt;https://world.optimizely.com/blogs/bartosz-sekula/dates/2023/5/inline-blocks-in-contentarea/&lt;/a&gt;&lt;br /&gt;Or in the documentation &lt;a href=&quot;https://docs.developers.optimizely.com/content-management-system/docs/inline-edit-settings&quot;&gt;https://docs.developers.optimizely.com/content-management-system/docs/inline-edit-settings&lt;/a&gt;&lt;/h4&gt;



&lt;h3&gt;OLD BLOG POST BELOW:&lt;/h3&gt;
&lt;p&gt;In CMS &lt;a href=&quot;https://nuget.optimizely.com/package/?id=EPiServer.CMS.UI&amp;amp;amp;amp;amp;amp;amp;v=12.21.0&quot;&gt;12.21.0&lt;/a&gt; we added a way to create inline blocks inside each ContentArea property.&lt;/p&gt;
&lt;p&gt;This feature is very powerful and has many advantaged as described in my previous blog post: &lt;a href=&quot;/link/2d33c5fb7a6a44498171901416604556.aspx&quot;&gt;https://world.optimizely.com/blogs/bartosz-sekula/dates/2023/5/inline-blocks-in-contentarea/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;However, this also introduced issues for some clients, mostly non-MVC clients who rely on our API endpoints and always expected that each ContentAreaItem to have a non-null ContentLink.&lt;/p&gt;
&lt;p&gt;For such cases in CMS UI &lt;a href=&quot;https://nuget.optimizely.com/package/?id=EPiServer.CMS.UI&amp;amp;amp;amp;amp;amp;amp;v=12.22.2&quot;&gt;12.22.2&lt;/a&gt; we added a way to opt-out from the ability to create inline blocks inside ContentArea.&lt;/p&gt;
&lt;p&gt;This new setting is global and the default value is &lt;em&gt;true&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;InlineBlocksInContentAreaEnabled&lt;/strong&gt; is a new boolean property added to UIOptions class. You can set it directly in code or you can also use the appsettings approach as shown below:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;&quot;EPiServer&quot;: {
    &quot;CmsUI&quot;: {
        &quot;UI&quot;: {
            &quot;InlineBlocksInContentAreaEnabled&quot;: false
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After turning that flag to false the link to create an inline block in each&amp;nbsp;&lt;em&gt;ContentArea&amp;nbsp;&lt;/em&gt;property will no longer be available.&lt;/p&gt;
&lt;p&gt;Of course it is still possible to use the Assets pane to create shared blocks within the folder structure or use the `For this page/block` folder as contextual storage.&lt;/p&gt;
&lt;p&gt;Important note: It is no longer possible to create shared blocks from Content Area.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://nuget.optimizely.com/package/?id=EPiServer.CMS.UI&amp;amp;amp;amp;amp;amp;v=12.22.2&quot;&gt;12.22.2&lt;/a&gt; also introduces a way to configure the labels for inline blocks.&lt;/p&gt;
&lt;p&gt;By default inline blocks labels are just their type names. It is fine if there are just a few but if the list of blocks is long it may be very problematic to find the block you need to edit (because in contrast to the On-Page-Edit here you don&#39;t see the view of the block). We added a way to instruct ContentArea to use a specific property from block type as a label.&lt;/p&gt;
&lt;p&gt;In order to do it you can use the new &lt;strong&gt;InlineBlockNamePropertiesOptions&amp;nbsp;&lt;/strong&gt;which can be set via appsettings.json in the following way:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;&quot;EPiServer&quot;: {
    &quot;CmsUI&quot;: {
        &quot;InlineBlockNameProperties&quot;: {
            &quot;Contact&quot;: &quot;Heading&quot;,
            &quot;Teaser&quot;: &quot;Heading&quot;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It is a simple Dictionary&amp;lt;string, string&amp;gt; of BlockTypeName / PropertyName&lt;/p&gt;
&lt;p&gt;Of course both options classes can be set in c#:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;services.Configure&amp;lt;UIOptions&amp;gt;(o =&amp;gt; {
    o.InlineBlocksInContentAreaEnabled = false;
});

services.Configure&amp;lt;InlineBlockNamePropertiesOptions&amp;gt;(options =&amp;gt;
{
    options.Add(&quot;Teaser&quot;, &quot;Heading&quot;);
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;More details available here: &lt;a href=&quot;https://docs.developers.optimizely.com/content-management-system/docs/inline-edit-settings&quot;&gt;https://docs.developers.optimizely.com/content-management-system/docs/inline-edit-settings&lt;/a&gt;&amp;nbsp;&lt;/p&gt;</description>            <guid>https://world.optimizely.com/blogs/bartosz-sekula/dates/2023/7/opt-out-from-inline-blocks-support-in-contentarea-properties/</guid>            <pubDate>Mon, 24 Jul 2023 14:58:25 GMT</pubDate>           <category>Blog post</category></item><item> <title>Inline blocks in ContentArea</title>            <link>https://world.optimizely.com/blogs/bartosz-sekula/dates/2023/5/inline-blocks-in-contentarea/</link>            <description>&lt;h1&gt;WARNING: This post has been updated after releasing version CMS UI 12.24.0&lt;/h1&gt;
&lt;h1&gt;Inline blocks are not enabled by default. Please scroll down to &quot;How to enable this feature&quot;&lt;br /&gt;&lt;br /&gt;&lt;/h1&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;EPiServer&#39;s most powerful property type &lt;em&gt;ContentArea&amp;nbsp;&lt;/em&gt;supports a number of use cases. Editors can store all kinds of content types there: pages, blocks, media, video, etc.&lt;/p&gt;
&lt;p&gt;ContentArea does not just store references to those content items but additionally it stores their names, types and personalization information.&lt;/p&gt;
&lt;p&gt;During the rendering phase property renderer loops through ContentAreaItems, applies personalization rules based on current identity and then tries to find a view for each item.&lt;/p&gt;
&lt;p&gt;It is indeed quite flexible because it lets the editors to create any kinds of layouts using just a single property on a page or combining multiple ContentArea properties.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/deaf411d09154e9895488fad829d8d13.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Editor can either select an existing IContent instance or decide to Create a new Block.&lt;/p&gt;
&lt;p&gt;Blocks created via ContentArea were placed in `For this page/block` folders and were accessible through the Assets pane:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/674421cdbf384e93a7992fc96cd527c4.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Because of the fact that each block is a separate IContent it is not possible to easily preview the changes, review by reviewers and finally to publish all items as once.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;There were several attempts to solving this issue, one of them being the Block Enhancements Labs package:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://nuget.optimizely.com/package/?id=EPiServer.Labs.BlockEnhancements&quot;&gt;https://nuget.optimizely.com/package/?id=EPiServer.Labs.BlockEnhancements&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/link/c256bf3bdc2743f0aa6179e8c9c76fb9.aspx&quot;&gt;https://world.optimizely.com/blogs/grzegorz-wiechec/dates/2019/7/episerver-labs---block-enhancements/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/link/5f6c3c6e2cee4262a5ff1765d82c348e.aspx&quot;&gt;https://world.optimizely.com/blogs/bartosz-sekula/dates/2021/9/block-enhancements-update/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It is a bit cumbersome that after creating a page with several blocks inside editor has to remember to always go through each page dependency to make sure it is in correct state. That each dependent block also has to be sent for review or added a project item even though it is only used once on that single page.&lt;/p&gt;
&lt;h2&gt;Inline blocks inside ContentArea&lt;/h2&gt;
&lt;p&gt;&lt;span&gt;In&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://nuget.optimizely.com/package/?id=EPiServer.CMS&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;v=12.22.3&quot;&gt;EPiServer.CMS 12.22.3&lt;/a&gt;&lt;span&gt;&amp;nbsp;we are releasing a big improvement to ContentArea. It is now possible to store BlockData inside ContentAreaItem as plain object.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;There are no more tricks required to synchronize a parent page with dependencies to publish them all at once. Editor just need to deal with a single piece of content (page, multi-channel content, ..) and is now allowed to inline blockdata and make them part of that parent content.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Nothing changes in regards to property definition in code, ContentArea is still defined the same way:&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public virtual ContentArea ContentItems { get; set; }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It is still possible to limit available content types by using &lt;em&gt;AllowedTypes &lt;/em&gt;attribute&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;[AllowedTypes(typeof(TeaserBlock), typeof(EditorialBlock), typeof(ContactBlock)]
public virtual ContentArea ContentItems { get; set; }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The UI also stays the same:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/a12ca8bfd9694cc4a650c00296cba754.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;However, after clicking &lt;em&gt;Create new block&lt;/em&gt; we are no longer going to create a new instance of block which would be visible in &lt;em&gt;For this page&lt;/em&gt; folder.&lt;/p&gt;
&lt;p&gt;Instead we would create a property bag and inline that property bag into &lt;em&gt;ContentAreaItem &lt;/em&gt;instance and store it inside the parent content.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/c884d900677347a09061534156b1212a.aspx&quot; width=&quot;1149&quot; height=&quot;837&quot; /&gt;&lt;/p&gt;
&lt;p&gt;After choosing the type and filling in the details:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/f11f3acb84cc44afb14a60ef8c4be152.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;We can immediately see the result:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/16db20e308944fe59887126c9d4a219f.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;After changing that &lt;em&gt;ContentAreaItem&amp;nbsp;&lt;/em&gt;we no longer need to publish the block but instead will get a new version of the parent page.&lt;/p&gt;
&lt;p&gt;That has several implications:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Editor no longer needs to switch context to edit those inline blocks.&lt;/li&gt;
&lt;li&gt;Inline blocks inside ContentArea do not have publishing lifecycle on their own, they are part of the page.&lt;/li&gt;
&lt;li&gt;Those items also do not have approval workflow on their own. It is the parent content that has to be reviewed and approved.&lt;/li&gt;
&lt;li&gt;Those items will not be included in projects, only their parent content as a whole&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&#39;s say we publish the page now but after a while editor decides to change the test on our &lt;em&gt;ButtonBlock&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In order to do that we can just run the &lt;em&gt;Edit &lt;/em&gt;command or double click the content area item.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/73384137c932491e93a272b8cdb4657b.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Change the properties:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/d934cd6d11234200bbd2b2d6ff940728.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;And after saving the dialog we can immediately see the changes on the page:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/8fb20d9f07554260ac10a9b226fa4c3a.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;As you can see a new page version was created. We can easily preview how the page will look like after the changes. We can also use the compare mode:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/e8f04eff64674f36b3f2d85ecb523b04.aspx&quot; width=&quot;1198&quot; height=&quot;552&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;All properties mode&lt;/h2&gt;
&lt;p&gt;Inline blocks can be named as any other blocks however for some simple block types it may be that there is already a textual property which can be used instead (for example if you have a&amp;nbsp;&lt;em&gt;Heading&amp;nbsp;&lt;/em&gt;property in your ContactBlock or &lt;em&gt;TeaserBlock&lt;/em&gt;.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;In order to do it you can use the new &lt;strong&gt;InlineBlockNamePropertiesOptions&amp;nbsp;&lt;/strong&gt;which can be set via appsettings.json in the following way:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;&quot;InlineBlockNameProperties&quot;: {
    &quot;Contact&quot;: &quot;Heading&quot;,
    &quot;Teaser&quot;: &quot;Heading&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It is a simple Dictionary&amp;lt;string, string&amp;gt; of BlockTypeName / PropertyName&lt;/p&gt;
&lt;p&gt;After that for ContactBlock and TeaserBlock instances&amp;nbsp;&lt;em&gt;Name&amp;nbsp;&lt;/em&gt;will no longer be displayed in&amp;nbsp;&lt;em&gt;Create new block&amp;nbsp;&lt;/em&gt;dialog and also the value of&amp;nbsp;&lt;em&gt;Heading&amp;nbsp;&lt;/em&gt;property will be used in Content Area editor.&lt;/p&gt;
&lt;h2&gt;View Mode Rendering&lt;/h2&gt;
&lt;p&gt;No special attributes or techniques are needed in order to render inline blocks. If you use our HtmlHelpers or TagHelpers they will render exactly the same as regular blocks.&lt;/p&gt;
&lt;p&gt;If you have a display template defined for a specific block type then the runtime will pick it up and use.&lt;/p&gt;
&lt;h2&gt;Migration&lt;/h2&gt;
&lt;p&gt;If you have existing local or shared blocks which you would like to inline into ContentArea properties then you have two options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We added a utility command which converts ContentAreaItem to an inline version. The original IContent is not removed, you will need to clean up manually&lt;img src=&quot;/link/65f1d6a9f8c64e0db4a939b3fe7a338b.aspx&quot; /&gt;&lt;/li&gt;
&lt;li&gt;There is an admin mode plugin available in &lt;a href=&quot;https://nuget.optimizely.com/package/?id=EPiServer.Labs.BlockEnhancements&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;v=1.2.2&quot;&gt;Block Enhancements Labs 1.2.2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/episerver/content-app-labs-block-enhancements/raw/develop/assets/docsimages/migration-tool.png&quot; alt=&quot;migration tool&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The migration can be done in 3 steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Convert shared blocks used only once to their respective `For this page` folder&lt;/li&gt;
&lt;li&gt;Make blocks from `For this page` inlined into ContentArea properties&lt;/li&gt;
&lt;li&gt;Remove empty folders from assets pane.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;All the steps can be either run at once or separately. It&#39;s up to you.&lt;/p&gt;
&lt;p&gt;It is also possible to run the migration the other way around. If you have inline blocks in the system and would like to turn them off and switch back to shared blocks then please use `Convert inline to shared` link or scheduled job. You can use `Dry run` to list all conten items containing inline blocks.&lt;/p&gt;
&lt;h2&gt;Changes to ContentArea `Create a new block` command&lt;/h2&gt;
&lt;p&gt;Prior to CMS 12.22.3 clicking on `Create a new block` link button:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/5cd5f2cb1d634492a0a1370457d20a6e.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Would create the new block inside `For this page` folder. After upgrading to CMS 12.22.3 it creates an inline blocks and embeds it inside ContentArea property.&lt;/p&gt;
&lt;h2&gt;Changes to shared content&lt;/h2&gt;
&lt;p&gt;Each shared item (essentially every content instance which has an ID and can be referenced by something else) will be clearly marked with `SHARED` label. Both in Forms view and in On-Page Edit.&lt;/p&gt;
&lt;p&gt;Additionally when editing a shared block through `Quick Edit` we now display a warning if that item being edited is used more than once:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/e079765907824f60b652b96e0d406258.aspx&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;ContentAreaItem property&lt;/h2&gt;
&lt;p&gt;Another enhancement is that we added the possibility to define a single ContentAreaItem property. It looks as a regular ContentReference property but offers much more functionality.&lt;/p&gt;
&lt;p&gt;Essentially it works as a ContentArea with a single item limitation applied.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public virtual ContentAreaItem MyContentItem { get; set; }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It is also possible to limit available content types by using &lt;em&gt;AllowedTypes &lt;/em&gt;attribute&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;[AllowedTypes(typeof(TeaserBlock), typeof(EditorialBlock), typeof(ContactBlock)]
public virtual ContentAreaItem MyContentItem { get; set; }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The UI resembles a regular ContentReference:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/073907cdb4374ed19505c51b850ad927.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;However apart from storing just a reference it also allowes you to create and store an inline block inside:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/41e6c72458f841e5a09fca12b3fc2256.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This property will also work fine in OPE and will support display options, the same way as in ContentArea (we reuse ContentAreaRenderer).&lt;/p&gt;
&lt;h2&gt;Q&amp;amp;A&lt;/h2&gt;
&lt;p&gt;Q: What happens after upgrading to CMS 12.22.3? Is there any automatic content migration?&lt;/p&gt;
&lt;p&gt;A: No. This is not a breaking release. Nothing will happen to your content. However, please be aware that inline blocks DO NOT HAVE content links on their own. They are just simple property bags.&lt;/p&gt;
&lt;p&gt;Q: How to start using this new feature?&lt;/p&gt;
&lt;p&gt;A: Please scroll down to `How to enable this feature`&lt;/p&gt;
&lt;p&gt;Q: Is this also supported through the API&lt;/p&gt;
&lt;p&gt;A: Yes. All the things you can do from Edit Mode are supported through our Management API&lt;/p&gt;
&lt;p&gt;Q: Does ContentArea still support old functionality?&lt;/p&gt;
&lt;p&gt;A: Yes, ContentArea still supports storing ContentReference, it still supports personalization, display options etc.&lt;/p&gt;
&lt;h2&gt;EPiServer.Labs.BlockEnhancements&lt;/h2&gt;
&lt;p&gt;That package is going away. All of its functionalities are now part of the main package. The last remaining feature is the migration tool described above.&lt;/p&gt;
&lt;p&gt;There will be no further releases.&lt;/p&gt;
&lt;h2&gt;&lt;a&gt;&lt;/a&gt;&lt;a&gt;&lt;/a&gt;&lt;a&gt;&lt;/a&gt;How to enable this feature&lt;/h2&gt;
&lt;p&gt;You can either use UIOptions.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;services.Configure&amp;lt;UIOptions&amp;gt;(uiOptions =&amp;gt;
{
    uiOptions.InlineBlocksInContentAreaEnabled = true;
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or via appsettings.json&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;&quot;EPiServer&quot;: {
    &quot;CmsUI&quot;: {
        &quot;UI&quot;: {
            &quot;InlineBlocksInContentAreaEnabled&quot;: true
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Documentation for this feature can be found here&amp;nbsp;&lt;a href=&quot;https://docs.developers.optimizely.com/content-management-system/docs/inline-edit-settings&quot;&gt;https://docs.developers.optimizely.com/content-management-system/docs/inline-edit-settings&lt;/a&gt;&lt;/p&gt;</description>            <guid>https://world.optimizely.com/blogs/bartosz-sekula/dates/2023/5/inline-blocks-in-contentarea/</guid>            <pubDate>Tue, 20 Jun 2023 12:05:59 GMT</pubDate>           <category>Blog post</category></item><item> <title>Official List property support</title>            <link>https://world.optimizely.com/blogs/bartosz-sekula/dates/2023/1/official-list-property-support/</link>            <description>&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Until now users were able to store list properties in three ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Store simple types (int, string, DateTime, double) as native properties like IList&amp;lt;string&amp;gt;, IList&amp;lt;int&amp;gt;, etc.. as described in &lt;a href=&quot;/link/b89585bc85f84513a24796d6f3457eee.aspx&quot;&gt;https://world.optimizely.com/blogs/bartosz-sekula/dates/2017/10/property-value-list/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Use a custom EPiServer.Core.PropertyList&amp;lt;T&amp;gt; which was introduced in CMS9 and described in a nice way in this blog post &lt;a href=&quot;/link/763efe6588f84cfb8c101358905752f9.aspx&quot;&gt;https://world.optimizely.com/blogs/Per-Magne-Skuseth/Dates/2015/11/trying-out-propertylistt/&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Use block reference list of any kind like a Content Area, or a list of ContentReference&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span&gt;All those approaches had its own issues and limitations.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;The first one&#39;s obvious limitation was that it was only able to operate on very basic types. On the other hand it provided a nice editing experience with an inline value editor and autosave.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;img src=&quot;https://files.readme.io/be67e83-stringlist.png&quot; /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;The next one was much more powerful because it allowed the user to use any kind of POCO and then render the list in a tabular way:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;img src=&quot;https://files.readme.io/07c80dd-generic-propertylist.png&quot; alt=&quot;755&quot; /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;and let the user edit each item from within a dialog:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;img src=&quot;https://files.readme.io/550c80a-editing-generic-propertylist.png&quot; alt=&quot;757&quot; /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;However this approach also had a few issues:&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span&gt;Property values are serialized to JSON and stored as plain string in the database&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Permanent links are not validated&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Some things like import/export, default values, custom metadata extenders need to be implemented manually&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span&gt;Finally the approach with using ContentArea or a list of ContentReference in theory solves all those issues because the editor has the ability to define the block list item anyhow and is able to use existing properties, however each list item is a separate IContent instance which has its own publishing lifecycle. In addition it also goes through the same approval process, can be included in a project as project item etc.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;img src=&quot;/link/f3253f3f7bf34a1f80d9985b9040cbaa.aspx&quot; width=&quot;959&quot; height=&quot;465&quot; /&gt;&lt;/span&gt;&lt;a href=&quot;/link/b89585bc85f84513a24796d6f3457eee.aspx&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Because of the fact that each block is a separate IContent it is not possible to easily preview the changes or publish all items as once.&lt;/p&gt;
&lt;p&gt;There were several attempts to solving this issue, one of them being the Block Enhancements Labs package:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://nuget.optimizely.com/package/?id=EPiServer.Labs.BlockEnhancements&quot;&gt;https://nuget.optimizely.com/package/?id=EPiServer.Labs.BlockEnhancements&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/link/c256bf3bdc2743f0aa6179e8c9c76fb9.aspx&quot;&gt;https://world.optimizely.com/blogs/grzegorz-wiechec/dates/2019/7/episerver-labs---block-enhancements/&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;The new list property&lt;/h2&gt;
&lt;p&gt;In &lt;a href=&quot;https://nuget.optimizely.com/package/?id=EPiServer.CMS&amp;amp;amp;amp;amp;amp;v=12.18.0&quot;&gt;EPiServer.CMS 12.18.0&lt;/a&gt; we are releasing a brand new list property which solves all the issues outlined above.&lt;/p&gt;
&lt;p&gt;Adding a list property is as simple as:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public virtual IList&amp;lt;ContactBlock&amp;gt; Contacts { get; set; }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Where ContactBlock can be defined like this:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;[ContentType(AvailableInEditMode = false, GUID = &quot;38d57768-e09e-4da9-90df-54c73c61b270&quot;)]  
public class ContactBlock : BlockData  
{  
   //block properties  
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What is important here is the fact that each ContactBlock instance will not be a content item of its own but will be stored inside its parent.&lt;/p&gt;
&lt;p&gt;That fact has several implications:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Editor no longer needs to switch context to edit those inline blocks&lt;/li&gt;
&lt;li&gt;Inline blocks do not have publishing lifecycle meaning that whenever any list item is changed the user the editor will see a new version of the current content being created&lt;/li&gt;
&lt;li&gt;List items will inherit approval sequence from their parent, it is the parent content that has to be reviewed and approved&lt;/li&gt;
&lt;li&gt;List items will not be included in projects, only their parent content as a whole&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The editor of such contact block list will look like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/6dadece007364ec5bf3ac7463dd2be92.aspx&quot; width=&quot;676&quot; alt=&quot;&quot; height=&quot;715&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Of course, having the ability to make the blocks inline is the primary use case of the new List&amp;lt;T&amp;gt; however it is capable of storing any kind of property type inside.&lt;/p&gt;
&lt;p&gt;Users can still utilize basic types like:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public virtual IList&amp;lt;int&amp;gt; Numbers { get; set; }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/link/40069ff76fa04f318b1b909604ff581d.aspx&quot; width=&quot;302&quot; alt=&quot;&quot; height=&quot;267&quot; /&gt;&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public virtual IList&amp;lt;string&amp;gt; Strings { get; set; }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/link/b6b7667868634345b944ff8ab0b43267.aspx&quot; width=&quot;658&quot; alt=&quot;&quot; height=&quot;262&quot; /&gt;&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public virtual IList&amp;lt;DateTime&amp;gt; Dates { get; set; }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/link/cfdd1b4f5a60491a960f3d95a083b09c.aspx&quot; width=&quot;301&quot; alt=&quot;&quot; height=&quot;270&quot; /&gt;&lt;/p&gt;
&lt;p&gt;But it is also possible to use more complex types:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public virtual IList&amp;lt;XHtmlString&amp;gt; XHtmlStrings { get; set; }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/link/4bdb46c836dd4ff18cb5efa0a933bc2c.aspx&quot; width=&quot;692&quot; alt=&quot;&quot; height=&quot;814&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Or for example create a list of images or videos:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;[ListItemUIHint(UIHint.Image)]
public virtual IEnumerable&amp;lt;ContentReference&amp;gt; Images { get; set; }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which will be rendered like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/dd91efa630fc42d4b078aaac5c2c377b.aspx&quot; width=&quot;408&quot; alt=&quot;&quot; height=&quot;363&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Please note that the property is annotated with a new attribute ListItemUIHintAttribute which works the same as regular UIHint but applies to the generic type of the list item.&lt;/p&gt;
&lt;p&gt;So we are telling the List property to use UIHint.Image for individual items.&lt;/p&gt;
&lt;p&gt;It is still possible to use UIHint(&quot;YourCustomListEditor&quot;) if you have your own editor.&lt;/p&gt;
&lt;h2&gt;Rendering&lt;/h2&gt;
&lt;p&gt;No special attributes or techniques are needed in order to render a list of any property types.&lt;/p&gt;
&lt;p&gt;The only thing needed is to provide a display template for a single item and the CMS will do the rest by wrapping each item in a list item.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public virtual IList&amp;lt;EditorialBlock&amp;gt; Blocks { get; set; }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will just need to provide EditorialBlock.cshtml which could look like this:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;@model EditorialBlock

&amp;lt;div class=&quot;clearfix&quot; @Html.EditAttributes(x =&amp;gt; x.MainBody)&amp;gt;
    @Html.DisplayFor(x =&amp;gt; Model.MainBody)
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And to render the `Blocks` property on the page you can use either &lt;a href=&quot;https://nuget.optimizely.com/package/?id=EPiServer.CMS.AspNetCore.HtmlHelpers&quot;&gt;HtmlHelpers &lt;/a&gt;package:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;@model MyPage

@Html.PropertyFor(x =&amp;gt; x.EditorialBlocks)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or the new &lt;a href=&quot;https://nuget.optimizely.com/package/?id=EPiServer.CMS.AspNetCore.TagHelpers&quot;&gt;TagHelpers&lt;/a&gt; package:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;@model MyPage

&amp;lt;div epi-property=&quot;EditorialBlocks&quot; /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Migration&lt;/h2&gt;
&lt;p&gt;The data model behind the scenes is much different from the previous implementations which always were based on some sort of JSON serialization.&lt;/p&gt;
&lt;p&gt;There is no automatic way to migrate from the old model to the new one and all migrations might different from case to case but let&#39;s imagine a hypothetical scenario like this:&lt;/p&gt;
&lt;p&gt;Let&#39;s say we have a page MyPage.cs with a single property inside:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public class MyPage : PageData 
{
   [EditorDescriptor(EditorDescriptorType = typeof(CollectionEditorDescriptor&amp;lt;Person&amp;gt;))]
   public virtual IList&amp;lt;Person&amp;gt; PersonList { get; set; }
}

public class Person
{    
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
    public string Email { get; set; }
}

[PropertyDefinitionTypePlugIn]
public class PersonListProperty : PropertyList&amp;lt;Person&amp;gt;
{
    public PersonListProperty()
    {
        _objectSerializer = _objectSerializerFactory.Service.GetSerializer(KnownContentTypes.Json);
    }

    private Injected&amp;lt;IObjectSerializerFactory&amp;gt; _objectSerializerFactory;

    private IObjectSerializer _objectSerializer;

    protected override Person ParseItem(string value)
    {
        return _objectSerializer.Deserialize&amp;lt;Person&amp;gt;(value);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The property would look like this in Edit Mode:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/a4433fac1311474db8e1ce71aabd238d.aspx&quot; width=&quot;1027&quot; alt=&quot;&quot; height=&quot;175&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In order to migrate to the new property and use the new inline editor we would have to:&lt;/p&gt;
&lt;p&gt;Define a new PersonBlock type and mark it as AvailableInEdit = false&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;[ContentType(AvailableInEditMode = false, GUID = &quot;11157768-e09e-4da9-90df-54c73c61b270&quot;)] 
public class PersonBlock : BlockData
{    
    public virtual string FirstName { get; set; }
    public virtual string LastName { get; set; }
    public virtual int Age { get; set; }
    public virtual string Email { get; set; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In order to use it on MyPage:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public class MyPage : PageData 
{
   public virtual IList&amp;lt;PersonBlock&amp;gt; PersonsNew { get; set; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the new editing experience will look like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/903ab63691f242c884f6e2430c198be3.aspx&quot; width=&quot;701&quot; alt=&quot;&quot; height=&quot;551&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Please note that it is no longer necessary to define your own property type.&lt;/p&gt;
&lt;p&gt;In order to migrate from IList&amp;lt;Person&amp;gt; to IList&amp;lt;PersonBlock&amp;gt; a custom piece of code which iterates over all instances of MyPage analyzes what is inside PersonList property and then uses the same values to create an a list of PersonBlock instances.&lt;/p&gt;
&lt;h2&gt;Next improvements coming up&lt;/h2&gt;
&lt;p&gt;We are planning to take it even further and allow to inline blocks into Content Area as well so stay tuned for next updates on that matter.&lt;/p&gt;
&lt;p&gt;Eventually we will turn off `Quick edit` command and promote the new Inline Blocks which no longer need any kind of tricks to keep their publishing lifecycle synchonized with their parent.&lt;/p&gt;
&lt;p&gt;We will also not need any commands like `Publish page with blocks` &lt;a href=&quot;https://github.com/episerver/EPiServer.Labs.BlockEnhancements#publish-page-and-shared-blocks&quot;&gt;https://github.com/episerver/EPiServer.Labs.BlockEnhancements#publish-page-and-shared-blocks&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Obsoleting EPiServer.Labs.BlockEnhancements&lt;/h2&gt;
&lt;p&gt;The whole idea of `inlining` the blocks into page came up as part of our Labs initiative which turned out to be highly successful &lt;a href=&quot;https://github.com/episerver/EPiServer.Labs.BlockEnhancements#local-content&quot;&gt;https://github.com/episerver/EPiServer.Labs.BlockEnhancements#local-content&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Now, since most of Labs functionality ends up in the official package it is time to deprecate it.&lt;/p&gt;</description>            <guid>https://world.optimizely.com/blogs/bartosz-sekula/dates/2023/1/official-list-property-support/</guid>            <pubDate>Tue, 28 Mar 2023 12:16:26 GMT</pubDate>           <category>Blog post</category></item><item> <title>Built-in support for single LinkItem property</title>            <link>https://world.optimizely.com/blogs/bartosz-sekula/dates/2022/6/built-in-support-for-single-linkitem-property/</link>            <description>&lt;p&gt;&lt;a href=&quot;https://nuget.optimizely.com/package/?id=EPiServer.CMS&amp;amp;amp;amp;amp;amp;amp;v=12.11.0&quot;&gt;EPiServer.CMS 12.11.0&lt;/a&gt; introduces a new property type &lt;em&gt;LinkItem . &lt;/em&gt;This feature has been one of the most requested features for a long time.&lt;/p&gt;
&lt;p&gt;The feature is very simple: it is to allow the editor to choose a single LinkItem.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/52cea5acc6104224b61b0a83de96471a.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;After clicking the `Add Link` button editor is presented with the standard Linkitem dialog:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/ccd79a0e925a45a6b35e53057fb6781d.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;After selecting any content item the editor will look like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/aed1e0566e2f416aa262baa5ebdbfa67.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In order to use the new property type you just need to use the LinkItem data type. No annotations are needed.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public class PartnerPage : PageData
{
    public virtual LinkItem MyLink { get; set; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Migration from EPiServer.Labs.LinkItemProperty&lt;/h3&gt;
&lt;p&gt;If you used the Labs package before and you wish to update then you will need to follow a few manual upgrade steps:&lt;/p&gt;
&lt;p&gt;Labs used the following:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public class PartnerPage : PageData
{
    [LinkItemProperty]
    [BackingType(typeof(PropertyLinkCollection))]
    public virtual LinkItem Link
    {
        get =&amp;gt; this.GetLinkItemPropertyValue(nameof(Link));
        set =&amp;gt; this.SetLinkItemPropertyValue(nameof(Link), value);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You have to remove both &lt;em&gt;LinkItemProperty &lt;/em&gt;and &lt;em&gt;BackingType&amp;nbsp;&lt;/em&gt;annotations and switch to auto property.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public class PartnerPage : PageData
{
    public virtual LinkItem Link { get; set; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will also need to run the following SQL command (&lt;span&gt;don&#39;t forget to do the backup for safety)&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;language-c&quot;&gt;&lt;code&gt;DECLARE @PropertiesToUpdate NVARCHAR(4000); -- one or more coma separated properties to update with [ContentTypeName.PropertyName] format
SET @PropertiesToUpdate = &#39;&#39;; -- for example ArticlePage2.SingleLink,ArticlePage2.SingleLinkCultureSpecific

-- update published versions
UPDATE tblContentProperty
 SET tblContentProperty.LongString = SUBSTRING(  -- start after `&amp;lt;links&amp;gt;`
           SUBSTRING(propValue.LongString, 0, CHARINDEX(&#39;&amp;lt;/a&amp;gt;&#39;,  propValue.LongString) + 4), -- remove all text after first `&amp;lt;/a&amp;gt;`
           8, 100000)
 OUTPUT INSERTED.*, deleted.*
  FROM tblPropertyDefinition propDef
  JOIN tblPropertyDefinitionType propDefType ON propDefType.[Name] = &#39;LinkCollection&#39;
   AND propDef.fkPropertyDefinitionTypeID = propDefType.pkID
  JOIN tblContentType ON tblContentType.pkID = propDef.fkContentTypeID
  JOIN tblContentProperty propValue ON propValue.fkPropertyDefinitionID = propDef.pkID
 WHERE propValue.LongString LIKE &#39;&amp;lt;links&amp;gt;%&#39;
   AND CHARINDEX(tblContentType.[Name] + &#39;.&#39; + propDef.[Name] + &#39;,&#39;, @PropertiesToUpdate + &#39;,&#39;) &amp;gt; 0;

-- update versions  
UPDATE tblWorkContentProperty
   SET tblWorkContentProperty.LongString = SUBSTRING(  -- start after `&amp;lt;links&amp;gt;`
          SUBSTRING(propValue.LongString, 0, CHARINDEX(&#39;&amp;lt;/a&amp;gt;&#39;,  propValue.LongString) + 4), -- remove all text after first `&amp;lt;/a&amp;gt;`
          8, 100000)
OUTPUT INSERTED.*, deleted.*
  FROM tblPropertyDefinition propDef
  JOIN tblPropertyDefinitionType propDefType ON propDefType.[Name] = &#39;LinkCollection&#39;
   AND propDef.fkPropertyDefinitionTypeID = propDefType.pkID
  JOIN tblContentType ON tblContentType.pkID = propDef.fkContentTypeID
  JOIN tblWorkContentProperty propValue ON propValue.fkPropertyDefinitionID = propDef.pkID
 WHERE propValue.LongString LIKE &#39;&amp;lt;links&amp;gt;%&#39;
   AND CHARINDEX(tblContentType.[Name] + &#39;.&#39; + propDef.[Name] + &#39;,&#39;, @PropertiesToUpdate + &#39;,&#39;) &amp;gt; 0;

-- update property type
UPDATE tblPropertyDefinition
   SET tblPropertyDefinition.fkPropertyDefinitionTypeID = (SELECT pkID from tblPropertyDefinitionType where [Name] = &#39;LinkItem&#39;)
OUTPUT INSERTED.*, deleted.*
  FROM tblContentType
 WHERE tblPropertyDefinition.fkPropertyDefinitionTypeID = (SELECT pkID from tblPropertyDefinitionType where [Name] = &#39;LinkCollection&#39;) 
   AND CHARINDEX(tblContentType.[Name] + &#39;.&#39; + tblPropertyDefinition.[Name] + &#39;,&#39;, @PropertiesToUpdate + &#39;,&#39;) &amp;gt; 0;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Afterwards the Labs package can just be uninstalled.&lt;/p&gt;</description>            <guid>https://world.optimizely.com/blogs/bartosz-sekula/dates/2022/6/built-in-support-for-single-linkitem-property/</guid>            <pubDate>Wed, 14 Sep 2022 12:35:49 GMT</pubDate>           <category>Blog post</category></item><item> <title>Block Enhancements Update</title>            <link>https://world.optimizely.com/blogs/bartosz-sekula/dates/2021/9/block-enhancements-update/</link>            <description>&lt;p&gt;We have just released a new version of &lt;a href=&quot;https://nuget.optimizely.com/package/?id=EPiServer.Labs.BlockEnhancements&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;v=0.11.0&quot;&gt;EPiServer.Labs.BlockEnhancements 0.11.0&lt;/a&gt;&amp;nbsp; and we would really love to hear your feedback. Please find what it brings to the Editors!&amp;nbsp;&lt;/p&gt;
&lt;p&gt;It greatly enhances the way editors have to deal with local content - which means content that is not shared accross multiple other pages but used only once.&lt;/p&gt;
&lt;p&gt;Such local content is typically stored in `For this page/block` folders and is considered an integral part of the page. It turns out that on average more than 90% of blocks are only used on a single page which means that they either already reside in `For this page` folder or could easily be moved there.&lt;/p&gt;
&lt;p&gt;After installing/upgrading to &lt;a href=&quot;https://nuget.optimizely.com/package/?id=EPiServer.Labs.BlockEnhancements&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;v=0.11.0&quot;&gt;0.11.0&lt;/a&gt; e&lt;span&gt;ditors no longer have to manage Local Blocks and Local Assets independently.&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;All local contents&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(&lt;span&gt;meaning content that resides in&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;For this page/block&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;folders&lt;/span&gt;)&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;lifecycle will now be&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;synchronized to its parent.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;This greatly simplifies the process of creating and editing pages because editor/reviewer&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;no longer need to deal with individual blocks or assets but can focus on the big picture.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;When publishing a page all local content will be published altogether.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;The same with approval workflow,&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;when content is marked as&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;Ready to review&lt;/code&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;Approved&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;or&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;Declined&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;then the editor/reviewer will just receive a single notification about the page.&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;There will be no need to review the block individually.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Changing a local block will create a new page version which lets the editors to review and preview&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;what actually changed on a given page.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Please see the new feature in action&lt;/span&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/episerver/EPiServer.Labs.BlockEnhancements/raw/master/assets/docsimages/local-editing-local-block.gif&quot; width=&quot;1298&quot; height=&quot;1062&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;This allows the editor to use the Compare Mode and see what actually changed&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;img src=&quot;https://github.com/episerver/EPiServer.Labs.BlockEnhancements/raw/master/assets/docsimages/local-compare-mode.gif&quot; width=&quot;1298&quot; height=&quot;1062&quot; /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Editor can also mark the page as&amp;nbsp;&lt;code&gt;Ready to review&lt;/code&gt;&amp;nbsp;without dealing with local blocks one by one&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;img src=&quot;https://github.com/episerver/EPiServer.Labs.BlockEnhancements/raw/master/assets/docsimages/local-ready-to-review.gif&quot; width=&quot;1298&quot; height=&quot;1062&quot; /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Approving/declining is just as easy:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;img src=&quot;https://github.com/episerver/EPiServer.Labs.BlockEnhancements/raw/master/assets/docsimages/local-approve-content.gif&quot; width=&quot;1298&quot; height=&quot;1062&quot; /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;We also added an admin mode plugin that lets you analyze the blocks on your site:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/827621a0fc9c4558b62b04f491614000.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;All the blocks that are not truly Shared (meaning they are only used once) will be moved the the respective `For this page/block` folders. You can move the blocks one by one or move all the visible blocks at once.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;For performance reasons we only display the N first blocks that match the search filter.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;In this version we removed two features:&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span&gt;Content Draft View - it is no longer needed because version 0.11.0 makes the preview work automatically for all local content. The editor no longer needs to know that some parts of the page are not published, everything that builds a page is one entity that is saved, previewed and published at the same time. However, all shared content items (pages, shared blocks, shared media) have to be managed on their own.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Inline Publish from Content Area - it was not popular and is no longer needed since now you can just publish the page and all local dependencies do not have to be handled separately and will be published automatically. Shared content has to be published on its own.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Additionally we disabled the `Publish Page with Blocks` aka `Smart publish` by default - the command can still be turned on via Options if you really need to because you already have many shared blocks that cannot be convert to local blocks. We would like this command to be phased out eventually. Blocks which are truly shared between multiple pages should be published from their respective context where the editor can clearly see the list of items that will be affected by the change.&lt;/p&gt;
&lt;p&gt;Idea of linking pages with their local content seems more attractive.&lt;/p&gt;
&lt;p&gt;Documentation and full source code is available on our github &lt;a href=&quot;https://github.com/episerver/EPiServer.Labs.BlockEnhancements&quot;&gt;https://github.com/episerver/EPiServer.Labs.BlockEnhancements&lt;/a&gt; and this specific feature is available here &lt;a href=&quot;https://github.com/episerver/EPiServer.Labs.BlockEnhancements#local-content&quot;&gt;https://github.com/episerver/EPiServer.Labs.BlockEnhancements#local-content&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;If you find any issue or would like to contribute then you are more than welcome to let us know :)&lt;/p&gt;
&lt;p&gt;&lt;span&gt;We are also in the middle of migrating this Labs package to .net core. It will be available soon!&lt;/span&gt;&lt;/p&gt;</description>            <guid>https://world.optimizely.com/blogs/bartosz-sekula/dates/2021/9/block-enhancements-update/</guid>            <pubDate>Wed, 27 Oct 2021 08:38:07 GMT</pubDate>           <category>Blog post</category></item><item> <title>How to resolve current content context</title>            <link>https://world.optimizely.com/blogs/bartosz-sekula/dates/2021/9/how-to-resolve-current-content-context/</link>            <description>&lt;p&gt;With the release of&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://nuget.episerver.com/package/?id=EPiServer.CMS.UI&amp;amp;amp;amp;amp;amp;amp;v=11.36.2&quot;&gt;CMS UI 11.36.2&lt;/a&gt; it is now possible to resolve the current loaded content context from anywhere you may need.&lt;/p&gt;
&lt;p&gt;It might be useful if you need any custom logic in any of the Content Events:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;[ModuleDependency(typeof(EPiServer.Shell.UI.InitializationModule))]
public class CustomModule : IConfigurableModule
{
    public void Initialize(InitializationEngine context)
    {
        context.Locate.ContentEvents().CreatedContent += CreatedContent;
        context.Locate.ContentEvents().CreatingContent += CreatingContent;
    }

    private void CreatingContent(object sender, ContentEventArgs e)
    {   
        var currentContentContext = ServiceLocator.Current.GetInstance&amp;lt;CurrentContentContext&amp;gt;();
        // we know which content context initiated the CREATE operation        
        var currentContentLink = currentContentContext.ContentLink;

        var newlyCreatedContentLink = e.ContentLink; // --&amp;gt; null as at this point the ContentReference was not assigned yet
    }

    private void CreatedContent(object sender, ContentEventArgs e)
    {
        var newlyCreatedContentLink = e.ContentLink; // this is the ContentReference of the newly created content item
        // but we might need the ContentReference of the content that was being viewed when the CREATE operation was run
        var currentContentContext = ServiceLocator.Current.GetInstance&amp;lt;CurrentContentContext&amp;gt;();
        // this gives us the content context the editor was in
        var currentContentLink = currentContentContext.ContentLink;
    }

    public void Uninitialize(InitializationEngine context)
    {
        context.Locate.ContentEvents().CreatedContent -= CreatedContent;
        context.Locate.ContentEvents().CreatingContent -= CreatingContent;
    }

    public void ConfigureContainer(ServiceConfigurationContext context)
    {

    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You might also need to know the current context in metadata aware components, like for example in this Editor Descriptor:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public class FooPageType : PageData 
{
    [EditorDescriptor(EditorDescriptorType = typeof(CollectionEditorDescriptor&amp;lt;OfferDetailsItem&amp;gt;))]
    public virtual IList&amp;lt;OfferDetailsItem&amp;gt; OfferDetailsItems { get; set; }
}

public class OfferDetailsItem
{
    [Display(Order = 1)]
    [UIHint(&quot;test&quot;)]
    public string Label { get; set; }
}

[PropertyDefinitionTypePlugIn]
public class OfferDetailsItemPropertyList : PropertyList&amp;lt;OfferDetailsItem&amp;gt;
{
}

[EditorDescriptorRegistration(TargetType = typeof(string), UIHint = &quot;test&quot;)]
public class CustomXhtmlDescriptor : StringEditorDescriptor
{
    private readonly CurrentContentContext _currentContent;

    public CustomXhtmlDescriptor(CurrentContentContext currentContent)
    {
        _currentContent = currentContent;
    }

    public override void ModifyMetadata(ExtendedMetadata metadata, IEnumerable&amp;lt;Attribute&amp;gt; attributes)
    {
        base.ModifyMetadata(metadata, attributes);

        var ownerContent = metadata.FindOwnerContent(); // --&amp;gt; will return null because the object we annotate is not ContentData but simple POCO
        // https://world.optimizely.com/forum/developer-forum/CMS/Thread-Container/2016/1/current-content-for-propertylist-item-selection-factory/
        // and
        // https://world.optimizely.com/forum/developer-forum/CMS/Thread-Container/2021/9/tinymce-customization-depending-on-page-context/
        var currentContent = _currentContent.ContentLink; // --&amp;gt; will return correct page id
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Related forum posts that describe the problem:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/link/ec6e9a05c9b845e2bee164655efd48a2.aspx&quot;&gt;https://world.optimizely.com/forum/developer-forum/CMS/Thread-Container/2021/9/tinymce-customization-depending-on-page-context/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/link/24ad8e2785f64a3db39620a44a522726.aspx#234848&quot;&gt;https://world.optimizely.com/forum/developer-forum/CMS/Thread-Container/2020/12/get-value-of-contextepi-cms-contentdata/#234848&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/link/b25cdca6360e470298e49dab4964a13e.aspx&quot;&gt;https://world.optimizely.com/forum/developer-forum/CMS/Thread-Container/2016/1/current-content-for-propertylist-item-selection-factory/&lt;/a&gt;&lt;/p&gt;</description>            <guid>https://world.optimizely.com/blogs/bartosz-sekula/dates/2021/9/how-to-resolve-current-content-context/</guid>            <pubDate>Tue, 28 Sep 2021 08:38:11 GMT</pubDate>           <category>Blog post</category></item><item> <title>Refresh current editing context on property value change</title>            <link>https://world.optimizely.com/blogs/bartosz-sekula/dates/2021/7/refresh-current-editing-context-on-property-value-change/</link>            <description>&lt;p&gt;With the release of &lt;a href=&quot;https://nuget.episerver.com/package/?id=EPiServer.CMS.UI&amp;amp;amp;amp;amp;v=11.36.0&quot;&gt;CMS UI 11.36.0&amp;nbsp;&lt;/a&gt;it is now possible to annotate any model property with a new attribute from&amp;nbsp;&lt;em&gt;EPiServer.Cms.Shell.UI.ObjectEditing&lt;/em&gt; namespace called&amp;nbsp;&lt;em&gt;ReloadOnChangeAttribute&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Imagine a simplified scenario - we have a list of available options&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;[Display(GroupName = Global.GroupNames.MetaData, Order = 250)]
[SelectOne(SelectionFactoryType = typeof(ItemsSelectionFactory))]
public virtual string Items { get; set; }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;but the list should be filtered based on some extra property value also defined in our model:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;[Display(
    GroupName = &quot;EPiServerCMS_SettingsPanel&quot;,
    Order = 2)]
public virtual bool Important { get; set; }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we would like a different set of items to be displayed when Important flag is on and different when Important flag is off.&lt;/p&gt;
&lt;p&gt;All we have to do is to annotate our conditional property with the new attribute:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;[Display(
    GroupName = &quot;EPiServerCMS_SettingsPanel&quot;,
    Order = 2)]
[ReloadOnChange]
public virtual bool Important { get; set; }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And now we can utilize the value of &lt;em&gt;Important&amp;nbsp;&lt;/em&gt;property inside our factory.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public class ItemsSelectionFactory : ISelectionFactory
{
    public IEnumerable&amp;lt;ISelectItem&amp;gt; GetSelections(ExtendedMetadata metadata)
    {
        var content = metadata.FindOwnerContent();
        var property = content?.Property[nameof(SitePageData.Important)];
        if (property?.Value != null &amp;amp;&amp;amp; (bool) property.Value)
        {
            return new[]
            {
                new SelectItem
                {
                    Text = &quot;VERY IMPORTANT ITEM 1&quot;,
                    Value = &quot;100&quot;
                }
            };
        }

        return new[]
        {
            new SelectItem
            {
                Text = &quot;NOT IMPORTANT ITEM 1&quot;,
                Value = &quot;1&quot;
            },
            new SelectItem
            {
                Text = &quot;NOT IMPORTANT ITEM 2&quot;,
                Value = &quot;2&quot;
            },
            new SelectItem
            {
                Text = &quot;NOT IMPORTANT ITEM 3&quot;,
                Value = &quot;3&quot;
            }
        };
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The same applies to OPE. Let&#39;s say you only need a specific box to be shown when the Important flag is on. All you need to do is to just read the value in the template and on every value change the template will be rerendered:&lt;/p&gt;
&lt;pre class=&quot;language-markup&quot;&gt;&lt;code&gt;@if (Model.CurrentPage.Important)
{
    &amp;lt;div style=&quot;height: 100px; width: 100px; background-color: red;&quot;&amp;gt;&amp;lt;/div&amp;gt;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is how it looks in action:&lt;img src=&quot;https://github.com/episerver/episerver-labs-content-manager-docs/raw/master/images/content-manager/refresh_on_value_change.gif&quot; width=&quot;1561&quot; alt=&quot;&quot; height=&quot;1067&quot; /&gt;&lt;/p&gt;</description>            <guid>https://world.optimizely.com/blogs/bartosz-sekula/dates/2021/7/refresh-current-editing-context-on-property-value-change/</guid>            <pubDate>Tue, 07 Sep 2021 12:16:25 GMT</pubDate>           <category>Blog post</category></item><item> <title>Forms compatibility package with Labs.BlockEnhancements</title>            <link>https://world.optimizely.com/blogs/bartosz-sekula/dates/2020/4/forms-compatibility-package-with-labs-blockenhancements/</link>            <description>&lt;p&gt;&lt;a href=&quot;https://github.com/episerver/EPiServer.Labs.BlockEnhancements&quot;&gt;Block Enhancements Labs&lt;/a&gt; package does not fully support &lt;a href=&quot;/link/1c63c14e3ef148fb8080b50b09dfeabd.aspx&quot;&gt;EPiServer Forms&lt;/a&gt;&amp;nbsp;so if you use both packages in one of your sites then you may need a tiny compatibility package that I have just released a new version &lt;a href=&quot;https://nuget.episerver.com/package/?id=Advanced.CMS.Forms&amp;amp;amp;amp;v=0.2.0&quot;&gt;v0.2.0&lt;/a&gt; of.&lt;/p&gt;
&lt;p&gt;The source code is Open Source, more details can be found here:&amp;nbsp;&lt;a href=&quot;https://github.com/advanced-cms/forms&quot;&gt;https://github.com/advanced-cms/forms&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The nuget can be installed from&amp;nbsp;&lt;a href=&quot;https://nuget.episerver.com/package/?id=Advanced.CMS.Forms&amp;amp;amp;amp;amp;v=0.2.0&quot;&gt;https://nuget.episerver.com/package/?id=Advanced.CMS.Forms&amp;amp;v=0.2.0&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The package lets you configure the Forms without changing the Editing Context which is a bit more convenient when working with compex and long forms:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/advanced-cms/forms/blob/master/assets/documentation/inline-form-add.gif&quot; alt=&quot;&quot; /&gt;&lt;img src=&quot;https://github.com/advanced-cms/forms/blob/master/assets/documentation/inline-form-add.gif&quot; alt=&quot;&quot; /&gt;&lt;img src=&quot;https://github.com/advanced-cms/forms/raw/master/assets/documentation/inline-form-add.gif&quot; alt=&quot;Add&amp;#32;Form&amp;#32;Elements&quot; /&gt;&lt;/p&gt;
&lt;p&gt;It also works in OPE:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/advanced-cms/forms/raw/master/assets/documentation/inline_edit_form.gif&quot; alt=&quot;Edit&amp;#32;Form&amp;#32;Properties&quot; /&gt;&lt;/p&gt;
&lt;p&gt;It is not an official package from EPiServer but is actively maintained by the &quot;advanced-cms&quot; team (Greg &amp;amp; Bart).&lt;/p&gt;</description>            <guid>https://world.optimizely.com/blogs/bartosz-sekula/dates/2020/4/forms-compatibility-package-with-labs-blockenhancements/</guid>            <pubDate>Thu, 23 Apr 2020 17:04:47 GMT</pubDate>           <category>Blog post</category></item><item> <title>Content Manager - lightweight editing UI</title>            <link>https://world.optimizely.com/blogs/bartosz-sekula/dates/2020/4/content-manager---lightweight-editing-ui/</link>            <description>&lt;p&gt;It&#39;s a pleasure to announce the release of two new Episerver Labs packages (more info about&amp;nbsp;Labs in general can be found &lt;a href=&quot;/link/487e09260da244f69c5a0ad6fbdaa75d.aspx?_t_id=yoa4m-OJvUXk555VM1bEgA%3d%3d&amp;amp;amp;amp;amp;amp;amp;amp;amp;_t_uuid=bZzypfMRQ_2rxi0B4ECXkw&amp;amp;amp;amp;amp;amp;amp;amp;amp;_t_q=introducing+labs&amp;amp;amp;amp;amp;amp;amp;amp;amp;_t_tags=language:en,siteid:4ba62c03-2627-4a1e-8939-9db6098db0eb,andquerymatch&amp;amp;amp;amp;amp;amp;amp;amp;amp;_t_hit.id=EPiServerWorld_ContentTypes_WebFormTypes_BlogitemPageType/_487e0926-0da2-44f6-9c5a-0ad6fbdaa75d_en&amp;amp;amp;amp;amp;amp;amp;amp;amp;_t_hit.pos=1&quot;&gt;here&lt;/a&gt;).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span&gt;Content Manager - which is&amp;nbsp;&lt;/span&gt;&lt;span&gt;a new feature that allows you to manage content items outside of Edit Mode.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Grid View - which is an add-on that helps you to manage large volumes of data inside Edit Mode.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span&gt;In the following weeks, we will be discussing both packages in details in separate blog posts not to overwhelm you with too much information.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;NuGet packages are available here:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;a href=&quot;https://nuget.episerver.com/package/?id=EPiServer.Labs.ContentManager&quot;&gt;https://nuget.episerver.com/package/?id=EPiServer.Labs.ContentManager&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;a href=&quot;https://nuget.episerver.com/package/?id=EPiServer.Labs.GridView&quot;&gt;https://nuget.episerver.com/package/?id=EPiServer.Labs.GridView&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;IMPORTANT!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;As of now Content Manager package is still dependent on the GridView package, so if you plan to give Content Manager a try then you must install both packages.&lt;/p&gt;
&lt;p&gt;Documentation is available here:&lt;/p&gt;
&lt;p&gt;https://github.com/episerver/episerver-labs-content-manager-docs&lt;/p&gt;
&lt;p&gt;Please report bugs/issues/questions here: https://github.com/episerver/episerver-labs-content-manager-docs/issues&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Let&#39;s just take a quick look on what is possible with the new packages!&lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;There are two&amp;nbsp;ways we can organize content:&lt;span&gt;Introduction&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;The first one is Structured. You are all familiar with it as it&#39;s widely used in CMS to display the sites as Tree Views or to display&amp;nbsp;Asset folder structures.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/d35060f63af24229bae218321c4188b1.aspx&quot; width=&quot;414&quot; height=&quot;508&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This model has been there for a long time and is very well supported by current Episerver product family.&lt;/p&gt;
&lt;p&gt;However,&amp;nbsp;more and more frequently there are&amp;nbsp;requirements to have parts of the system that does not fit into such&amp;nbsp;tree model.&lt;/p&gt;
&lt;p&gt;We saw a lot of solutions where only a small part of the system was hierarchical while the major part of content items was placed in a single container.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/f61f066b224e4936aad7861052ed1cc5.aspx&quot; width=&quot;414&quot; height=&quot;508&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The web is changing, and content no longer means what it used to mean 10 years ago.&lt;/p&gt;
&lt;p&gt;Now, a content item is not necessarily only a web page, it may not have an addressable URL and it may be just a container for data that can be seen as a database record.&lt;/p&gt;
&lt;p&gt;Sometimes, the same piece of content can be used on a page, on social media or in print which means the content is no longer assigned to a specific channel. Usually, those kind of content items are simple&amp;nbsp;with only basic property types and some metadata.&lt;/p&gt;
&lt;p&gt;When trying to use large volumes of structureless content in the current Edit Mode, we easily might get into trouble. The tree is not optimized to display long lists of children items under a single node. It is easy to solve this with some kind of clustering but everything comes with a price. Clustered content is much harder to find and to work with.&lt;/p&gt;
&lt;p&gt;Another option is to use the &lt;a href=&quot;https://www.episerver.com/products/features/search-navigation/&quot;&gt;Episerver Search &amp;amp; Navigation&lt;/a&gt; package (formerly Episerver Find) together with the PowerSlice add-on which allows you to get page lists by page type but its functionality is limited and not customizable.&lt;/p&gt;
&lt;p&gt;Hopefully, with the release &lt;a href=&quot;https://nuget.episerver.com/?q=episerver.labs.gridview&quot;&gt;Grid View&lt;/a&gt;, it will be much easier for the editors.&lt;/p&gt;
&lt;h2&gt;Grid View&lt;/h2&gt;
&lt;p&gt;Grid View, started some time ago by Greg (&amp;nbsp;&lt;a href=&quot;https://gregwiechec.com/2017/11/content-childrengrid-view/&quot;&gt;https://gregwiechec.com/2017/11/content-childrengrid-view/&lt;/a&gt;) gives you a way to mark such a node as a &#39;Container&#39; and browse its descendants in a dedicated custom view which gives a much better navigation and filtering.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/55fd03671c3d4826bca37f22a9893938.aspx&quot; width=&quot;1043&quot; alt=&quot;Locked&amp;#32;nodes&quot; height=&quot;514&quot; /&gt;&lt;/p&gt;
&lt;p&gt;It is possible to convert a page/folder to a &#39;container&#39; either by code or by the admin interface (it will all be explained in detail in future posts).&lt;/p&gt;
&lt;p&gt;One more Grid View feature worth mentioning is the ability to turn off the Page Tree and use the Grid as an alternative:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/fc700d9a28b140e7bcb213b6384d1cf8.aspx&quot; width=&quot;961&quot; alt=&quot;navigation&amp;#32;component&amp;#32;drag&amp;#32;and&amp;#32;drop&quot; height=&quot;513&quot; /&gt;&lt;/p&gt;
&lt;p&gt;It offers the same functionality, such as context menus and D&amp;amp;D, so it can be used interchangeably with the Tree.&lt;/p&gt;
&lt;p&gt;Grid View gives much more functionality and is highly customizable (it will all be covered in future posts).&lt;/p&gt;
&lt;h2&gt;Content Manager&lt;/h2&gt;
&lt;p&gt;Edit Mode is very powerful. It contains a lot of features which makes it powerful but as with all feature-rich products that also causes that it takes time to learn how to use it properly.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Some editors may use it to its full extent; however, some editors may just need the very basic functionalities.&lt;/p&gt;
&lt;p&gt;Content Manager is designed to be an alternative for Edit Mode, offering a subset of functionality in a modern and mobile-friendly UI.&lt;/p&gt;
&lt;p&gt;It does not focus on web specifically; there is no page tree, no concept or URLs, SEO optimization, publishing workflows etc. It is just a way to edit content items that can later be used in any other channel like social media, web, print and so on.&lt;/p&gt;
&lt;p&gt;It is available to use right after installing the NuGet package. By default, it will present the content items from the whole site.&lt;/p&gt;
&lt;h3&gt;Dashboard&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/link/c67f9abccaf94b0089325473d10032b0.aspx&quot; width=&quot;611&quot; height=&quot;613&quot; /&gt;&lt;/p&gt;
&lt;p&gt;As you can see it looks much different from what we are used to in Edit Mode. There is no tree or panes etc. Instead you get a powerful full-text search with configurable autocomplete, a few content queries and the ability to quickly create a new content item directly from the Dashboard.&lt;/p&gt;
&lt;p&gt;The Dashboard View is completely configurable:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/a4d4968ba9234975b52bfe6fe9719702.aspx&quot; width=&quot;606&quot; height=&quot;607&quot; /&gt;&lt;/p&gt;
&lt;p&gt;All areas marked in &lt;span style=&quot;color:&amp;#32;#ffff00;&amp;#32;background-color:&amp;#32;#000000;&quot;&gt;yellow&lt;/span&gt;&lt;span style=&quot;color:&amp;#32;#ffff00;&amp;#32;background-color:&amp;#32;#000000;&quot;&gt;&amp;nbsp;can be adjusted by the developer or an admin.&lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;List View&lt;/h3&gt;
&lt;p&gt;Content Manager allows to create multiple List View instances. Each can display different Content Types, and each can fetch items from a different folder or even an external content provider.&lt;/p&gt;
&lt;p&gt;Adding and configuring List View instances will be covered in detail in the following blog posts and in our documentation.&lt;/p&gt;
&lt;p&gt;The purpose of this screen is to give the ability to find and browse content items. You can either use the Card view:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/ebe991c4fb9640b88fe4e0d317a769a2.aspx&quot; width=&quot;1587&quot; height=&quot;1017&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Or a table view:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/03330ce851154ee6982213b8fc4a922b.aspx&quot; width=&quot;1589&quot; height=&quot;1017&quot; /&gt;&lt;/p&gt;
&lt;p&gt;On the left, you can find two facets that let you filter by Changed Date and current Content Status.&lt;/p&gt;
&lt;p&gt;Each item is editable:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/d9d8c3d5dc6249f88ad78c93cc70840c.aspx&quot; width=&quot;1567&quot; alt=&quot;editing-cars.gif&quot; height=&quot;1005&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The review workflow is greatly simplified. The user of Content Manager just marks the item as &#39;Ready&#39;. It is not important whether there&#39;s a Content Approval workflow configured behind the scenes or the item is just marked as &#39;Ready to Publish&#39;. This makes it much easier.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/12c92db78d7d4c43a0d5798ceea9de92.aspx&quot; width=&quot;1567&quot; alt=&quot;reviewing-car.gif&quot; height=&quot;1005&quot; /&gt;&lt;/p&gt;
&lt;p&gt;It is easy to create a setup where you have one &quot;super editor&quot; with access to Edit Mode. That user would configure the approval workflows, publish pages etc. but would also review content items created by &quot;content editors&quot; using the new Content Manager. All the &#39;publishing &amp;amp; review&#39; complexity is entirely handled by the &#39;super editor&#39; from Edit Mode.&lt;/p&gt;
&lt;p&gt;Content Manager makes it much easier to perform the basic editing operations.&lt;/p&gt;
&lt;h3&gt;Managing Media Files&lt;/h3&gt;
&lt;p&gt;Content Manager can also be used to work with Digital Assets. It is possible to configure a List Instance to display image files. Then the &#39;Add new&#39; button is replaced with an &#39;Upload image&#39; option.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/fae165b525d7412ba0a9c66d1f679d07.aspx&quot; width=&quot;1181&quot; alt=&quot;asset-view.gif&quot; height=&quot;923&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Mobile friendly&lt;/h3&gt;
&lt;p&gt;Content Manager works fine on mobile and tablets. It&#39;s fully responsive.&lt;/p&gt;
&lt;p&gt;So for example, it is very easy to just open Content Manager on your phone, open the DAM View, click upload image and take a photo with your phone, which will automatically upload the image to a preconfigured folder.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/ad43ef8b9c974a22a40866d9f898a303.aspx&quot; width=&quot;375&quot; alt=&quot;mobile-view.gif&quot; height=&quot;810&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Performance&lt;/h2&gt;
&lt;p&gt;The data is available through an abstraction. We wrote two implementations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Default data repository - included in the main NuGet package and uses the native Episerver.Data api and works to a certain volume of data. (We might try to optimize the performance of the default implementation after v1.)&lt;/li&gt;
&lt;li&gt;Data repository based on the Episerver Search &amp;amp; Navigation package - &lt;span&gt;&lt;a href=&quot;https://nuget.episerver.com/package/?id=EPiServer.Labs.ContentManager.Core.FindQuery&quot;&gt;https://nuget.episerver.com/package/?id=EPiServer.Labs.ContentManager.Core.FindQuery&lt;/a&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;If you already use Episerver Search &amp;amp; Navigation in your solution, then after installing that extra NuGet package, Content Manager will start fetching data from the index giving you a much better performance and scalability. The volume of data is no longer an issue.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;External content providers&lt;/h2&gt;
&lt;p&gt;As mentioned before, it is very easy to use Content Manager to manage content stored outside of the Episerver database and accessible via a Custom Content Provider.&lt;/p&gt;
&lt;p&gt;For example to expose the Product Catalog from your Commerce instance and let the product management team edit the catalog without the need to log in to Edit Mode.&lt;/p&gt;
&lt;h2&gt;Q&amp;amp;A&lt;/h2&gt;
&lt;p&gt;It is important to note that NO changes in the site configuration are required (no changes in models, business logic, etc.).&lt;/p&gt;
&lt;p&gt;It is just enough to install the NuGet to get the default configuration which can be adjusted later on.&lt;/p&gt;
&lt;p&gt;Every feature is opt-in by default.&lt;/p&gt;
&lt;h2&gt;Feedback&lt;/h2&gt;
&lt;p&gt;We would really appreciate any kind of feedback. Stay tuned for more information!&lt;/p&gt;</description>            <guid>https://world.optimizely.com/blogs/bartosz-sekula/dates/2020/4/content-manager---lightweight-editing-ui/</guid>            <pubDate>Mon, 20 Apr 2020 06:48:23 GMT</pubDate>           <category>Blog post</category></item><item> <title>Translate content items without switching context</title>            <link>https://world.optimizely.com/blogs/bartosz-sekula/dates/2020/4/translate-content-items-without-switching-context/</link>            <description>&lt;p&gt;&lt;span&gt;We have just released a new version of &lt;a href=&quot;https://nuget.episerver.com/package/?id=EPiServer.Labs.BlockEnhancements&amp;amp;amp;v=0.8.0&quot;&gt;EPiServer.Labs.BlockEnhancements&lt;/a&gt; v0.8.0&amp;nbsp;that contains an improvement for multilingual sites.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Editors will now be able to create language versions without the need to first navigate to the item and lose their current Editing Context.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/episerver/EPiServer.Labs.BlockEnhancements/raw/master/assets/docsimages/translate.gif&quot; alt=&quot;Translate&quot; /&gt;&lt;/p&gt;
&lt;p&gt;As you can see, the Inline Edit command is not available if the item does not exist in the current editing language.&lt;/p&gt;
&lt;p&gt;After clicking &#39;Create {missingLanguage} language version&#39; a dialog will pop out with a list of language specific properties. It will include both required and optional properties so that the content item can be created in one go.&lt;/p&gt;
&lt;p&gt;Of course the new command is also available from the Assets Pane.&lt;/p&gt;
&lt;p&gt;If you haven&#39;t tried the Block Enhancements labs yet go ahead and install from&amp;nbsp;&lt;a href=&quot;https://nuget.episerver.com/package/?id=EPiServer.Labs.BlockEnhancements&amp;amp;amp;v=0.8.0&quot;&gt;https://nuget.episerver.com/package/?id=EPiServer.Labs.BlockEnhancements&amp;amp;v=0.8.0&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The add-on is open-source, source code is available on our github&amp;nbsp;&lt;a href=&quot;https://github.com/episerver/EPiServer.Labs.BlockEnhancements&quot;&gt;https://github.com/episerver/EPiServer.Labs.BlockEnhancements&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We would, as always, appreciate your feedback!&lt;/p&gt;</description>            <guid>https://world.optimizely.com/blogs/bartosz-sekula/dates/2020/4/translate-content-items-without-switching-context/</guid>            <pubDate>Thu, 16 Apr 2020 05:38:34 GMT</pubDate>           <category>Blog post</category></item><item> <title>Inline Block Publishing - create new blocks without leaving current context</title>            <link>https://world.optimizely.com/blogs/bartosz-sekula/dates/2019/11/simple-block-publishing---episerver-labs-blockenhancements-v0-5-0/</link>            <description>&lt;p&gt;We added a new major improvement to the block publishing that I would like to share with you.&lt;/p&gt;
&lt;p&gt;Previously, the editors were able to only inline edit existing blocks. However, whenever a new block was needed then they still had to use the default form, that caused the current content context to switch.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/38b0980a635f490b9170796fec8f0085.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;It was quite annoying especially in nested block scenarios where you had several levels of nesting i.e. &lt;em&gt;Bootstrap&amp;nbsp;Row Blocks&amp;nbsp;&lt;/em&gt;containing&amp;nbsp;&lt;em&gt;Content Blocks&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;With&amp;nbsp;&lt;a href=&quot;https://nuget.episerver.com/package/?id=EPiServer.Labs.BlockEnhancements&amp;amp;amp;v=0.5.0&quot;&gt;https://nuget.episerver.com/package/?id=EPiServer.Labs.BlockEnhancements&amp;amp;v=0.5.0&lt;/a&gt;&amp;nbsp;it is now possible to create new blocks using the same inline form, without the need to ever leave the current context.&lt;/p&gt;
&lt;p&gt;The best part is that we support nesting. As many levels as needed. And it works both in On-Page Edit (OPE) and in regular Forms view.&lt;/p&gt;
&lt;p&gt;After upgrading to v0.5 the &quot;Create a new block&quot; link will no longer open a big content type selector and switch editor&#39;s context to the newly created content item.&lt;/p&gt;
&lt;p&gt;Instead a simple dialog will be presented:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/882d4a0dd8a0406c91e3d973dec63124.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;After choosing the content type the editor will see the inline edit form straight away. All properties will be included (both required and optional):&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/0fb0650228de4347b1fec575beccc812.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Please note that the new commands are available both from the Forms View and the On-Page Edit.&lt;/p&gt;
&lt;p&gt;Please take a look how it all works:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/episerver/EPiServer.Labs.BlockEnhancements/master/assets/docsimages/create_new_nested_block.gif&quot; /&gt;&lt;/p&gt;</description>            <guid>https://world.optimizely.com/blogs/bartosz-sekula/dates/2019/11/simple-block-publishing---episerver-labs-blockenhancements-v0-5-0/</guid>            <pubDate>Wed, 06 Nov 2019 09:47:13 GMT</pubDate>           <category>Blog post</category></item><item> <title>Editing enhancements for the EPiServer.Forms package</title>            <link>https://bartoszsekula.com/post/2019/10/14/editing-enhancements-for-the-episerver-forms-package</link>            <description>&lt;p&gt;Together with &lt;a title=&quot;Greg Wiechec&quot; href=&quot;https://gregwiechec.com/&quot; target=&quot;_blank&quot;&gt;Greg&lt;/a&gt;&amp;nbsp;we created a little, yet powerful add-on that brings enhancements from &lt;a title=&quot;Block Enhancements&quot; href=&quot;https://github.com/episerver/EPiServer.Labs.BlockEnhancements&quot; target=&quot;_blank&quot;&gt;EPiServer.Labs.BlockEnhancements&lt;/a&gt; to EPiServer.Forms based properties.&lt;/p&gt;
&lt;p&gt;Editor is now able to edit every form inline, without the need to switch the editing context. Just double click any&amp;nbsp;&lt;code&gt;Form Element&lt;/code&gt;&amp;nbsp;to edit its properties. Then use Inline Publish to public the Form Element.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://bartoszsekula.com/image.axd?picture=/2019/forms/inline_edit_form.gif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;You can install it from here:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://nuget.episerver.com/package/?id=Advanced.CMS.Forms&quot;&gt;https://nuget.episerver.com/package/?id=Advanced.CMS.Forms&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The source code is available here:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/advanced-cms/forms&quot;&gt;https://github.com/advanced-cms/forms&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The addon is MIT licensed. Feel free to use/contribute however you want.&lt;/p&gt;</description>            <guid>https://bartoszsekula.com/post/2019/10/14/editing-enhancements-for-the-episerver-forms-package</guid>            <pubDate>Mon, 14 Oct 2019 11:06:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>Nice improvement for the Inline Editing feature</title>            <link>https://bartoszsekula.com/post/2019/10/13/nice-improvement-for-the-inline-editing-feature</link>            <description>&lt;p&gt;I have just pushed a nice little improvement that lets the editor to double-click a block and edit its properties straight away.&lt;/p&gt;
&lt;p&gt;No context switching, no context menus or finding the rights commands, just double click an item in the Content Area and you&#39;re good to go!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://bartoszsekula.com/image.axd?picture=/2019/double_click_inline_edit.gif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;More info here:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/episerver/EPiServer.Labs.BlockEnhancements#inline-block-editing&quot;&gt;https://github.com/episerver/EPiServer.Labs.BlockEnhancements#inline-block-editing&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;v0.4.0 is available here:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://nuget.episerver.com/package/?id=EPiServer.Labs.BlockEnhancements&amp;amp;v=0.4.0&quot;&gt;https://nuget.episerver.com/package/?id=EPiServer.Labs.BlockEnhancements&amp;amp;v=0.4.0&lt;/a&gt;&lt;/p&gt;</description>            <guid>https://bartoszsekula.com/post/2019/10/13/nice-improvement-for-the-inline-editing-feature</guid>            <pubDate>Sun, 13 Oct 2019 20:51:00 GMT</pubDate>           <category>Blog post</category></item></channel>
</rss>