Skip to content

Instantly share code, notes, and snippets.

@timw255
Last active August 29, 2015 13:56
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 timw255/8943514 to your computer and use it in GitHub Desktop.
Save timw255/8943514 to your computer and use it in GitHub Desktop.
How to do custom, geo aware form notifications in Telerik Sitefinity CMS.

This example relies on the form submitted having a field titled "Zip" and a custom content type with an Address associated.

The UserSelector used for associating Sitefinity users to the stores is available on GitHub too. Sitefinity UserSelector

Oh, and ONE more thing. This uses the GoogleMaps.LocationServices package that's available on NuGet.

using GoogleMaps.LocationServices;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using Telerik.Sitefinity;
using Telerik.Sitefinity.Configuration;
using Telerik.Sitefinity.DynamicModules;
using Telerik.Sitefinity.DynamicModules.Model;
using Telerik.Sitefinity.GenericContent.Model;
using Telerik.Sitefinity.GeoLocations;
using Telerik.Sitefinity.GeoLocations.Model;
using Telerik.Sitefinity.Localization;
using Telerik.Sitefinity.Model;
using Telerik.Sitefinity.Model.ContentLinks;
using Telerik.Sitefinity.Modules.Forms;
using Telerik.Sitefinity.Modules.Forms.Configuration;
using Telerik.Sitefinity.Modules.Forms.Events;
using Telerik.Sitefinity.Project.Configuration;
using Telerik.Sitefinity.Security;
using Telerik.Sitefinity.Security.Model;
using Telerik.Sitefinity.Services;
using Telerik.Sitefinity.Services.Notifications;
using Telerik.Sitefinity.Utilities.TypeConverters;
using Telerik.Sitefinity.Web;
namespace SitefinityWebApp.Custom
{
public class FancyFormNotifications
{
public void SendNotification(IFormEntryCreatedEvent eventInfo)
{
// Pull back all the locations we're going to work with
DynamicModuleManager dynamicModuleManager = DynamicModuleManager.GetManager();
Type storeType = TypeResolutionService.ResolveType("Telerik.Sitefinity.DynamicTypes.Model.Stores.Store");
// Grab all the live locations
var locations = dynamicModuleManager.GetDataItems(storeType).Where(s => s.Status == ContentLifecycleStatus.Live);
var zipField = eventInfo.Controls.Where(c => c.Title == "Zip").FirstOrDefault();
// Where is our visitor located?
var locationService = new GoogleLocationService();
var userLocation = locationService.GetLatLongFromAddress(zipField.Value.ToString());
// Magic number time. Set the radius to whatever you want...just make sure there's a good chance
// it's big enough to return at least one location :)
var radius = 10000;
// Filter and sort our locations
var itemFilter = new ItemFilter { ContentType = storeType.ToString() };
IEnumerable<IGeoLocation> geolocations;
locations = (dynamicModuleManager as IGeoLocationManager).FilterByGeoLocation(locations, userLocation.Latitude, userLocation.Longitude, radius, out geolocations, itemFilter: itemFilter);
var sortedLocations = (dynamicModuleManager as IGeoLocationManager).SortByDistance(locations, geolocations, userLocation.Latitude, userLocation.Longitude, DistanceSorting.Asc);
// Get the closest location, geographically speaking, to our visitor
DynamicContent closestStore = sortedLocations.FirstOrDefault();
if (closestStore == null)
return;
// ****
// Time to start building our custom "subscribers" list
// ****
// These are the IDs of the users who should get the notifications
IEnumerable<Guid> recipientIds = closestStore.GetValue<Guid[]>("NotificationRecipients");
UserManager userManager = UserManager.GetManager();
// Grab the user accounts that correspond to the users selected in our closest location
IEnumerable<User> recipients = userManager.GetUsers().Where(u => recipientIds.Contains(u.Id)).ToList();
// No sense in continuing on if we don't need to (or can't) send the notification
if (recipients.Count() == 0 || !Config.Get<FormsConfig>().Notifications.Enabled)
return;
// ****
// Here's where we really work out who should be on our subscriber list
// and set up the notification email
//
// Helpful Resources:
// http://www.sitefinity.com/documentation/documentationarticles/developers-guide/deep-dive/notification-service/message-jobs
// http://www.sitefinity.com/documentation/documentationarticles/developers-guide/deep-dive/notification-service/subscription-lists
// ****
var ns = SystemManager.GetNotificationService();
var serviceContext = new ServiceContext("ThisApplicationKey", "Forms");
Guid subscriptionListId = eventInfo.FormSubscriptionListId;
// Who's already subscribed?
IEnumerable<ISubscriberRequest> currentSubscribers = ns.GetSubscribers(serviceContext, subscriptionListId, null);
// Remove them from our "recipients" so we don't send duplicate notifications
var currentSubscribersLookup = currentSubscribers.ToLookup(s => s.Email);
var dynamicSubscribers = recipients.Where(r => (!currentSubscribersLookup.Contains(r.Email)));
// We still good on recipients?
if (dynamicSubscribers.Count() == 0)
return;
// This is going to be our impromptu subscriber list
List<ISubscriberRequest> subscribers = new List<ISubscriberRequest>();
foreach (User user in dynamicSubscribers)
{
var subscriber = new SubscriberRequestProxy()
{
Email = user.Email,
ResolveKey = user.Id.ToString()
};
subscribers.Add(subscriber);
}
// Get all the things!
IDictionary<string, string> notificationTemplateItems = GetNotificationTemplateItems(eventInfo);
MessageJobRequestProxy messageJobRequestProxy = new MessageJobRequestProxy()
{
Subscribers = subscribers,
MessageTemplateId = Config.Get<FormsConfig>().Notifications.FormEntrySubmittedNotificationTemplateId,
SenderProfileName = Config.Get<FormsConfig>().Notifications.SenderProfile,
Description = string.Format("Form entry submission email notification for form '{0}'", eventInfo.FormTitle)
};
// Make it so, Number One.
ns.SendMessage(serviceContext, messageJobRequestProxy, notificationTemplateItems);
}
// Borrowed from the Telerik Sitefinity Forms module
// It constructs the items we're going to plug into our notification template
private IDictionary<string, string> GetNotificationTemplateItems(IFormEntryCreatedEvent evt)
{
Dictionary<string, string> strs = new Dictionary<string, string>();
string projectName = Config.Get<ProjectConfig>().ProjectName;
strs.Add("Form.ProjectName", projectName);
string domainUrl = UrlPath.GetDomainUrl();
char[] chrArray = new char[] { '/' };
strs.Add("Form.Host", string.Concat(domainUrl, "/", projectName.TrimStart(chrArray)));
string empty = string.Empty;
HttpContextBase currentHttpContext = SystemManager.CurrentHttpContext;
if (currentHttpContext != null && currentHttpContext.Request != null)
{
empty = currentHttpContext.Request.Url.AbsoluteUri;
}
strs.Add("Form.Url", empty);
strs.Add("Form.Title", evt.FormTitle);
strs.Add("Form.IpAddress", evt.IpAddress);
DateTime sitefinityUITime = evt.SubmissionTime.ToSitefinityUITime();
strs.Add("Form.SubmittedOn", sitefinityUITime.ToString("dd MMMM yyyy"));
string username = evt.Username;
if (string.IsNullOrWhiteSpace(username))
{
username = Res.Get<FormsResources>("AnonymousUsername");
}
strs.Add("Form.Username", username);
if (evt.Controls.Any<IFormEntryEventControl>())
{
strs.Add("Form.Fields", GetNotificationControlsHtml(evt.Controls));
}
return strs;
}
// Borrowed from the Telerik Sitefinity Forms module
// This could be customized to return whatever HTML you want.
private string GetNotificationControlsHtml(IEnumerable<IFormEntryEventControl> controls)
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append("<table width=\"580px\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\" style=\"width: 580px; margin: 0 auto; padding: 0; background-color: #fff; border-left: 1px solid #e4e4e4; border-right: 1px solid #e4e4e4; border-top: 1px solid #e4e4e4;\">");
foreach (IFormEntryEventControl control in controls)
{
switch (control.Type)
{
case FormEntryEventControlType.FieldControl:
case FormEntryEventControlType.FileFieldControl:
{
stringBuilder.AppendFormat("<tr><th width=\"190px\" bgcolor=\"#f2f2f2\" style=\"width: 190px; padding: 10px 9px; border-bottom: 1px solid #e4e4e4; border-right: 1px solid #e4e4e4; font-family: arial,verdana,sans-serif; line-height: 1.2; font-size: 11px; font-weight: normal; font-style: normal; text-align: left; background-color: #f2f2f2;\">{0}</th>", control.Title ?? "");
stringBuilder.Append("<td style=\"padding: 10px 9px; border-bottom: 1px solid #e4e4e4; font-family: arial,verdana,sans-serif; line-height: 1.2; font-size: 12px; font-weight: normal; font-style: normal; text-align: left;\">");
string contentLinksHtml = "";
if (control.Type != FormEntryEventControlType.FieldControl)
{
IEnumerable<ContentLink> value = control.Value as IEnumerable<ContentLink>;
if (value != null)
{
contentLinksHtml = GetContentLinksHtml(value);
}
}
else
{
contentLinksHtml = control.Value.ToString();
}
stringBuilder.Append(contentLinksHtml);
stringBuilder.Append("</td></tr>");
continue;
}
case FormEntryEventControlType.SectionHeader:
{
stringBuilder.AppendFormat("<tr><th colspan='2' style=\"padding: 12px 9px 12px; border-bottom: 1px solid #e4e4e4; font-family: arial,verdana,sans-serif; line-height: 1.2; font-size: 15px; font-weight: normal; font-style: normal; text-align: left;\">{0}<th/></tr>", control.Title);
continue;
}
case FormEntryEventControlType.InstructionalText:
{
stringBuilder.AppendFormat("<tr><th colspan='2' style=\"padding: 9px; border-bottom: 1px solid #e4e4e4; font-family: arial,verdana,sans-serif; line-height: 1.2; font-size: 12px; font-weight: normal; font-style: italic; color: #666; text-align: left;\">{0}<th/></tr>", control.Title);
continue;
}
default:
{
continue;
}
}
}
stringBuilder.Append("</table>");
return stringBuilder.ToString();
}
// Borrowed from the Telerik Sitefinity Forms module
private string GetContentLinksHtml(IEnumerable<ContentLink> contentLinks)
{
StringBuilder stringBuilder = new StringBuilder();
foreach (ContentLink contentLink in contentLinks)
{
if (stringBuilder.Length > 0)
{
stringBuilder.Append(", ");
}
string childItemAdditionalInfo = contentLink.ChildItemAdditionalInfo;
int num = childItemAdditionalInfo.IndexOf('|');
if (num == -1)
{
continue;
}
string str = childItemAdditionalInfo.Substring(0, num);
string str1 = childItemAdditionalInfo.Substring(num + 1);
stringBuilder.AppendFormat("<a href=\"{0}\">{1}</a>", str1, HttpUtility.HtmlEncode(str));
}
return stringBuilder.ToString();
}
}
}
protected void Application_Start(object sender, EventArgs e)
{
Bootstrapper.Initialized += new EventHandler<ExecutedEventArgs>(Bootstrapper_Initialized);
}
protected void Bootstrapper_Initialized(object sender, ExecutedEventArgs e)
{
if (e.CommandName == "Bootstrapped")
{
// Kick off our own custom event handler
EventHub.Subscribe<IFormEntryCreatedEvent>(evt => FormEntryCreatedEventHandler(evt));
}
}
public void FormEntryCreatedEventHandler(IFormEntryCreatedEvent eventInfo)
{
// Make sure we need to even react to this form entry.
var zipField = eventInfo.Controls.Where(c => c.Title == "Zip").FirstOrDefault();
if (zipField == null || eventInfo.FormName != "sf_contactform")
return;
FancyFormNotifications ffn = new FancyFormNotifications();
ffn.SendNotification(eventInfo);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment