Skip to content

Commit 51bf613

Browse files
committed
Account association and custom fields mapping
1 parent c6a9303 commit 51bf613

28 files changed

+466
-59
lines changed

src/Umbraco.Forms.Integrations.Crm.ActiveCampaign/ActiveCampaignComposer.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,15 @@ public void Compose(IUmbracoBuilder builder)
2020
.Add<ActiveCampaignContactsWorkflow>();
2121

2222
builder.Services
23-
.AddHttpClient(Constants.ContactsHttpClient, client =>
23+
.AddHttpClient(Constants.HttpClient, client =>
2424
{
2525
client.BaseAddress = new Uri(
26-
$"{builder.Config.GetSection(Constants.SettingsPath)[nameof(ActiveCampaignSettings.BaseUrl)]}/api/3/contacts");
26+
$"{builder.Config.GetSection(Constants.SettingsPath)[nameof(ActiveCampaignSettings.BaseUrl)]}/api/3");
2727
client.DefaultRequestHeaders
2828
.Add("Api-Token", builder.Config.GetSection(Constants.SettingsPath)[nameof(ActiveCampaignSettings.ApiKey)]);
2929
});
3030

31+
builder.Services.AddSingleton<IAccountService, AccountService>();
3132
builder.Services.AddSingleton<IContactService, ContactService>();
3233
}
3334
}

