Created
March 13, 2024 22:03
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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}"; | |
}; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- 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 file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- 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. </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