Skip to content

Instantly share code, notes, and snippets.

@deanebarker
Created March 13, 2024 22:03
Show Gist options
  • Save deanebarker/addacb8ca2b7a7a257b2e4dbd7c7aa5c to your computer and use it in GitHub Desktop.
Save deanebarker/addacb8ca2b7a7a257b2e4dbd7c7aa5c to your computer and use it in GitHub Desktop.
A POC showing how to create endpoints on an Optimize CMS instance that return fully-contained client-side components
using EPiServer.ServiceLocation;
using Microsoft.AspNetCore.Mvc;
using Optimizely.ContentGraph.Cms.Core.Internal;
namespace DeaneBarker.Optimizely.Cms
{
[Route("component")]
public class ComponentController : Controller
{
private const string KEY_TOKEN = "@key";
private readonly IContentRepository repo = ServiceLocator.Current.GetInstance<IContentRepository>();
private readonly IContentSerializer contentSerializer = ServiceLocator.Current.GetInstance<IContentSerializer>();
// Example: domain.com/component/get/editorial/116
// Will load content id #116, turn it into JSON, and prepend it to the code in "editorial.html"
[Route("get/{name}/{id}")]
public IActionResult Serialize(int id, string name)
{
// Get the content they want and turn it into JSON
var content = repo.Get<IContent>(new ContentReference(id));
var json = contentSerializer.CreateJsonContent(content);
// Get a key
var key = keyProvider();
// Get the component code they want
var component = componentProvider(name).Replace(KEY_TOKEN, key);
return Content(outputProvider(json, component, key), "text/html");
}
// These should probably be injected services...
// Formats the final output
private readonly Func<string, string, string, string> outputProvider = (json, component, key) =>
{
var lines = new List<string>
{
"<!-- This is content to populate the component with -->",
$"<script type=\"application/json\" id=\"{key}-data\">",
json,
"</script>",
"",
"",
component
};
return string.Join(Environment.NewLine, lines);
};
// Provides component code in response to a string
private readonly Func<string, string> componentProvider = (n) =>
{
var basePath = AppDomain.CurrentDomain.BaseDirectory;
var path = Path.Combine(basePath, $"App_Data/components/{n}.html");
return System.IO.File.ReadAllText(path);
};
// Provides a unique key to "namespace" CSS and JS code in the component
// Prevents problems if you have more than one of the same component injected into the same page
private readonly Func<string> keyProvider = () =>
{
var key = string.Join(string.Empty, Guid.NewGuid().ToString().Where(c => Char.IsAsciiLetter(c)));
return $"key-{key}";
};
}
}
<!-- All instances of "@key" will be replaced with the same random value. -->
<!-- This is the component code -->
<!-- (...need a more graceful way of injecting CSS; I can think of a couple ways.) -->
<div id="@key">
<h1 id="@key-heading"></h1>
<div id="@key-content"></div>
</div>
<script>
var data = JSON.parse(document.getElementById('@key-data').textContent);
document.getElementById('@key-heading').innerText = data.Name;
document.getElementById('@key-content').innerHTML = data.MainBody;
</script>
<style>
#@key {
padding: 1em;
border-radius: 0.5em;
border: solid 1px rgb(200,200,200);
max-width: 500px;
}
#@key h1 {
margin-top: 0;
}
</style>
<!-- This is content to populate the component with -->
<script type="application/json" id="key-cfbeabbabcac-data">
{"ContentLink":{"Id":116,"WorkId":0,"GuidValue":"acd8e422-e3a1-4781-a42e-05f56c74cedd","ProviderName":null,"Url":null,"Expanded":null},"Name":"Some Content!!!!!!!!","Language":{"Link":null,"DisplayName":"English","Name":"en"},"ExistingLanguages":[{"Link":null,"DisplayName":"English","Name":"en"}],"MasterLanguage":{"Link":null,"DisplayName":"English","Name":"en"},"ContentType":["Block","EditorialBlock","Content"],"ParentLink":{"Id":115,"WorkId":0,"GuidValue":"deead795-03d7-4edb-9ecf-c51df79c5ae0","ProviderName":null,"Url":"https://localhost:5000/en/Content/components/","Expanded":null},"RouteSegment":null,"Url":null,"Changed":"2024-03-13T21:20:22Z","Created":"2024-03-13T21:20:01Z","StartPublish":"2024-03-13T21:20:22Z","StopPublish":null,"Saved":"2024-03-13T21:27:27Z","Status":"Published","Category":[],"MainBody":"<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi at ultrices lorem, eget egestas ante. Nulla facilisi. Ut ullamcorper orci nec nibh lacinia, molestie molestie diam ultricies. Donec ullamcorper, ex eget egestas tincidunt, augue justo vestibulum ex, quis interdum mauris libero id lacus. Duis odio est, efficitur nec feugiat eget, vestibulum nec odio. Mauris ut justo sed sapien lobortis dictum a ut turpis. Mauris tincidunt velit non malesuada lobortis. Ut at auctor lorem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Ut eleifend auctor nibh, in cursus dui vehicula et. Donec suscipit lacus non quam lacinia dignissim. Pellentesque ac lorem ante. Maecenas ipsum dui, euismod ut ex quis, dictum ultrices dui. Aenean et gravida dui, sed ultricies magna. Mauris risus nisi, placerat sed sem nec, rutrum gravida eros. Sed sodales tortor vel porttitor ullamcorper.&nbsp;</p>","Ancestors":["deead795-03d7-4edb-9ecf-c51df79c5ae0","41118a41-5c8c-4be0-8e73-520ff3de8244","43f936c9-9b23-4ea3-97b2-61c538ad07c9"],"IsCommonDraft":false,"_rbac":["r:Administrators:FullAccess","r:Everyone:Read","r:WebAdmins:FullAccess"],"RelativePath":"","RolesWithReadAccess":["Administrators","Everyone","WebAdmins"],"Shortcut":"","__typename":"EditorialBlock","UsersWithReadAccess":[],"SiteId":"5aa45bf4-5bb2-401a-b99f-84a4bca46c18"}
</script>
<!-- This is the component code -->
<!-- (...need a more graceful way of injecting CSS; I can think of a couple ways.) -->
<div id="key-cfbeabbabcac">
<h1 id="key-cfbeabbabcac-heading"></h1>
<div id="key-cfbeabbabcac-content"></div>
</div>
<script>
var data = JSON.parse(document.getElementById('key-cfbeabbabcac-data').textContent);
document.getElementById('key-cfbeabbabcac-heading').innerText = data.Name;
document.getElementById('key-cfbeabbabcac-content').innerHTML = data.MainBody;
</script>
<style>
#key-cfbeabbabcac {
padding: 1em;
border-radius: 0.5em;
border: solid 1px rgb(200,200,200);
max-width: 500px;
}
#key-cfbeabbabcac h1 {
margin-top: 0;
}
</style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment