Create Sitefinity connectors using service hooks
Overview
The service hooks in Sitefinity provide a powerful and easy mechanism for creating custom connectors that can update external systems when an event occurs in Sitefinity. For example, you can create a HubSpot contact when a user logs in Sitefinity. To do that you need to create custom triggers and custom actions for the service hooks.
Create custom triggers
You can register your own triggers and actions. They should be registered in custom Sitefinity modules as shown in the sample below. For more information, see Custom modules.
To register a trigger, you need to implement IServiceHookTriggerProvider interface in your module. You also need to create a new trigger class that inherits from ServiceHookTriggerSettingsBase.
When defining trigger settings, you need to add this data in constructor:
-
key- unique key for the trigger (can be any string) -
triggerTitle- title to be displayed in the UI. -
eventType– EventHub event that you want to track -
module- name of the module to be displayed in the UIAlso, you can add additional event filter in the
IsMatchfunction.
using Telerik.Sitefinity.ServiceHooks;
namespace SitefinityWebApp
{
public class HubSpotConnectivityTriggerSettings : ServiceHookTriggerSettingsBase
{
public HubSpotConnectivityTriggerSettings() : base("SomeRandomValue", "User is logged in", typeof(Telerik.Sitefinity.Web.Events.ILoginCompletedEvent), "HubSpotConnectivity")
{
}
}
}
Create custom actions
To register custom actions, you must implement IServiceHookActionProvider interface in your module. In addition, you need to create new action setting class that inherits from IServiceHookActionSettings or the built-in WebhookActionSettings
When defining action setting you need to
-
Define unique
Key -
Define action
Titlethat is displayed in UI -
Define
ParametersTypeproperty (Can be left null if no parameters are needed for the action). The properties of the provided type will be rendered in the Create screen of the Service hooks page and the configured values will be available later during the execution of the action. How the properties are rendered in the UI depends on their type and provided attributes. For more information, see Create widget designers. The properties values will be stored in Advanced settings. If you’d like to encrypt the data in the configurations you can decorate the property with[Sitefinity.Configuration.SecretData]attribute. And if you’d like to hide the field’s value in the UI you can decorate it with the[DataType(customDataType: KnownFieldTypes.Password)]attribute.
To complete the action, you must implement the ExecuteActionAsync method as shown in the sample
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Progress.Sitefinity.Renderer.Designers.Attributes;
using SitefinityWebApp;
using Telerik.Sitefinity.Configuration;
using Telerik.Sitefinity.Data.Linq.Dynamic;
using Telerik.Sitefinity.HubSpotConnector.Configuration;
using Telerik.Sitefinity.Security;
using Telerik.Sitefinity.Security.Model;
using Telerik.Sitefinity.ServiceHooks;
using Telerik.Sitefinity.ServiceHooks.Model;
using Telerik.Sitefinity.Services;
[assembly: SitefinityModule(HubSpotConnectivityModule.ModuleName, typeof(HubSpotConnectivityModule), "HubSpot connectivity", "", StartupType.OnApplicationStart)]
namespace SitefinityWebApp
{
public class HubSpotContactCreationActionSettings : IServiceHookActionSettings
{
/// <inheritdoc />
public virtual string Title
{
get
{
return ActionTitle;
}
}
/// <inheritdoc />
public virtual string Key
{
get
{
return Name;
}
}
/// <inheritdoc />
public virtual Type ParametersType
{
get
{
return typeof(HubSpotContactCreationActionParameters);
}
}
/// <inheritdoc />
public virtual async Task<ServiceHookActionResult> ExecuteActionAsync(ITriggerData triggerData, object parameters, CancellationToken cancellationToken)
{
if (triggerData == null)
throw new ArgumentNullException(nameof(triggerData));
if (parameters == null)
throw new ArgumentNullException(nameof(parameters));
if (triggerData.OriginalEvent != null && triggerData.OriginalEvent.UserId != null)
{
var userId = Guid.Parse(triggerData.OriginalEvent.UserId.ToString());
if (userId != Guid.Empty)
{
var actionParameters = parameters as HubSpotContactCreationActionParameters;
if (actionParameters != null)
{
var apiKey = this.GetApiKey(actionParameters.ApiKey);
if (!string.IsNullOrEmpty(apiKey))
{
var url = $"https://api.hubapi.com/crm/v3/objects/contacts?hapikey={apiKey}";
UserManager userManager = UserManager.GetManager();
User user = userManager.GetUser(userId);
if (user != null)
{
UserProfileManager profileManager = UserProfileManager.GetManager();
var profile = profileManager.GetUserProfile<SitefinityProfile>(user);
var requestContent = new StringContent(
"{{\"properties\":{{\"company\":\"{0}\",\"email\":\"{1}\",\"firstname\":\"{2}\",\"lastname\":\"{3}\",\"phone\":\"{4}\",\"website\":\"{5}\"}}}}"
.Arrange("mycompany", user.Email, profile.FirstName, profile.LastName, "1113334444222", "www.mycompanysite.com"),
Encoding.UTF8,
"application/json");
if (this.CanSendRequest(actionParameters.RoleType, userId))
{
var request = new HttpRequestMessage(HttpMethod.Post, url);
request.Content = requestContent;
// If a hubspot contact does not exist, sending will create new
// If it exists, in HubSpot, in the api key logs for request response, you will see "Contact already exists" message.
using (HttpClient httpClient = new HttpClient())
{
HttpResponseMessage httpResponseMessage = null;
try
{
httpResponseMessage = await httpClient.SendAsync(request);
httpResponseMessage.EnsureSuccessStatusCode();
}
catch (HttpRequestException exc)
{
if (httpResponseMessage != null && httpResponseMessage.StatusCode != System.Net.HttpStatusCode.Conflict)
{
throw exc;
}
}
}
}
}
}
}
}
}
return new ServiceHookActionResult(null, true);
}
private string GetApiKey(string paramaterApiKey)
{
var apiKey = paramaterApiKey;
if (string.IsNullOrEmpty(apiKey))
{
var configManager = ConfigManager.GetManager();
var hubSpotConnectorConfig = configManager.GetSection<HubSpotConnectorConfig>();
apiKey = hubSpotConnectorConfig.HubSpotApiKey;
}
return apiKey;
}
private bool CanSendRequest(RoleType roleType, Guid userId)
{
IList<Role> roles = RoleManager.FindRolesForUser(userId);
var canSendRequest = true;
switch (roleType)
{
case RoleType.NonAdminstrators:
canSendRequest = roles.Any(role => !SecurityManager.IsAdministrativeRole(role.Id));
break;
case RoleType.Administrators:
canSendRequest = roles.Any(role => SecurityManager.IsAdministrativeRole(role.Id));
break;
case RoleType.All:
default:
break;
}
return canSendRequest;
}
private const string Name = "HubSpotContactCreation";
private const string ActionTitle = "HubSpot Contact Creation";
public enum RoleType
{
NonAdminstrators,
Administrators,
All
}
public class HubSpotContactCreationActionParameters
{
[DisplayName("Role type")]
[DefaultValue(RoleType.NonAdminstrators)]
[Choice("[{\"Title\":\"Non adminstrators\",\"Name\":\"NonAdminstrators\",\"Value\":\"NonAdminstrators\",\"Icon\":null},{\"Title\":\"Administrators\",\"Name\":\"Administrators\",\"Value\":\"Administrators\",\"Icon\":null},{\"Title\":\"All\",\"Name\":\"All\",\"Value\":\"All\",\"Icon\":null}]", NotResponsive = true)]
public RoleType RoleType { get; set; }
[DisplayName("Api Key")]
[DefaultValue("")]
public string ApiKey { get; set; }
}
}
}
Finally, you need to register the trigger and the action in a Sitefinity module:
using System;
using System.Collections.Generic;
using SitefinityWebApp;
using Telerik.Sitefinity;
using Telerik.Sitefinity.Abstractions;
using Telerik.Sitefinity.Configuration;
using Telerik.Sitefinity.ServiceHooks;
using Telerik.Sitefinity.Services;
[assembly: SitefinityModule(HubSpotConnectivityModule.ModuleName, typeof(HubSpotConnectivityModule), "HubSpot connectivity", "", StartupType.OnApplicationStart)]
namespace SitefinityWebApp
{
public class HubSpotConnectivityModule : ModuleBase, IServiceHookTriggerProvider, IServiceHookActionProvider
{
public override Guid LandingPageId
{
get { return Guid.Empty; }
}
public override void Initialize(ModuleSettings settings)
{
base.Initialize(settings);
App.WorkWith().Module(Name)
.Initialize();
}
public override void Install(SiteInitializer initializer)
{
}
protected override ConfigSection GetModuleConfig()
{
return null;
}
public override Type[] Managers
{
get { return null; }
}
IDictionary<string, IServiceHookTriggerSettings> IServiceHookTriggerProvider.GetServiceHookTriggersSettings()
{
var triggerSettings = new Dictionary<string, IServiceHookTriggerSettings>();
var loginCompletedTriggerSettings = new HubSpotConnectivityTriggerSettings();
triggerSettings.Add(loginCompletedTriggerSettings.Key, loginCompletedTriggerSettings);
return triggerSettings;
}
IDictionary<string, IServiceHookActionSettings> IServiceHookActionProvider.GetServiceHookActionsSettings()
{
var actionSettings = new Dictionary<string, IServiceHookActionSettings>();
actionSettings.Add("HubSpotContactCreation", new HubSpotContactCreationActionSettings());
return actionSettings;
}
internal const string ModuleName = "HubSpotConnectivity";
}
}
After you have implemented and registered your custom action and trigger you are ready to configure your first service hook using the new connector.
- Navigate to Administration » Servicehooks
- Click Create.
- Select your newly created trigger and action using the selectors.
Customize UI fields
By providing a value for the ParemtersType in the trigger and in the action you can easily build custom UI for your connectors. But if the built-in functionalities are not enough you can customize the fields in the create/edit screen using the Admin App extensions. Learn more about custom fields in GitHub.