src/Umbraco.Forms.Integrations.Crm.ActiveCampaign/ActiveCampaignContactsWorkflow.cs

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,26 @@ public class ActiveCampaignContactsWorkflow : WorkflowType
1515
{
1616
private readonly ActiveCampaignSettings _settings;
1717

18+
private readonly IAccountService _accountService;
19+
1820
private readonly IContactService _contactService;
1921

22+
[Core.Attributes.Setting("Account",
23+
Description = "Please select an account",
24+
View = "~/App_Plugins/UmbracoForms.Integrations/Crm/ActiveCampaign/accountpicker.html")]
25+
public string Account { get; set; }
26+
2027
[Core.Attributes.Setting("Contact Mappings",
2128
Description = "Map contact details with form fields",
2229
View = "~/App_Plugins/UmbracoForms.Integrations/Crm/ActiveCampaign/contact-mapper.html")]
2330
public string ContactMappings { get; set; }
2431

25-
public ActiveCampaignContactsWorkflow(IOptions<ActiveCampaignSettings> options, IContactService contactService)
32+
[Core.Attributes.Setting("Custom Field Mappings",
33+
Description = "Map contact custom fields with form fields",
34+
View = "~/App_Plugins/UmbracoForms.Integrations/Crm/ActiveCampaign/customfield-mapper.html")]
35+
public string CustomFieldMappings { get; set; }
36+
37+
public ActiveCampaignContactsWorkflow(IOptions<ActiveCampaignSettings> options, IAccountService accountService, IContactService contactService)
2638
{
2739
Id = new Guid(Constants.WorkflowId);
2840
Name = "ActiveCampaign Contacts Workflow";
@@ -31,6 +43,8 @@ public ActiveCampaignContactsWorkflow(IOptions<ActiveCampaignSettings> options,
3143

3244
_settings = options.Value;
3345

46+
_accountService = accountService;
47+
3448
_contactService = contactService;
3549
}
3650

@@ -41,14 +55,34 @@ public override WorkflowExecutionStatus Execute(WorkflowExecutionContext context
4155
var email = context.Record.RecordFields[Guid.Parse(mappings.First(p => p.ContactField == "email").FormField.Id)]
4256
.ValuesAsString();
4357

58+
// Check if contact exists.
4459
var contacts = _contactService.Get(email).Result;
4560

46-
var requestDto = new ContactRequestDto { Contact = Build(context.Record) };
61+
var requestDto = new ContactDetailDto { Contact = Build(context.Record) };
62+
4763
if (contacts.Contacts.Count > 0) requestDto.Contact.Id = contacts.Contacts.First().Id;
4864

49-
var result = _contactService.CreateOrUpdate(requestDto, contacts.Contacts.Count > 0).Result;
65+
// Set contact custom fields.
66+
if(!string.IsNullOrEmpty(CustomFieldMappings))
67+
{
68+
var customFieldMappings = JsonSerializer.Deserialize<List<CustomFieldMappingDto>>(CustomFieldMappings);
5069

51-
if (!result) return WorkflowExecutionStatus.Failed;
70+
requestDto.Contact.FieldValues = customFieldMappings.Select(p => new CustomFieldValueDto
71+
{
72+
Field = p.CustomField.Id,
73+
Value = context.Record.RecordFields[Guid.Parse(p.FormField.Id)].ValuesAsString()
74+
}).ToList();
75+
}
76+
77+
var contactId = _contactService.CreateOrUpdate(requestDto, contacts.Contacts.Count > 0).Result;
78+
79+
if (string.IsNullOrEmpty(contactId)) return WorkflowExecutionStatus.Failed;
80+
81+
// Associate contact with account if last one is specified.
82+
if(!string.IsNullOrEmpty(Account))
83+
{
84+
var associationResponse = _accountService.CreateAssociation(int.Parse(Account), int.Parse(contactId)).Result;
85+
}
5286

5387
return WorkflowExecutionStatus.Completed;
5488
}
@@ -61,11 +95,14 @@ public override List<Exception> ValidateSettings()
6195
list.Add(new Exception("Contact mappings are required."));
6296

6397
var mappings = JsonSerializer.Deserialize<List<ContactMappingDto>>(ContactMappings);
64-
bool validMappings = _settings.ContactFields
65-
.Where(p => p.Required)
66-
.Any(p => mappings.Select(q => q.ContactField).Contains(p.Name));
67-
if (!validMappings)
68-
list.Add(new Exception("Invalid contact mappings. Please make sure the mandatory fields are mapped."));
98+
foreach(var contactField in _settings.ContactFields.Where(p => p.Required))
99+
{
100+
if(!mappings.Any(p => p.ContactField == contactField.Name))
101+
{
102+
list.Add(new Exception("Invalid contact mappings. Please make sure the mandatory fields are mapped."));
103+
break;
104+
}
105+
}
69106

70107
return list;
71108
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
function accountPickerController($scope, umbracoFormsIntegrationsCrmActiveCampaignResource) {
2+
3+
var vm = this;
4+
5+
umbracoFormsIntegrationsCrmActiveCampaignResource.getAccounts().then(function (response) {
6+
vm.accounts = response.accounts;
7+
8+
vm.selectedAccount = $scope.setting.value;
9+
});
10+
11+
vm.save = function () {
12+
$scope.setting.value = vm.selectedAccount.length > 0
13+
? vm.selectedAccount : "";
14+
15+
16+
}
17+
}
18+
19+
angular.module("umbraco")
20+
.controller("UmbracoForms.Integrations.Crm.ActiveCampaign.AccountPickerController", accountPickerController)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<div ng-controller="UmbracoForms.Integrations.Crm.ActiveCampaign.AccountPickerController as vm">
2+
<div>
3+
<select ng-model="vm.selectedAccount" ng-change="vm.save()">
4+
<option value="">Select an account</option>
5+
<option ng-repeat="account in vm.accounts" value="{{ account.id }}">{{ account.name }}</option>
6+
</select>
7+
</div>
8+
9+
<!--
10+
<div ng-if="vm.contactFields.length > 0" class="alert alert-info mt2 mr3" role="alert">
11+
<span>Mandatory fields: {{ vm.requiredContactFields }}</span>
12+
</div>
13+
14+
<div class="mt2">
15+
<umb-button type="button" class="mt2"
16+
action="vm.addMapping()"
17+
label="Add mapping"
18+
disabled="vm.selectedMapping.contactField.length === 0 || vm.selectedMapping.formField.length === 0">
19+
</umb-button>
20+
</div>
21+
22+
<div class="umb-forms-mappings mt2" ng-if="vm.contactMappings.length > 0">
23+
24+
<div class="umb-forms-mapping-header">
25+
<div class="umb-forms-mapping-field -no-margin-left">Contact Field</div>
26+
<div class="umb-forms-mapping-field">Form Field</div>
27+
<div class="umb-forms-mapping-remove -no-margin-right"></div>
28+
</div>
29+
30+
<div class="umb-forms-mapping" ng-repeat="mapping in vm.contactMappings">
31+
<div class="umb-forms-mapping-field -no-margin-left">{{ mapping.contactField }}</div>
32+
<div class="umb-forms-mapping-field">{{ mapping.formField.value }}</div>
33+
<div class="umb-forms-mapping-remove -no-margin-right">
34+
<a href="" ng-click="vm.deleteMapping($index)">
35+
<i class="icon-trash"></i>
36+
</a>
37+
</div>
38+
</div>
39+
</div>-->
40+
41+
</div>

src/Umbraco.Forms.Integrations.Crm.ActiveCampaign/App_Plugins/UmbracoForms.Integrations/Crm/ActiveCampaign/activecampaign.resource.js

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,27 @@
11
function activeCampaignResource($http, umbRequestHelper) {
22

3-
const apiEndpoint = "backoffice/UmbracoFormsIntegrationsCrmActiveCampaign/Contacts";
3+
const accountsApiEndpoint = "backoffice/UmbracoFormsIntegrationsCrmActiveCampaign/Accounts";
4+
const contactsApiEndpoint = "backoffice/UmbracoFormsIntegrationsCrmActiveCampaign/Contacts";
45

56
return {
67
checkApiAccess: function () {
78
return umbRequestHelper.resourcePromise(
8-
$http.get(`${apiEndpoint}/CheckApiAccess`),
9+
$http.get(`${contactsApiEndpoint}/CheckApiAccess`),
910
"Failed to get resource");
1011
},
11-
getContactFields: function () {
12+
getAccounts: function () {
1213
return umbRequestHelper.resourcePromise(
13-
$http.get(`${apiEndpoint}/GetContactFields`),
14+
$http.get(`${accountsApiEndpoint}/GetAccounts`),
1415
"Failed to get resource");
1516
},
16-
getCurrencies: function () {
17-
return umbRequestHelper.resourcePromise(
18-
$http.get(`${apiEndpoint}/GetCurrencies`),
19-
"Failed to get resource");
20-
},
21-
getAvailableMappingFields: function () {
17+
getContactFields: function () {
2218
return umbRequestHelper.resourcePromise(
23-
$http.get(`${apiEndpoint}/GetAvailableMappingFields`),
19+
$http.get(`${contactsApiEndpoint}/GetContactFields`),
2420
"Failed to get resource");
2521
},
26-
getRequiredMappingFields: function () {
22+
getCustomFields: function () {
2723
return umbRequestHelper.resourcePromise(
28-
$http.get(`${apiEndpoint}/GetRequiredMappingFields`),
24+
$http.get(`${contactsApiEndpoint}/GetCustomFields`),
2925
"Failed to get resource");
3026
}
3127
};

src/Umbraco.Forms.Integrations.Crm.ActiveCampaign/App_Plugins/UmbracoForms.Integrations/Crm/ActiveCampaign/contact-mapper.controller.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,17 @@
2525

2626
vm.contactMappings.splice(index, 1);
2727

28-
$scope.setting.value = JSON.stringify(vm.mappings);
28+
$scope.setting.value = JSON.stringify(vm.contactMappings);
2929
}
3030

3131

3232
function init() {
3333

3434
vm.selectedMapping = {
35-
customerField: "",
35+
contactField: "",
3636
formField: "",
3737
clear: function () {
38-
this.customerField = "";
38+
this.contactField = "";
3939
this.formField = "";
4040
}
4141
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
function customFieldMapperController($scope, notificationsService, activeCampaignService, umbracoFormsIntegrationsCrmActiveCampaignResource) {
2+
3+
var vm = this;
4+
5+
init();
6+
7+
vm.addMapping = function () {
8+
9+
if (vm.customFieldMappings.find(p => p.customField === vm.selectedMapping.customField)) {
10+
notificationsService.warning("Custom field is already mapped.");
11+
return;
12+
}
13+
14+
vm.customFieldMappings.push({
15+
customField: vm.customFields.find(p => p.id === vm.selectedMapping.customField),
16+
formField: vm.formFields.find(p => p.id === vm.selectedMapping.formField)
17+
});
18+
19+
$scope.setting.value = JSON.stringify(vm.customFieldMappings);
20+
21+
vm.selectedMapping.clear();
22+
}
23+
24+
vm.deleteMapping = function (index) {
25+
26+
vm.customFieldMappings.splice(index, 1);
27+
28+
$scope.setting.value = JSON.stringify(vm.customFieldMappings);
29+
}
30+
31+
function init() {
32+
vm.selectedMapping = {
33+
customField: "",
34+
formField: "",
35+
clear: function () {
36+
this.customField = "";
37+
this.formField = "";
38+
}
39+
};
40+
41+
vm.customFieldMappings = $scope.setting.value
42+
? JSON.parse($scope.setting.value)
43+
: [];
44+
45+
console.log(vm.customFieldMappings);
46+
47+
activeCampaignService.getFormFields(function (response) {
48+
vm.formFields = response;
49+
});
50+
51+
umbracoFormsIntegrationsCrmActiveCampaignResource.getCustomFields().then(function (response) {
52+
vm.customFields = response.fields;
53+
});
54+
}
55+
}
56+
57+
angular.module("umbraco")
58+
.controller("UmbracoForms.Integrations.Crm.ActiveCampaign.CustomFieldMapperController", customFieldMapperController);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<div ng-controller="UmbracoForms.Integrations.Crm.ActiveCampaign.CustomFieldMapperController as vm">
2+
<div>
3+
<select ng-model="vm.selectedMapping.customField">
4+
<option value="">Select custom field</option>
5+
<option ng-repeat="field in vm.customFields" value="{{ field.id }}">{{ field.title }}</option>
6+
</select>
7+
<select ng-model="vm.selectedMapping.formField">
8+
<option value="">Select form field</option>
9+
<option ng-repeat="field in vm.formFields" value="{{ field.id }}">{{ field.value }}</option>
10+
</select>
11+
</div>
12+
13+
<div class="mt2">
14+
<umb-button type="button" class="mt2"
15+
action="vm.addMapping()"
16+
label="Add mapping"
17+
disabled="vm.selectedMapping.customField.length === 0 || vm.selectedMapping.formField.length === 0">
18+
</umb-button>
19+
</div>
20+
21+
<div class="umb-forms-mappings mt2" ng-if="vm.customFieldMappings.length > 0">
22+
23+
<div class="umb-forms-mapping-header">
24+
<div class="umb-forms-mapping-field -no-margin-left">Custom Field</div>
25+
<div class="umb-forms-mapping-field">Form Field</div>
26+
<div class="umb-forms-mapping-remove -no-margin-right"></div>
27+
</div>
28+
29+
<div class="umb-forms-mapping" ng-repeat="mapping in vm.customFieldMappings">
30+
<div class="umb-forms-mapping-field -no-margin-left">{{ mapping.customField.title }}</div>
31+
<div class="umb-forms-mapping-field">{{ mapping.formField.value }}</div>
32+
<div class="umb-forms-mapping-remove -no-margin-right">
33+
<a href="" ng-click="vm.deleteMapping($index)">
34+
<i class="icon-trash"></i>
35+
</a>
36+
</div>
37+
</div>
38+
</div>
39+
40+
</div>

src/Umbraco.Forms.Integrations.Crm.ActiveCampaign/App_Plugins/UmbracoForms.Integrations/Crm/ActiveCampaign/package.manifest

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
{
22
"javascript": [
33
"~/App_Plugins/UmbracoForms.Integrations/Crm/ActiveCampaign/contact-mapper.controller.js",
4+
"~/App_Plugins/UmbracoForms.Integrations/Crm/ActiveCampaign/customfield-mapper.controller.js",
5+
"~/App_Plugins/UmbracoForms.Integrations/Crm/ActiveCampaign/accountpicker.controller.js",
46
"~/App_Plugins/UmbracoForms.Integrations/Crm/ActiveCampaign/activecampaign.resource.js",
57
"~/App_Plugins/UmbracoForms.Integrations/Crm/ActiveCampaign/activecampaign.service.js"
68
],

src/Umbraco.Forms.Integrations.Crm.ActiveCampaign/Constants.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ public class Constants
55
{
66
public const string WorkflowId = "5e8b110b-715f-4ba5-a384-86dba53a4d1b";
77

8-
public const string ContactsHttpClient = "ContactsClient";
9-
108
public const string SettingsPath = "Umbraco:Forms:Integrations:Crm:ActiveCampaign:Settings";
9+
10+
public const string HttpClient = "ActiveCampaignClient";
1111
}
1212
}

0 commit comments

Comments
 (0)