Eloqua is a tool maintained by Oracle that is used for Marketing Automatization. Eloqua helps B2B markets and organizations manage marketing campaigns and sales lead generation. Those organizations have huge websites that contains many contact forms. As, we talk enormous websites Sitecore is a great DXP (Digital Experience Platform) to build those kind of websites.
So, this article is going to talk about how to integrate Sitecore Forms with Eloqua Integration in order to facilitate the developers life. I created a project in Github, which contains the code that integrate Eloqua with Sitecore Forms.
2. Drag and Drop the form controls that you need it for your form.
When you dragged a control like Single-line text you will noticed that this control contains a section called Eloqua to turn on the Prefill feature if your requirements needed.
The important thing here is named the form same as Eloqua Form and the Single-line text’s control name match as Eloqua Form.
3. Add submit button
After drag and drop Submit Button, Add an action and select Submit To Eloqua.
4. Add Eloqua Tracking Scripts
You can add Eloqua Tracking scripts into your form by drag and drop Eloqua Tracking Scripts control. Then you can fill the Eloqua Form Name and the Eloqua Site Id
And that’s it, now you can place your form in any page. and this will submit the form to Eloqua.
In conclusion this is a basic tool that allows you to integrate your Sitecore Forms to Eloqua Forms without coding.
In this article, I will focus on a customization for Multi-list with Search. This scenario is not common. But sometimes you will need to customize a field in Sitecore. I will show you the steps that I followed to customize the Multi-List with Search.
Step 1: Prepare your component
I created a List View Component which contains multiple items.
Figure 1: List View Data source
This item will contains 2 fields:
Title – Single-Line Text
Description – Multi-Line Text
Figure 2: List Item
As we know, The Multi-List with Search by default shows the Item Name / Display Name, but as per client requirements we need to display and search using another field or even another item relation field.
Step 2: Create custom control
For this operation you can duplicate existing Multi-List with Search and name it “Custom Multilist with Search” or name that make sense to you.
Then, scroll down to Control Field and name something like “contentSpecificExtension:CustomMultilistWithSearch”
Step 3: Create custom Search.ashx Service and page load control class
Mutli-List with Search as you know it use Solr search to fetch the options. So in that case we will override search functionality to display the name as we required. Also we need to change the page load rendering to display the desired name.
Page Load Control Class
To create the class that will control the UI, we need to create a class named CustomMultilistWithSearch.cs
In this script we are going to override ScriptParameters and noticed that is pointing to a CustomSearch.ashx file. And OuputString here we can add the code to display the desired name.
Then Website\sitecore\shell\Applications\Buckets\Services create a file which specified the class Handler that we are going to use to override the Search Behavior.
Sometimes you will need to check programmatically if rendering is already added to a Presentation Details in Sitecore. In my case Content Authors doesn’t want to add a modal in renderings each time that they added another component to the view. Also, you can add multiple times that Widget rendering on this page, but the modal should be added once.
To provide a solution I added a helper I will show you the code that I used.
public static class RenderingHelper
{
public static bool HasRendering(string guid)
{
var refs = Sitecore.Context.Item.Visualization.GetRenderings(Context.Device, false);
if (!refs.Any())
return false;
var renderingReferences = refs.Where(r => r.RenderingID.ToString().Equals(guid, StringComparison.InvariantCultureIgnoreCase)).ToList();
if (renderingReferences.Any())
return true;
return false;
}
}
Then, when you implement the validation the view you can add something like this
@if (!RenderingHelper.HasRendering("{--YOUR RENDERING ID HERE--}"))
{
@Html.Sitecore().Rendering("{--YOUR RENDERING ID HERE--}", new
{
DataSource = "{--YOUR DATASOURCE ITEM ID HERE--}"
})
}
Many businesses require to implements GEO IP Services. Business Rules differ between locations. As Developers, Sitecore makes our lives easier by providing a Service that we can use to obtain the location of the website’s visitor.
The IP Geolocation provides information like country, state, city, and every visitor’s registered company name as well. Based on geodata, we can build modules that interact with our visitors and provide accurate information depending on where they are.
In your Siecore Solution ensure you add the following Nuget Packages:
Sitecore.Kernel
Sitecore.Analytics
Solution
Sometimes when you work with lower environments or your local environment Geo IP is not available. I developed the following simple class to get the visitor’s country and city. We can add the next file to setup a city and country code when Sitecore IP Geolocation is not available for your local. I used Sitecore 9.3.
The next class contains a method GetUserGeolocation to get user location information.
public class IPGeolocationService
{
/// <summary>
/// Get User IP form Request Context.
/// </summary>
/// <returns>(string) IP Address</returns>
private static string GetUserIP()
{
var ip = (HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"] != null
&& HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"] != "")
? HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"]
: HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"];
if (ip.Contains(","))
ip = ip.Split(',').First().Trim();
return ip.Trim();
}
/// <summary>
/// Fetch country from Sitecore IP Geolocation Service check
/// setup at https://doc.sitecore.com/developers/93/sitecore-experience-manager/en/set-up-sitecore-ip-geolocation.html
/// </summary>
/// <returns></returns>
public static GeoData GetUserGeolocation()
{
GeoData geoData;
bool IsDevEnviroment = Settings.GetBoolSetting("Dev.IPGeolocation.IsDevMode", false);
if (!IsDevEnviroment)
{
var millisecondsTimeout = 2000;
try
{
var options = new GeoIpOptions(IPAddress.Parse(GetUserIP()), millisecondsTimeout);
var result = GeoIpManager.GetGeoIpData(options);
if (result.ResolveState == GeoIpResolveState.Resolved &&
result.WhoIsInformation != null)
{
geoData = new GeoData()
{
City = result.WhoIsInformation.City,
CountryCode = result.WhoIsInformation.Country
};
}
else
{
throw new Exception("GeoIP whoIsInformation is null");
}
}
catch (Exception e)
{
Log.Error("Error Getting IPAddress",e, typeof(IPGeolocationService));
if (Tracker.Current != null && Tracker.Current.Interaction != null && Tracker.Current.Interaction.GeoData != null)
{
geoData = new GeoData()
{
City = Tracker.Current.Interaction?.GeoData?.City,
CountryCode = Tracker.Current.Interaction?.GeoData?.Country
};
}
else
{
Log.Error("IPGeolocation: There is an issue with IP Geolocation Service", typeof(IPGeolocationService));
geoData = new GeoData()
{
City = null,
CountryCode = Settings.GetSetting("LocationEngine.IPGeolocation.CountryCode")
};
}
}
}
else
{
Log.Info("IPGeolocation: Dev Mode is enabled", typeof(IPGeolocationService));
geoData = new GeoData()
{
City = Settings.GetSetting("Dev.IPGeolocation.City"),
CountryCode = Settings.GetSetting("Dev.IPGeolocation.CountryCode")
};
}
Log.Info($"IPGeolocation: Country Code is {geoData.CountryCode}", typeof(IPGeolocationService));
return geoData;
}
}
Finally, GeoData is a model class that contains the location information.
public class GeoData
{
public string CountryCode { get; set; }
public string City { get; set; }
}
I hope this information will be usefully when you work with Sitecore Geo IP Services.
In this blog post I will talk about how to integrate APIs with Sitecore using Swagger.
What is Swagger?
Swagger is an open-source tool that uses Open API Specification, the initiative was started in 2015 by Linux Foundation. In my opinion, this tool saves a lot of time, it will support your development lifecycle starting with Design, Building, and Documentation.
In the following example, we can appreciate that /store/inventory is a GET http method which is part of "store" section. The definition is clear and easy to understand. You can read what is the possible responses as 200 code that responds an object (Dictionary<string,int>).
Once you design the definition you are ready to go to the next phase.
Build Phase:
Swagger provides Swagger CodeGen to generate any Client or Server code base, this provides the interfaces where you can implement to add the API’s business logic.
Swagger editor also provide you a tab in the interface to generate this client / server code. This blog post will show you how to implement Swagger Client API with an existing Sitecore MVC Solution. In order to generate the client you will need to select from Generate client tab -> csharp.
It will generate a C# Project. That contains the following folder structure:
API: Contains all Entity Interfaces, in our previous example, we had an operation GetInventory which is part of the store group. Swagger will generate a class per group, Example: StoreApi
Client: The folder contains all classes to build the client. Basically, you will find classes like ApiCall, Configuration, Exceptions.
Model: This is the model folder that contains all entities used in your APIs, like Pet, ApiResponse, Catalog, Product, etc.
Move API, Client and Model folder to your Sitecore Solution Project.
Prepare your solution
Install the following NuGet packages on your solution:
RestSharp
Newtonsoft.Json
System.ComponentModel
System.ComponentModel.Annotations
Sitecore.Kernel
Configure your solution
After installing all packages that are required and moved the files into the solution edit Client\Configuration.cs class in order to setup the Base URL.
/// <summary>
/// Initializes a new instance of the <see cref="Configuration" /> class
/// </summary>
public Configuration()
{
UserAgent = "Swagger-Codegen/1.0.0/csharp";
BasePath = Sitecore.Configuration.Settings.GetSetting("PetStoreConnector.BaseUrl");
DefaultHeader = new ConcurrentDictionary<string, string>();
ApiKey = new ConcurrentDictionary<string, string>();
ApiKeyPrefix = new ConcurrentDictionary<string, string>();
// Setting Timeout has side effects (forces ApiClient creation).
Timeout = 100000;
}
Change the constructor class to include Sitecore.Configuration.Settings.GetSetting("PetStoreConnector.BaseUrl") and create a config file on Sitecore like PetStoreConnector.config
In your Controller you can add some code to call the API´s endpoints, here there are an example:
public class InventoryController : Controller
{
public ActionResult Index()
{
IStoreApi api = new StoreApi();
var inventory = api.GetInventory();
return Json(inventory, JsonRequestBehavior.AllowGet);
}
}
Finally build your solution and deploy your changes and you are ready to consume your API on Sitecore using Swagger.
This approach could reduce your development time, you can easily integrate your logic with another system building a robust C# client solution.
I had the opportunity to talk in Sitecore User Group Ecuador Session 15 in the following YouTube link you can watch the session (in Spanish).
In C:\Solr\solr-8.4.0\server\etc add the following Powershell script and save it as solrssl.ps1:
param(
[string]$KeystoreFile = 'solr-ssl.keystore.jks',
[string]$KeystorePassword = 'secret',
[string]$SolrDomain = 'localhost',
[switch]$Clobber
)
$ErrorActionPreference = 'Stop'
### PARAM VALIDATION
if($KeystorePassword -ne 'secret') {
Write-Error 'The keystore password must be "secret", because Solr apparently ignores the parameter'
}
if((Test-Path $KeystoreFile)) {
if($Clobber) {
Write-Host "Removing $KeystoreFile..."
Remove-Item $KeystoreFile
} else {
$KeystorePath = Resolve-Path $KeystoreFile
Write-Error "Keystore file $KeystorePath already existed. To regenerate it, pass -Clobber."
}
}
$P12Path = [IO.Path]::ChangeExtension($KeystoreFile, 'p12')
if((Test-Path $P12Path)) {
if($Clobber) {
Write-Host "Removing $P12Path..."
Remove-Item $P12Path
} else {
$P12Path = Resolve-Path $P12Path
Write-Error "Keystore file $P12Path already existed. To regenerate it, pass -Clobber."
}
}
try {
$keytool = (Get-Command 'C:\Program Files\Java\jdk1.8.0_261\bin\keytool.exe').Source
} catch {
$keytool = Read-Host "keytool.exe not on path. Enter path to keytool (found in JRE bin folder)"
if([string]::IsNullOrEmpty($keytool) -or -not (Test-Path $keytool)) {
Write-Error "Keytool path was invalid."
}
}
### DOING STUFF
Write-Host ''
Write-Host 'Generating JKS keystore...'
& $keytool -genkeypair -alias solr-ssl -keyalg RSA -keysize 2048 -keypass $KeystorePassword -storepass $KeystorePassword -validity 9999 -keystore $KeystoreFile -ext SAN=DNS:$SolrDomain,IP:127.0.0.1 -dname "CN=$SolrDomain, OU=Enginnering, O=Verndale, L=Quito, ST=Pichincha, C=EC"
Write-Host ''
Write-Host 'Generating .p12 to import to Windows...'
& $keytool -importkeystore -srckeystore $KeystoreFile -destkeystore $P12Path -srcstoretype jks -deststoretype pkcs12 -srcstorepass $KeystorePassword -deststorepass $KeystorePassword
Write-Host ''
Write-Host 'Trusting generated SSL certificate...'
$secureStringKeystorePassword = ConvertTo-SecureString -String $KeystorePassword -Force -AsPlainText
$root = Import-PfxCertificate -FilePath $P12Path -Password $secureStringKeystorePassword -CertStoreLocation Cert:\LocalMachine\Root
Write-Host 'SSL certificate is now locally trusted. (added as root CA)'
Write-Host ''
Write-Host '########## NEXT STEPS ##########' -ForegroundColor Green
Write-Host ''
Write-Host '1. Copy your keystore to $SOLR_HOME\server\etc (MUST be here)' -ForegroundColor Green
if(-not $KeystoreFile.EndsWith('solr-ssl.keystore.jks')) {
Write-Warning 'Your keystore file is not named "solr-ssl.keystore.jks"'
Write-Warning 'Solr requires this exact name, so make sure to rename it before use.'
}
$KeystorePath = Resolve-Path $KeystoreFile
Write-Host ''
Write-Host '2. Add the following lines to your solr.in.cmd:' -ForegroundColor Green
Write-Host ''
Write-Host "set SOLR_SSL_KEY_STORE=etc/solr-ssl.keystore.jks" -ForegroundColor Yellow
Write-Host "set SOLR_SSL_KEY_STORE_PASSWORD=$KeystorePassword" -ForegroundColor Yellow
Write-Host "set SOLR_SSL_TRUST_STORE=etc/solr-ssl.keystore.jks" -ForegroundColor Yellow
Write-Host "set SOLR_SSL_TRUST_STORE_PASSWORD=$KeystorePassword" -ForegroundColor Yellow
Write-Host ''
Write-Host 'Done!'
You need to replace C:\Program Files\Java\jdk1.8.0_261\bin\keytool.exe path that match your installed Java JDK.
Then you need to run the following powershell script that is located at C:\Solr\solr-8.4.0\server\etc
.\solrssl.ps1
When execution is done, you will see solr-ssl.keystore.jks and solr-ssl.keystore.p12 files generated.
Edit C:\Solr\solr-8.4.0\bin\solr.in.cmd file and uncomment the following lines:
set SOLR_SSL_KEY_STORE=etc/solr-ssl.keystore.jks
set SOLR_SSL_KEY_STORE_PASSWORD=secret
set SOLR_SSL_TRUST_STORE=etc/solr-ssl.keystore.jks
set SOLR_SSL_TRUST_STORE_PASSWORD=secret
set SOLR_SSL_KEY_STORE_TYPE=JKS
set SOLR_SSL_TRUST_STORE_TYPE=JKS
I started at Verndale in December 2019. In my job interview, I mentioned that I didn’t have much experience in C# .NET MVC, because I worked in many projects in PHP with Laravel which is an MVC Framework, it’s not the same but… dude concepts are concepts. To avoid making it long, I talked about my amazing experiences in my previous company Umpacto Soluciones and all international competitions that I participated in during my university career. Including competitions in Bogotá, Shanghai, Abu Dhabi, and many others in my natal city Quito.
In a few words, I considered that I love design patterns and programming concepts. In my personal opinion, this is why I see that programming is like an art, you could express all your ideas through your designs and code. Another important thing to mention is that I love challenges.
Verndale is a company that creates customer experiences, so I’m very happy to be part of this company. In my onboarding process, I learn many things about Verndale, and my first steps at Verndale was learning a new Platform called Sitecore.
Sitecore is an Experience Digital Platform (XDP), which includes powerful features like Personalization, Marketing Automatization, A/B Testing.
I got my Sitecore Developer Certification in my first month at the company and I love it. I love the way to do modules, the simplicity. The modules that I develop in Sitecore are semantic and clean.
Another important thing to mention is that I learned about Sitecore Content Hub, this is a Digital Asset Management (DAM), Content Marketing Platform (CMP), Marketing Resource Management (MRM). In my personal opinion I see this out of this world, Can you image manage all your digital assets in one platform?, and all images are automatically tagged by Azure Cognitive Services, and also integrated with Photoshop, Sitecore, One Drive, and many other services. So I recommended this platform for all companies that need to handle huge digital assets.
Finally, I could say that I’m happy in what I do. I’m going to post more about my experiences and troubleshooting in those technologies. Happy coding.
We use cookies on our website to give you the most relevant experience by remembering your preferences and repeat visits. By clicking “Accept”, you consent to the use of ALL the cookies.
This website uses cookies to improve your experience while you navigate through the website. Out of these cookies, the cookies that are categorized as necessary are stored on your browser as they are essential for the working of basic functionalities of the website. We also use third-party cookies that help us analyze and understand how you use this website. These cookies will be stored in your browser only with your consent. You also have the option to opt-out of these cookies. But opting out of some of these cookies may have an effect on your browsing experience.
Necessary cookies are absolutely essential for the website to function properly. This category only includes cookies that ensures basic functionalities and security features of the website. These cookies do not store any personal information.
Any cookies that may not be particularly necessary for the website to function and is used specifically to collect user personal data via analytics, ads, other embedded contents are termed as non-necessary cookies. It is mandatory to procure user consent prior to running these cookies on your website.