Skip to content

Instantly share code, notes, and snippets.

@alasvant
Last active June 18, 2022 11:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alasvant/5cfe6b643ecef37cdc31d9a85abc7383 to your computer and use it in GitHub Desktop.
Save alasvant/5cfe6b643ecef37cdc31d9a85abc7383 to your computer and use it in GitHub Desktop.
Optimizely World blog post: Custom placeholders in Optimizely Forms submission emails. https://world.optimizely.com/blogs/Antti-Alasvuo/Dates/2022/6/custom-place-holders-in-optimizely-forms-submission-emails/
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Web;
using EPiServer;
using EPiServer.Core;
using EPiServer.Forms;
using EPiServer.Forms.Core.Internal;
using EPiServer.Forms.Core.Models;
using EPiServer.ServiceLocation;
using EPiServer.Web.Routing;
namespace AlloyWithFind.Features.FormsCustomPlaceholder
{
public class FormsSystemColumnsPlaceholderProvider : IPlaceHolderProvider
{
/// <summary>
/// IContentLoader service.
/// </summary>
private readonly Injected<IContentLoader> _contentLoader;
/// <summary>
/// IUrlResolver service.
/// </summary>
private readonly Injected<IUrlResolver> _urlResolver;
// Note the placeholder key value is what is displayed in placeholder dropdown
/// <summary>
/// Form submitted placeholder.
/// </summary>
private const string FormSubmittedTimestamp = "Form submitted";
/// <summary>
/// Form submitted by user placeholder.
/// </summary>
private const string FormSubmittedBy = "Form submitted by";
/// <summary>
/// Form submitted from page placeholder.
/// </summary>
private const string FormSubmittedFromPage = "Form submit page";
/// <summary>
/// Text to use if the user was anonymous.
/// </summary>
private const string AnonymousUsername = "anonymous";
/// <inheritdoc/>
public int Order
{
get => 100;
set { }
}
/// <inheritdoc/>
public IEnumerable<PlaceHolder> ExtraPlaceHolders => new PlaceHolder[] {
new PlaceHolder(FormSubmittedTimestamp, null),
new PlaceHolder(FormSubmittedBy, null),
new PlaceHolder(FormSubmittedFromPage, null)
};
/// <inheritdoc/>
public IEnumerable<PlaceHolder> ProcessPlaceHolders(IEnumerable<PlaceHolder> availablePlaceHolders, FormIdentity formIden, HttpRequestBase requestBase = null, Submission submissionData = null, bool performHtmlEncode = true)
{
if (availablePlaceHolders == null)
{
// the DefaultPlaceHolderProvider throw null exception too if the availablePlaceHolders is null
// the interface documentation doesn't say anything about exceptions (which ones should be thrown)
// so do it the same way
throw new ArgumentNullException(nameof(availablePlaceHolders));
}
// get the submission data
var data = submissionData?.Data;
// if we don't have data, then do nothing
if (data == null)
{
return availablePlaceHolders;
}
foreach (var ph in availablePlaceHolders)
{
if (FormSubmittedTimestamp.Equals(ph.Key, StringComparison.Ordinal))
{
// get the submitted timestamp
if (data.TryGetSubmitTime(out DateTime submitted))
{
data.TryGetLanguage(out string languageCode);
try
{
// the timestamp is DateTimeKind.Utc, format the timestamp using
// the forms culture or the default culture
ph.Value = $"{submitted.ToString(GetCultureInfo(languageCode))} UTC";
}
catch {}
}
}
else if(FormSubmittedBy.Equals(ph.Key, StringComparison.Ordinal))
{
// get the submitted by user
if (data.TryGetSubmitUser(out string username))
{
ph.Value = string.IsNullOrWhiteSpace(username) ? AnonymousUsername : username;
}
else
{
ph.Value = AnonymousUsername;
}
}
else if (FormSubmittedFromPage.Equals(ph.Key, StringComparison.Ordinal))
{
// get the form hosted page
if (data.TryGetHostedPage(out ContentReference hostedPage))
{
LoaderOptions loadingOptions;
// try to get the language code
if (data.TryGetLanguage(out string languageCode))
{
loadingOptions = new LoaderOptions { LanguageLoaderOption.FallbackWithMaster(GetCultureInfo(languageCode)) };
}
else
{
loadingOptions = new LoaderOptions { LanguageLoaderOption.MasterLanguage() };
}
// try to load the content in the forms language and fallback to masterlanguage
if (_contentLoader.Service.TryGet(hostedPage, loadingOptions, out PageData page))
{
// and then resolve the url using the language the content was actually loaded in
var pageUrl = _urlResolver.Service.GetUrl(page);
ph.Value = $"{page.Name}, {pageUrl}";
}
}
}
}
return availablePlaceHolders;
}
/// <summary>
/// Get <see cref="CultureInfo"/> object using the <paramref name="languageCode"/>.
/// </summary>
/// <param name="languageCode">Language code (<see cref="CultureInfo.Name"/>).</param>
/// <returns><see cref="CultureInfo"/> object created using the <paramref name="languageCode"/> or if there was an exception then a CultureInfo object is returned using the <see cref="Constants.DefaultCulture"/>.</returns>
private static CultureInfo GetCultureInfo(string languageCode)
{
try
{
if (!string.IsNullOrEmpty(languageCode))
{
return CultureInfo.GetCultureInfo(languageCode);
}
}
catch {}
// this is fallback if there was an exception or the languageCode was null
return CultureInfo.GetCultureInfo(Constants.DefaultCulture);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment