Last active
February 11, 2019 23:46
-
-
Save folkertsj/c1b16cb36b24174a841ff52aca10bf0c to your computer and use it in GitHub Desktop.
Episerver Forms Mailchimp Integration
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.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)); | |
} | |
} |
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.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); | |
} | |
} |
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.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); | |
} | |
} |
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.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}"; | |
} | |
} | |
} |
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
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"; | |
} | |
} |
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.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>(); | |
} | |
} | |
} |
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.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; | |
} | |
} | |
} |
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 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