Last active
November 17, 2015 20:50
-
-
Save Chuhukon/7a076f7f4b049f23957d to your computer and use it in GitHub Desktop.
Cross session personalisation with EPiServer
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
public abstract class BaseRepository : IDisposable | |
{ | |
protected IDbConnection Connection { get; set; } | |
private bool _disposed; | |
protected ILog Log | |
{ | |
get { return LogManager.GetLogger(GetType()); } | |
} | |
protected BaseRepository() | |
{ | |
Initialize(); | |
} | |
private void Initialize() | |
{ | |
var episerver = ConfigurationManager.ConnectionStrings["EPiServerDB"]; | |
if (!episerver.ConnectionString.Contains("AsynchronousProcessing")) | |
{ | |
Connection = new SqlConnection(string.Format("{0};Asynchronous Processing=true;", episerver.ConnectionString)); | |
} | |
else | |
{ | |
Connection = new SqlConnection(episerver.ConnectionString); | |
} | |
SqlInsightDbProvider.RegisterProvider(); | |
EnsureDatabase(); | |
} | |
protected virtual void Dispose(bool disposing) | |
{ | |
if (!_disposed) | |
{ | |
if (disposing) | |
{ | |
ReleaseMangedResources(); | |
} | |
ReleaseUnmangedResources(); | |
_disposed = true; | |
} | |
} | |
public virtual void ReleaseMangedResources() | |
{ | |
} | |
public virtual void ReleaseUnmangedResources() | |
{ | |
} | |
public void Dispose() // Implement IDisposable | |
{ | |
Dispose(true); | |
GC.SuppressFinalize(this); | |
} | |
/// <summary> | |
/// Ensure that the database has all objects available to use the repository. | |
/// </summary> | |
public abstract void EnsureDatabase(); | |
} | |
public class XCriterionModel | |
{ | |
public Guid AnonymousUserId { get; set; } | |
public string SessionKey { get; set; } | |
public string Data { get; set; } | |
public DateTime Modified { get; set; } | |
} | |
public interface IXCriterionRepository | |
{ | |
/// <summary> | |
/// Get an exsiting or new Xcriterion record | |
/// </summary> | |
/// <param name="userId"></param> | |
/// <param name="key"></param> | |
/// <returns></returns> | |
XCriterionModel GetXCriterion(Guid userId, string key); | |
/// <summary> | |
/// async update an existing Xcriterion | |
/// </summary> | |
/// <param name="criterion"></param> | |
void UpdateXCriterion(XCriterionModel criterion); | |
/// <summary> | |
/// Delete all records which are not modified since x days | |
/// </summary> | |
/// <param name="expirationDays"></param> | |
void Clean(int expirationDays); | |
} | |
public class XCriterionRepository : BaseRepository, IXCriterionRepository | |
{ | |
public XCriterionModel GetXCriterion(Guid userId, string key) | |
{ | |
XCriterionModel result = null; | |
var results = Connection.QuerySql<XCriterionModel>( | |
"SELECT * FROM XCriterionModel WHERE AnonymousUserId = @AnonymousUserId AND SessionKey = @SessionKey", | |
new { AnonymousUserId = userId, SessionKey = key }); | |
if (!results.Any()) | |
{ | |
var newObj = new XCriterionModel() | |
{ | |
AnonymousUserId = userId, | |
SessionKey = key, | |
Data = string.Empty, | |
Modified = DateTime.Now | |
}; | |
try | |
{ | |
Connection.ExecuteSql( | |
"INSERT INTO XCriterionModel SELECT AnonymousUserId, SessionKey, Data, Modified FROM @Criterion", | |
new {Criterion = new List<XCriterionModel> {newObj}}); | |
result = newObj; | |
} | |
catch (Exception ex) | |
{ | |
Log.Error(ex); | |
throw ex; | |
} | |
} | |
else | |
{ | |
result = results.FirstOrDefault(); | |
} | |
return result; | |
} | |
public void UpdateXCriterion(XCriterionModel criterion) | |
{ | |
Connection.ExecuteSqlAsync("UPDATE XCriterionModel SET Data = @Data, Modified = @Modified WHERE AnonymousUserId = @AnonymousUserId AND SessionKey = @SessionKey", criterion); | |
} | |
public void Clean(int expirationDays) | |
{ | |
Connection.ExecuteSql( | |
"DELETE FROM XCriterionModel WHERE Modified < DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE()), @NumberOfDays)", | |
new {NumberOfDays = 0 - expirationDays}); | |
} | |
public override void EnsureDatabase() | |
{ | |
Connection.ExecuteSql( | |
"IF NOT exists(SELECT 1 FROM sys.Tables WHERE Name = N'XCriterionModel' AND Type = N'U')" + | |
"BEGIN" + | |
" CREATE TYPE XCriterionModelTable AS TABLE (AnonymousUserId [uniqueidentifier], SessionKey [nvarchar](50), Data [nvarchar](max), Modified [date]); " + | |
" CREATE TABLE XCriterionModel (" + | |
" AnonymousUserId uniqueidentifier," + | |
" SessionKey nvarchar(50)," + | |
" Data nvarchar(max)," + | |
" Modified date" + | |
" primary key (AnonymousUserId, SessionKey)" + | |
" );" + | |
"END"); | |
} | |
} | |
public abstract class XCriterionBase<T> : CriterionBase<T> where T : class, ICriterionModel, new() | |
{ | |
protected IXCriterionRepository Repository { get; set; } | |
protected abstract string SessionKey { get; } | |
protected XCriterionBase(IXCriterionRepository repository) | |
{ | |
Repository = repository; | |
} | |
private const string AnonymousUserCookieKey = "EPi:AnonymousUserId"; | |
protected Guid AnonymousUserId | |
{ | |
get | |
{ | |
return Guid.Parse(GetCookie(new HttpContextWrapper(HttpContext.Current), AnonymousUserCookieKey, Guid.NewGuid().ToString(), false).Value); | |
} | |
} | |
protected static DateTime CookieExpires | |
{ | |
get { return DateTime.Today.AddDays(int.Parse(ConfigurationManager.AppSettings["XCriterionBase.CookieExpirationDays"])); } | |
} | |
public override void Subscribe(ICriterionEvents criterionEvents) | |
{ | |
criterionEvents.StartSession += Handle_StartSession; | |
} | |
public override void Unsubscribe(ICriterionEvents criterionEvents) | |
{ | |
criterionEvents.StartSession -= Handle_StartSession; | |
} | |
protected static HttpCookie GetCookie(HttpContextBase httpContext, string cookieName, string cookieValue=null, bool overrideExistingValue=false) | |
{ | |
var cookie = httpContext.Request.Cookies[cookieName]; | |
if (cookie == null) | |
{ | |
cookie = new HttpCookie(cookieName) | |
{ | |
Value = cookieValue, | |
Expires = CookieExpires | |
}; | |
httpContext.Response.Cookies.Add(cookie); | |
httpContext.Request.Cookies.Add(cookie); | |
} | |
else //update exsiting | |
{ | |
if(overrideExistingValue) cookie.Value = cookieValue; | |
cookie.Expires = CookieExpires; | |
httpContext.Response.Cookies.Add(cookie); | |
httpContext.Request.Cookies.Set(cookie); | |
} | |
return cookie; | |
} | |
private static void Handle_StartSession(object sender, CriterionEventArgs e) | |
{ | |
//Set the cookie but do not override the existing value. | |
GetCookie(e.HttpContext, AnonymousUserCookieKey, Guid.NewGuid().ToString(), false); | |
} | |
} | |
//based on: EPiServer.Personalization.VisitorGroups.Criteria.ViewedPagesCriterion; | |
[VisitorGroupCriterion(Category = "Cross session Criteria", Description = "Matches after visiting a page", DisplayName = "Visited Page X")] | |
public class ViewedPagesXCriterion : XCriterionBase<ViewedPagesModel> | |
{ | |
protected override string SessionKey | |
{ | |
get | |
{ | |
return "EPi:ViewedPagesX"; | |
} | |
} | |
public ViewedPagesXCriterion(IXCriterionRepository repository) | |
: base(repository) | |
{ | |
} | |
public ViewedPagesXCriterion() | |
: this(ServiceLocator.Current.GetInstance<IXCriterionRepository>()) | |
{ | |
} | |
public override bool IsMatch(IPrincipal principal, HttpContextBase httpContext) | |
{ | |
var viewedPages = GetViewedPages(httpContext); | |
return viewedPages != null && viewedPages.Contains(Model.GetViewedPageLink()); | |
} | |
public override void Subscribe(ICriterionEvents criterionEvents) | |
{ | |
base.Subscribe(criterionEvents); | |
criterionEvents.VisitedPage += VisitedPage; | |
} | |
public override void Unsubscribe(ICriterionEvents criterionEvents) | |
{ | |
base.Unsubscribe(criterionEvents); | |
criterionEvents.VisitedPage -= VisitedPage; | |
} | |
private void VisitedPage(object sender, CriterionEventArgs e) | |
{ | |
var pageLink = e.GetPageLink(); | |
if (PageReference.IsNullOrEmpty(pageLink)) | |
return; | |
AddViewedPage(e.HttpContext, pageLink); | |
} | |
private void AddViewedPage(HttpContextBase httpContext, PageReference pageLink) | |
{ | |
var hashSet = GetViewedPages(httpContext); | |
if (hashSet == null) | |
{ | |
hashSet = new HashSet<PageReference>(); | |
if (httpContext.Session != null) | |
httpContext.Session[SessionKey] = hashSet; | |
} | |
if (!hashSet.Contains(pageLink)) | |
hashSet.Add(pageLink); //add to current set when not exists | |
//update in database | |
Repository.UpdateXCriterion( | |
new XCriterionModel() { | |
AnonymousUserId = AnonymousUserId, | |
SessionKey = SessionKey, | |
Data = JsonConvert.SerializeObject(hashSet), | |
Modified = DateTime.Now | |
}); | |
} | |
private HashSet<PageReference> GetViewedPages(HttpContextBase httpContext) | |
{ | |
if (httpContext.Session == null) | |
return null; | |
var hashSet = httpContext.Session[SessionKey] as HashSet<PageReference>; | |
if (hashSet == null) | |
{ | |
var criterion = Repository.GetXCriterion(AnonymousUserId, SessionKey); | |
if (criterion.Data.ValidateJSON()) | |
{ | |
hashSet = JsonConvert.DeserializeObject<HashSet<PageReference>>(criterion.Data); | |
httpContext.Session[SessionKey] = hashSet; | |
} | |
} | |
return hashSet; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment