Automatically have Optimizely Content created in the correct place

blog header image

A classic problem is that you want better structure and governance in for example your blocks. Maybe you have a policy to have all Banners or FAQ items in a specific folder so they can easily be found and re-used across the site, but way too often editors take the 'easy' approach and use the "Create a New Block" option in a content area - which by default places the block in the "For this page" folder, rendering re-use impossible. Here is a simple solution to that.

My approach to solving the above problem is pretty straightforward - whenever a content is being created, simply check if it's being created in the right place - and if not, simply change the parent to make sure that it is. 

This is easily done in the ContentEvents - and in the CreatingContent event you can modify the content before it's saved for the first time!

But - in order to make the solution a bit more generic and usable I've included a way to let editors define these rules on a settings page.

Screenshot 2022-12-30 134947.jpg

Note, this solution doesn't handle the fact that editors can move the content after creation - in which case it seems like a conscious decission. It simply just makes sure that the lazy 'create' button still places content where you want it.

I'd recommend putting the list of rules on a settings content item - in the below example it's on the SiteSettings tab of the start page - but that's for you to decide.

I also included a ContentTypeSelectionFactory. Note, there are many other ContentTypeSelectionFactories in other projects - but none included in the core of Optimizely CMS. It basically just produces a list of the content types for the property when you add a rule. A rule basically just that a certain type of content should go to a certain folder on creation.

Screenshot 2022-12-30 134907.jpg

using EPiServer.PlugIn;
using EPiServer.Shell.ObjectEditing;
using System.ComponentModel.DataAnnotations;
/// <summary>
/// Rule that specifies where a certain type of content should be put on creation
/// </summary>
public class ContentCreationRule
{
[Display(Name = "Content Type", Order = 10)]
[SelectOne(SelectionFactoryType = typeof(ContentTypeSelectionFactory))]
public virtual string ContentTypeID { get; set; }
[Display(Name = "Destination", Order = 20)]
public virtual ContentReference ParentToMoveTo { get; set; }
}
[PropertyDefinitionTypePlugIn]
public class ContentCreationRuleList : PropertyList<ContentCreationRule> { }
[ModuleDependency(typeof(InitializationModule))]
public class ContentPlacerInit : IInitializableModule
{
public void Initialize(InitializationEngine context)
{
var cevents = ServiceLocator.Current.GetInstance<IContentEvents>();
cevents.CreatingContent += Cevents_CreatingContent;
}
private void Cevents_CreatingContent(object sender, ContentEventArgs e)
{
var rules = ServiceLocator.Current.GetInstance<IContentLoader>().Get<StartPage>(ContentReference.StartPage).ContentCreationRules;
var rule=rules.FirstOrDefault(r => r.ContentTypeID == e.Content.ContentTypeID.ToString());
if (rule != null)
{
if (!e.Content.ParentLink.CompareToIgnoreWorkID(rule.ParentToMoveTo))
{
e.Content.ParentLink = rule.ParentToMoveTo;
}
}
}
public void Uninitialize(InitializationEngine context)
{
}
}
using EPiServer.Shell.ObjectEditing;
public class ContentTypeSelectionFactory : ISelectionFactory
{
private readonly IContentTypeRepository _repo;
public ContentTypeSelectionFactory(IContentTypeRepository repo)
{
_repo = repo;
}
public IEnumerable<ISelectItem> GetSelections(ExtendedMetadata metadata)
{
return _repo.List()
.Select(r => new SelectItem() { Text = r.Name, Value = r.ID.ToString() });
}
}
//...
[Display(GroupName = Globals.GroupNames.SiteSettings)]
[EditorDescriptor(EditorDescriptorType = typeof(CollectionEditorDescriptor<ContentCreationRule>))]
public virtual IList<ContentCreationRule> ContentCreationRules { get; set; }
//...
view raw StartPage.cs hosted with ❤ by GitHub
Recent posts