Sync page properties between Optimizely sites
In this blog post I describe how you can sync and replicate pages and properties between different Optimizely sites using ContentDelivery Api.
Published 29th dec 2022
CMS v11 and v12
Prerequisits/todo list
- Site S1 is the mother site - i want to replicate page type PT1, PT2, PT3 to another site(s)
- Site S2 is a child site - this site imports the page types above
- S1 and S2 are not in same vs-solution
- S1 needs to install Episerver.ContentDeliveryAPI (v2 for CMS 11) latest version for CMS 12
- S1 has Episerver.FIND configured and therefor i've installed Episerver.ContentDelivery.Search (needed for this solution)
- S2 is CMS12 and has Epicweb.Optimizely.ContentDelivery.Sync downloaded from github and attached as a project to the main web csproj.
- S2 has the ImportAndSyncJob.cs from github in it's main web csproj
- S2 - copy the Pagetypes (cs files) PT1, PT2 and PT3 to this proj from S1
Download code from Github
https://github.com/Epicweb-Optimizely/Epicweb.Optimizely.ContentDelivery.Sync
Configuration details
Follow the prerequisits/todo list above first
Startup registration
Register the services, point url to mother site, we're useing appsettings file.
services.AddSingleton<IImportApiClient, ImportApiClient>(sp =>
{ // add url and token for contentdeliveryapi to the mother site
return new ImportApiClient(Startup.StaticConfig.GetValue<string>("CustomSync:accesstoken"),Startup.StaticConfig.GetValue<string>("CustomSync:url"));
}); //example appsettings "url": "https://www.myOptiSite.com",
services.AddTransient<ISerializeService, SerializeService>();
services.AddSingleton<ContentDeliveryMapper>();
ImportAndSyncJob.cs
We can use GetPageModels to get pages by pagetypes from mother site, this function is useing Find search to get all pages of these types.
var pages = _serializeService.GetPageModels(
new string[] { "StandardPage", "ProductPage" },// names of pagetypes you want to import
propsToSync: null); // new string[] { "fax", "longitude", "telephone", "visitReservationXHtml", "VideoConferencing", "extraAddress1City" }); //list of props you want to import, if null, import all, this will not create new props, then need to be in code
Mapping helper between ContentDelivery format to pagetypes
_contentDeliveryMapper.Map(page: page, pageModel: pageModel, methodSpecialSerialization: null);
Special Serialization of custom properties (ListProperty)
In this example we have a list property of model OpenTime
Model:
public class OpenTime
{
[Display(Name = "Week")]
public string WeekDays { get; set; }
[Display(Name = "End")]
public string WeekEnds { get; set; }
[Display(Name = "Note")]
public string Note { get; set; }
}
Use mapper like this:
_contentDeliveryMapper.Map(page: page, pageModel: pageModel, methodSpecialSerialization: SerializeCustomPropertyLists);
Example CustomPropertySerializer:
public object SerializeCustomPropertyLists(string propertyDataType, System.Text.Json.JsonElement jsonElement)
{
switch (propertyDataType)
{
case "OpenTimeListProperty":
return jsonElement.ValueKind != JsonValueKind.Null ? jsonElement.Deserialize<IList<OpenTime>>(new JsonSerializerOptions() { PropertyNameCaseInsensitive = true }): null;
default:
break;
}
return null;
}
How about ContentReference and ISelectionFactory properties?
Yes, use [SyncPageName], it will take the contentref and get the PageName of the IContent
Example
On mother site i might look like this:
[Display(GroupName = SystemTabNames.Content, Order = 23)]
[UIHint(SiteUIHints.Regions)]
[Required]
public virtual string Regions { get; set; }
UI:
On child site:
[Display(GroupName = SystemTabNames.Content, Order = 23)]
[SyncPageName]
public virtual string Regions { get; set; }
UI:
This only works for ContentReference or lists of ContentReference
Extensible
Since it is not a plugin, and the code is on github and implemented to your solution, it is simple to extend. Feel free to contribute with new functionality.
Other approaches to consider
We are using content replication, this is one way of doing it, the pros with this content replication strategi was we wanted to sync some propertys on mother site, but wanted to be able to have extended properties in child site on same page model.
You should consider these alternatives to content replication:
- Use ContentProvider-model
- Use Eventsystem to push data from mother site to other sources (https://github.com/episerver/content-events-masstransit)
Pros and cons Content Provider/Content replication
Allan has described it well: Content Provider or Content Replication (codeart.dk)
EOF
Thank you for reading,
Happy coding!
Luc