Skip to content

Instantly share code, notes, and snippets.

@folkertsj
Last active February 11, 2019 23:46
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 folkertsj/c1b16cb36b24174a841ff52aca10bf0c to your computer and use it in GitHub Desktop.
Save folkertsj/c1b16cb36b24174a841ff52aca10bf0c to your computer and use it in GitHub Desktop.
Episerver Forms Mailchimp Integration
using EPiServer.Framework.Cache;
using System;
namespace MailChimpSample.Business.Caching
{
public abstract class AbstractCacheService : ICacheService
{
private static readonly TimeSpan _DefaultCacheDuration;
private static readonly CacheEvictionPolicy _DefaultCacheEvictionPolicy;
protected virtual TimeSpan DefaultCacheDuration => _DefaultCacheDuration;
protected virtual CacheEvictionPolicy DefaultCacheEvictionPolicy => _DefaultCacheEvictionPolicy;
static AbstractCacheService()
{
_DefaultCacheDuration = TimeSpan.FromHours(24);
_DefaultCacheEvictionPolicy = new CacheEvictionPolicy(_DefaultCacheDuration, CacheTimeoutType.Absolute);
}
public abstract TValue Get<TValue>(string cacheKey) where TValue : class;
public abstract TValue Get<TValue>(string cacheKey, CacheEvictionPolicy cacheEvictionPolicy, Func<TValue> getItemCallback) where TValue : class;
public TValue Get<TValue>(string cacheKey, Func<TValue> getItemCallback) where TValue : class
=> Get(cacheKey, DefaultCacheEvictionPolicy, getItemCallback);
public TValue Get<TValue>(string cacheKey, int durationInMinutes, Func<TValue> getItemCallback) where TValue : class
=> Get(cacheKey, new CacheEvictionPolicy(TimeSpan.FromMinutes(durationInMinutes), CacheTimeoutType.Absolute), getItemCallback);
public abstract TValue Get<TValue, TId>(string cacheKeyFormat, TId id, int durationInMinutes, Func<TId, TValue> getItemCallback) where TValue : class;
public TValue Get<TValue, TId>(string cacheKeyFormat, TId id, Func<TId, TValue> getItemCallback) where TValue : class
=> Get(cacheKeyFormat, id, (int)DefaultCacheDuration.TotalMinutes, getItemCallback);
protected virtual string FormatKey<TId>(string cacheKeyFormat, TId id) => string.Format(cacheKeyFormat, id);
public abstract void Remove(string cacheKey);
public void Remove<TId>(string cacheKey, TId id) => Remove(FormatKey(cacheKey, id));
}
}
using EPiServer.Framework.Cache;
using EPiServer.ServiceLocation;
using System;
namespace MailChimpSample.Business.Caching
{
[ServiceConfiguration(ServiceType = typeof(ICacheService), Lifecycle = ServiceInstanceScope.Transient)]
public class EPiObjectInstanceCache : AbstractCacheService
{
private readonly ISynchronizedObjectInstanceCache objectCacheService;
public EPiObjectInstanceCache(ISynchronizedObjectInstanceCache objectCacheService)
{
this.objectCacheService = objectCacheService;
}
public override TValue Get<TValue>(string cacheKey) => objectCacheService.Get(cacheKey) as TValue;
public override TValue Get<TValue>(string cacheKey, CacheEvictionPolicy cacheEvictionPolicy, Func<TValue> getItemCallback)
{
TValue item = Get<TValue>(cacheKey);
if (item == null)
{
item = getItemCallback();
objectCacheService.Insert(cacheKey, item, cacheEvictionPolicy);
}
return item;
}
public override TValue Get<TValue, TId>(string cacheKeyFormat, TId id, int durationInMinutes, Func<TId, TValue> getItemCallback)
{
string cacheKey = string.Format(cacheKeyFormat, id);
TValue item = Get<TValue>(cacheKey);
if (item == null)
{
item = getItemCallback(id);
objectCacheService.Insert(cacheKey, item, new CacheEvictionPolicy(TimeSpan.FromMinutes(durationInMinutes), CacheTimeoutType.Absolute));
}
return item;
}
public override void Remove(string cacheKey) => objectCacheService.Remove(cacheKey);
}
}
using EPiServer.Framework.Cache;
using System;
namespace MailChimpSample.Business.Caching
{
public interface ICacheService
{
TValue Get<TValue>(string cacheKey) where TValue : class;
TValue Get<TValue>(string cacheKey, Func<TValue> getItemCallback) where TValue : class;
TValue Get<TValue>(string cacheKey, CacheEvictionPolicy cacheEvictionPolicy, Func<TValue> getItemCallback) where TValue : class;
TValue Get<TValue>(string cacheKey, int durationInMinutes, Func<TValue> getItemCallback) where TValue : class;
TValue Get<TValue, TId>(string cacheKeyFormat, TId id, Func<TId, TValue> getItemCallback) where TValue : class;
TValue Get<TValue, TId>(string cacheKeyFormat, TId id, int durationInMinutes, Func<TId, TValue> getItemCallback) where TValue : class;
void Remove(string cacheKey);
void Remove<TId>(string cacheKey, TId id);
}
}
using EPiServer.PlugIn;
using EPiServer.Scheduler;
using EPiServer.ServiceLocation;
using MailChimpSample.Business.Caching;
using System;
namespace MailChimpSample.Business.MailChimpAPI
{
[ScheduledPlugIn(DisplayName = "Mail Chimp Cache Manager", IntervalType = EPiServer.DataAbstraction.ScheduledIntervalType.Hours, Restartable = true, IntervalLength = 6)]
public class MailChimpCacheScheduledJob : ScheduledJobBase
{
private bool _stopSignaled;
readonly MailChimpService mailchimpService;
readonly ICacheService cacheService;
public MailChimpCacheScheduledJob()
{
IsStoppable = true;
this.mailchimpService = ServiceLocator.Current.GetInstance<MailChimpService>();
this.cacheService = ServiceLocator.Current.GetInstance<ICacheService>();
}
/// <summary>
/// Called when a user clicks on Stop for a manually started job, or when ASP.NET shuts down.
/// </summary>
public override void Stop()
{
_stopSignaled = true;
}
/// <summary>
/// Called when a scheduled job executes
/// </summary>
/// <returns>A status message to be stored in the database log and visible from admin mode</returns>
public override string Execute()
{
//Call OnStatusChanged to periodically notify progress of job for manually started jobs
OnStatusChanged(String.Format("Starting execution of {0}", this.GetType()));
// Clear Lists
this.cacheService.Remove(MailChimpConstants.ListIds);
var lists = this.mailchimpService.GetLists();
var cacheItemsUpdated = 0;
foreach (var list in lists)
{
if (_stopSignaled)
{
return "Stop of job was called";
}
this.cacheService.Remove(string.Format(MailChimpConstants.ListId, list.Id));
this.cacheService.Get(string.Format(MailChimpConstants.ListId, list.Id), 720, () =>
{
return list;
});
this.cacheService.Remove(string.Format(MailChimpConstants.ListMergeFields, list.Id));
this.mailchimpService.GetFormFields(list.Id);
cacheItemsUpdated++;
this.OnStatusChanged($"Cache Items Updated: {cacheItemsUpdated}");
}
// Clear the dictionary
this.cacheService.Remove(MailChimpConstants.ListDictionaryItems);
this.mailchimpService.GetListsAsDictionary();
//For long running jobs periodically check if stop is signaled and if so stop execution
return $"Cache Items Updated: {cacheItemsUpdated}";
}
}
}
namespace MailChimpSample.Business.MailChimpAPI
{
public static class MailChimpConstants
{
public const string ListIds = "MailChimp_List_Items";
public const string ListId = "MailChimp_List_Item_{0}";
public const string ListMergeFields = "MailChimp_List_MergeFields_{0}";
public const string ListDictionaryItems = "MailChimp_List_Dictionary";
}
}
using EPiServer.Forms.Core;
using EPiServer.Forms.Core.Internal.Autofill;
using EPiServer.Forms.Core.Internal.ExternalSystem;
using EPiServer.ServiceLocation;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MailChimpSample.Business.MailChimpAPI
{
public class MailChimpDataSystem : IExternalSystem, IAutofillProvider
{
readonly MailChimpService mailChimpService;
public MailChimpDataSystem()
{
this.mailChimpService = ServiceLocator.Current.GetInstance<MailChimpService>();
}
public virtual string Id =>
"MailChimpDataSystem";
public virtual IEnumerable<IDatasource> Datasources
{
get
{
var items = this.mailChimpService.GetListsAsDictionary();
var datasources = items
.Select(x => new Datasource()
{
Name = x.Value,
Id = x.Key,
OwnerSystem = this,
Columns = this.mailChimpService.GetFormFieldsAsDictionary(x.Key)
});
return datasources;
}
}
public IEnumerable<string> GetSuggestedValues(IDatasource selectedDatasource, IEnumerable<RemoteFieldInfo> remoteFieldInfos, ElementBlockBase content, IFormContainerBlock formContainerBlock, HttpContextBase context)
{
if (selectedDatasource == null || remoteFieldInfos == null)
Enumerable.Empty<string>();
if (this.Datasources.Any(ds => ds.Id == selectedDatasource.Id) // datasource belong to this system
&& remoteFieldInfos.Any(mi => mi.DatasourceId == selectedDatasource.Id)) // and remoteFieldInfos is for our system datasource
{
return Enumerable.Empty<string>();
}
return Enumerable.Empty<string>();
}
}
}
using EPiServer.Forms.Core.PostSubmissionActor;
using EPiServer.ServiceLocation;
using System.Collections.Generic;
namespace MailChimpSample.Business.MailChimpAPI
{
public class MailChimpPostActor : PostSubmissionActorBase
{
readonly MailChimpService mailChimpService;
public MailChimpPostActor()
{
this.mailChimpService = ServiceLocator.Current.GetInstance<MailChimpService>();
}
public override object Run(object input)
{
string submissionResult = string.Empty;
if (this.SubmissionData == null)
return submissionResult;
Dictionary<string, string> postedFormDataDictionary = new Dictionary<string, string>();
foreach (KeyValuePair<string, object> pair in this.SubmissionData.Data)
if (!pair.Key.ToLower().StartsWith("systemcolumn") && pair.Value != null)
postedFormDataDictionary.Add(pair.Key, pair.Value.ToString());
var mappings = base.ActiveExternalFieldMappingTable;
if (mappings != null)
{
Dictionary<string, string> formDataAttributes = new Dictionary<string, string>();
string listId = string.Empty;
foreach (var item in mappings)
{
if (item.Value != null)
{
var fieldName = item.Key;
var remoteFieldName = item.Value.ColumnId;
if (postedFormDataDictionary.ContainsKey(fieldName))
{
formDataAttributes.Add(remoteFieldName, postedFormDataDictionary[fieldName]);
if (string.IsNullOrWhiteSpace(listId))
listId = item.Value.DatasourceId;
}
}
}
if (formDataAttributes.Count > 0)
this.mailChimpService.Send(listId, formDataAttributes);
}
return submissionResult;
}
}
}
using EPiServer.ServiceLocation;
using MailChimp.Net;
using MailChimp.Net.Interfaces;
using MailChimp.Net.Models;
using MailChimpSample.Business.Caching;
using Nito.AsyncEx;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
namespace MailChimpSample.Business.MailChimpAPI
{
[ServiceConfiguration(ServiceType = typeof(MailChimpService), Lifecycle = ServiceInstanceScope.Transient)]
public class MailChimpService
{
public readonly IMailChimpManager mailChimpManager;
readonly ICacheService cacheService;
public const int CacheTimeout = 720; // in minutes
public MailChimpService(ICacheService cacheService)
{
this.cacheService = cacheService;
this.mailChimpManager = new MailChimpManager(ConfigurationManager.AppSettings["MailChimpApiKey"]);
}
public List<List> GetLists() => this.cacheService.Get(MailChimpConstants.ListIds, CacheTimeout, () =>
{
return AsyncContext.Run(() =>
this.mailChimpManager.Lists
.GetAllAsync())
.OrderBy(x => x.Name)
.ToList();
});
public Dictionary<string, string> GetListsAsDictionary()
{
return this.cacheService.Get(MailChimpConstants.ListDictionaryItems, CacheTimeout, () =>
{
var list = new Dictionary<string, string>();
var items = this.GetLists();
foreach (var item in items)
{
if (!list.ContainsKey(item.Id))
{
list.Add(item.Id, item.Name);
}
}
return list;
});
}
public List GetListByName(string name)
{
string cacheKey = string.Format(MailChimpConstants.ListId, name);
return this.cacheService.Get(cacheKey, CacheTimeout, () =>
{
var result = this.GetLists().FirstOrDefault(x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
return result;
});
}
public List GetListById(string id)
{
string cacheKey = string.Format(MailChimpConstants.ListId, id);
return this.cacheService.Get(cacheKey, CacheTimeout, () =>
{
return AsyncContext.Run(() =>
this.mailChimpManager.Lists.GetAsync(id.ToString()));
});
}
public List<MergeField> GetFormFields(string listId) => this.cacheService.Get(string.Format(MailChimpConstants.ListMergeFields, listId), CacheTimeout, () =>
{
var mergeFields = AsyncContext.Run(() =>
this.mailChimpManager.MergeFields
.GetAllAsync(listId.ToString()))
.ToList();
return mergeFields;
});
public Dictionary<string, string> GetFormFieldsAsDictionary(string listId)
{
var dictionary = new Dictionary<string, string>() { { "EMAIL", "Email" } };
this.GetFormFields(listId)
.Select(x => new KeyValuePair<string, string>(x.Tag, x.Name))
.ToList()
.ForEach(x => dictionary.Add(x.Key, x.Value));
return dictionary;
}
public Member Send(string listId, Dictionary<string, string> fields)
{
var externalFields = this.GetFormFields(listId);
var member = new Member()
{
EmailAddress = fields["EMAIL"],
StatusIfNew = Status.Subscribed
};
foreach (var externalField in externalFields)
{
if (fields.ContainsKey(externalField.Tag))
member.MergeFields.Add(externalField.Tag, fields[externalField.Tag]);
}
return AsyncContext.Run(() =>
this.mailChimpManager.Members.AddOrUpdateAsync(listId.ToString(), member));
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment