Skip to content

Commit eb7f220

Browse files
committed
Algolia Search Engine integration
1 parent 72a3ace commit eb7f220

28 files changed

+1168
-1
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using Algolia.Search.Models.Search;
2+
3+
using Microsoft.Extensions.DependencyInjection;
4+
5+
using System.Dynamic;
6+
7+
using Umbraco.Cms.Core.Composing;
8+
using Umbraco.Cms.Core.DependencyInjection;
9+
using Umbraco.Cms.Core.Notifications;
10+
using Umbraco.Cms.Integrations.Search.Algolia;
11+
using Umbraco.Cms.Integrations.Search.Algolia.Configuration;
12+
using Umbraco.Cms.Integrations.Search.Algolia.Migrations;
13+
using Umbraco.Cms.Integrations.Search.Algolia.Models;
14+
using Umbraco.Cms.Integrations.Search.Algolia.Notifications;
15+
using Umbraco.Cms.Integrations.Search.Algolia.Services;
16+
17+
namespace Umbraco.Cms.Integrations.Crm.ActiveCampaign
18+
{
19+
public class AlgoliaComposer : IComposer
20+
{
21+
public void Compose(IUmbracoBuilder builder)
22+
{
23+
builder.AddNotificationHandler<UmbracoApplicationStartingNotification, RunAlgoliaIndicesMigration>();
24+
25+
var options = builder.Services.AddOptions<AlgoliaSettings>()
26+
.Bind(builder.Config.GetSection(Constants.SettingsPath));
27+
28+
builder.Services.AddSingleton<IAlgoliaIndexService, AlgoliaIndexService>();
29+
30+
builder.Services.AddSingleton<IAlgoliaSearchService<SearchResponse<Record>>, AlgoliaSearchService>();
31+
32+
builder.Services.AddScoped<IScopeService<AlgoliaIndex>, ScopeService>();
33+
34+
builder.AddNotificationHandler<ContentPublishedNotification, NewContentPublishedHandler>();
35+
}
36+
37+
}
38+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using Umbraco.Cms.Core.Composing;
2+
using Umbraco.Cms.Core.Dashboards;
3+
4+
namespace Umbraco.Cms.Integrations.Search.Algolia
5+
{
6+
[Weight(100)]
7+
public class AlgoliaDashboard : IDashboard
8+
{
9+
public string[] Sections => new[] { Umbraco.Cms.Core.Constants.Applications.Settings };
10+
11+
public IAccessRule[] AccessRules => Array.Empty<IAccessRule>();
12+
13+
public string Alias => "algoliaSearchManagement";
14+
15+
public string View => "/App_Plugins/UmbracoCms.Integrations/Search/Algolia/views/dashboard.html";
16+
}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
2+
<language>
3+
<area alias="dashboardTabs">
4+
<key alias="algoliaSearchManagement">Algolia Search Management</key>
5+
</area>
6+
<area alias="algolia">
7+
<key alias="pushData">Push Data</key>
8+
</area>
9+
</language>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
angular.module('umbraco.resources').factory('umbracoCmsIntegrationsSearchAlgoliaResource',
2+
function ($http, umbRequestHelper) {
3+
4+
const apiEndpoint = "backoffice/UmbracoCmsIntegrationsSearchAlgolia/Search";
5+
6+
return {
7+
getIndices: function () {
8+
return umbRequestHelper.resourcePromise(
9+
$http.get(`${apiEndpoint}/GetIndices`),
10+
"Failed");
11+
},
12+
saveIndex: function (name, contentData) {
13+
return umbRequestHelper.resourcePromise(
14+
$http.post(`${apiEndpoint}/SaveIndex`, { name: name, contentData: contentData }),
15+
"Failed");
16+
},
17+
deleteIndex: function (id) {
18+
return umbRequestHelper.resourcePromise(
19+
$http.delete(`${apiEndpoint}/DeleteIndex?id=${id}`),
20+
"Failed");
21+
},
22+
search: function (indexId, query) {
23+
return umbRequestHelper.resourcePromise(
24+
$http.get(`${apiEndpoint}/Search?indexId=${indexId}&query=${query}`),
25+
"Failed");
26+
}
27+
};
28+
}
29+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
function algoliaService(contentTypeResource) {
2+
return {
3+
getContentTypes: function (callback) {
4+
contentTypeResource.getAll().then(function (data) {
5+
callback(data.map((item) => { return { id: item.id, alias: item.alias, name: item.name, checked: false } }));
6+
});
7+
},
8+
getPropertiesByContentTypeId: function (contentTypeId, callback) {
9+
contentTypeResource.getById(contentTypeId).then(function (data) {
10+
var properties = [];
11+
12+
for (var i = 0; i < data.groups.length; i++) {
13+
for (var j = 0; j < data.groups[i].properties.length; j++) {
14+
properties.push({
15+
id: data.groups[i].properties[j].id,
16+
alias: data.groups[i].properties[j].alias,
17+
name: data.groups[i].properties[j].label,
18+
checked: false
19+
});
20+
}
21+
}
22+
23+
callback(properties);
24+
});
25+
}
26+
}
27+
}
28+
29+
angular.module("umbraco.services")
30+
.factory("algoliaService", algoliaService);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
function dashboardController(notificationsService, algoliaService, umbracoCmsIntegrationsSearchAlgoliaResource) {
2+
var vm = this;
3+
4+
vm.searchQuery = "";
5+
vm.searchIndex = {};
6+
vm.searchResults = {};
7+
8+
vm.viewState = "list";
9+
10+
init();
11+
12+
vm.addIndex = addIndex;
13+
vm.saveIndex = saveIndex;
14+
vm.viewIndex = viewIndex;
15+
vm.deleteIndex = deleteIndex;
16+
vm.search = search;
17+
18+
function init() {
19+
20+
// contentData property:
21+
// array of objects:
22+
// contentType -> string value
23+
// properties -> string array
24+
vm.manageIndex = {
25+
id: 0,
26+
name: "",
27+
selectedContentType: "",
28+
contentTypes: [],
29+
properties: [],
30+
contentData: [],
31+
selectContentType: function (contentType) {
32+
33+
this.properties = [];
34+
35+
var checked = !contentType.checked;
36+
37+
if (checked) {
38+
39+
this.selectedContentType = contentType.alias;
40+
this.contentTypes.find(p => p.alias == contentType.alias).checked = true;
41+
42+
var contentItem = {
43+
contentType: contentType.alias,
44+
properties: []
45+
};
46+
47+
this.contentData.push(contentItem);
48+
}
49+
else {
50+
this.contentTypes.find(p => p.alias == contentType.alias).checked = false;
51+
52+
const contentTypeIndex = this.contentData.findIndex((obj) => obj.contentType === contentType.alias);
53+
54+
if (contentTypeIndex > -1) this.contentData.splice(contentTypeIndex, 1);
55+
56+
this.selectedContentType = "";
57+
58+
this.properties = [];
59+
}
60+
},
61+
showProperties: function (contentType) {
62+
63+
algoliaService.getPropertiesByContentTypeId(contentType.id, (response) => {
64+
vm.manageIndex.properties = response;
65+
66+
var contentTypeData = this.contentData.find(p => p.contentType == contentType.alias);
67+
if (contentTypeData && contentTypeData.properties.length > 0) {
68+
vm.manageIndex.properties = vm.manageIndex.properties.map((obj) => {
69+
if (contentTypeData.properties.find(p => p == obj.alias)) {
70+
obj.checked = true;
71+
}
72+
73+
return obj;
74+
});
75+
}
76+
});
77+
},
78+
selectProperty: function (property) {
79+
var checked = !property.checked;
80+
81+
if (this.contentData.length == 0 || this.contentData.find(p => p.contentType === this.selectedContentType) === undefined) {
82+
notificationsService.warning("Please select the property matching content type.");
83+
return false;
84+
}
85+
86+
if (checked) {
87+
this.properties.find(p => p.alias == property.alias).checked = true;
88+
this.contentData.find(p => p.contentType === this.selectedContentType).properties.push(property.alias);
89+
}
90+
else {
91+
const propertyIndex = this.contentData.find(p => p.contentType === this.selectedContentType).properties.indexOf(property.alias);
92+
if (propertyIndex > -1) this.contentData.find(p => p.contentType === this.selectedContentType).properties.splice(propertyIndex, 1);
93+
}
94+
},
95+
reset: function () {
96+
this.visible = false;
97+
this.name = "";
98+
this.selectedContentType = "";
99+
this.contentTypes = [];
100+
this.properties = [];
101+
this.contentData = [];
102+
}
103+
};
104+
105+
algoliaService.getContentTypes((response) => vm.manageIndex.contentTypes = response);
106+
107+
getIndices();
108+
}
109+
110+
function addIndex() {
111+
vm.viewState = "manage";
112+
}
113+
114+
function saveIndex() {
115+
116+
if (vm.manageIndex.name.length == 0 || vm.manageIndex.contentData.length == 0) {
117+
notificationsService.error("Index name and content schema are required");
118+
return false;
119+
}
120+
umbracoCmsIntegrationsSearchAlgoliaResource
121+
.saveIndex(vm.manageIndex.name, vm.manageIndex.contentData)
122+
.then(function (response) {
123+
if (response.length == 0) {
124+
vm.manageIndex.reset();
125+
algoliaService.getContentTypes((response) => vm.manageIndex.contentTypes = response);
126+
} else {
127+
notificationsService.error(response);
128+
}
129+
130+
vm.viewState = "list";
131+
132+
getIndices();
133+
});
134+
}
135+
136+
function viewIndex(index) {
137+
138+
vm.viewState = "manage";
139+
140+
vm.manageIndex.id = index.id;
141+
vm.manageIndex.name = index.name;
142+
vm.manageIndex.contentData = index.contentData;
143+
144+
algoliaService.getContentTypes((response) => {
145+
146+
vm.manageIndex.contentTypes = response;
147+
148+
for (var i = 0; i < vm.manageIndex.contentData.length; i++) {
149+
vm.manageIndex.contentTypes.find(p => p.alias === vm.manageIndex.contentData[i].contentType).checked = true;
150+
}
151+
});
152+
}
153+
154+
function deleteIndex(index) {
155+
umbracoCmsIntegrationsSearchAlgoliaResource.deleteIndex(index.id).then(function (response) {
156+
getIndices();
157+
});
158+
}
159+
160+
function search() {
161+
umbracoCmsIntegrationsSearchAlgoliaResource.search(vm.searchIndex.id, vm.searchQuery).then(function (response) {
162+
vm.searchResults = response;
163+
});
164+
}
165+
166+
function getIndices() {
167+
vm.indices = [];
168+
169+
umbracoCmsIntegrationsSearchAlgoliaResource.getIndices().then(function (data) {
170+
vm.indices = data;
171+
});
172+
}
173+
}
174+
175+
angular.module("umbraco")
176+
.controller("Umbraco.Cms.Integrations.Search.Algolia.DashboardController", dashboardController);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"name": "Umbraco.Cms.Integrations.Search.Algolia",
3+
"version": "1.0.0",
4+
"allowPackageTelemetry": true,
5+
"javascript": [
6+
"~/App_Plugins/UmbracoCms.Integrations/Search/Algolia/js/algolia.service.js",
7+
"~/App_Plugins/UmbracoCms.Integrations/Search/Algolia/js/dashboard.controller.js",
8+
"~/App_Plugins/UmbracoCms.Integrations/Search/Algolia/js/algolia.resource.js"
9+
],
10+
"css": []
11+
}

0 commit comments

Comments
 (0)