In this post, we will see a custom component example to integrate Eloqua with Oracle Sites Cloud Service.

Oracle Eloqua is a Marketing Automation Tool to manage data and customer experiences. In this example, we are going to integrate in Sites Cloud Service an Eloqua Form generated by a marketer with all the specific processing rules defined in the Form, so we will be added to an specific campaign and some actions are going to be performed once the Eloqua component is included in a SCS page and a visitor submits the form.

We will use the same Bootstrap theme that we have created in the previous post (Modern Business).

Same as we did for the custom slider, first step is go to Components section and create a new Local Component:

eloquaform

Once the component is created and synchronized with our computer, we can see the default component structure in our favorite editor:

eloquaformatom

Let’s start by the component definition, so let’s open the appinfo.json file and modify it to reflect our specific needs (we don’t need nested components, neither default custom settings value, so let’s keep it pretty much clean):

{
  "id": "eloqua-form-component",
  "settingsData": {
    "settingsHeight": 50,
    "settingsWidth": 300,
    "settingsRenderOption": "inline"
  }
}

 

Next step is customize the settings panel. Same as we did with the component definition, we don’t need most part of the generated code, as we only will have a dropdown that will list the available Eloqua Forms and we will only store the Form Id in our custom settings. The only interesting thing in the below code, is the ajax call to retrieve the forms in Eloqua and populate the dropdown with the different values (using name and id keys)

<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

<title>Eloqua Form settings</title>

<!-- include sample apps styling -->
							<link href="/_sitescloud/renderer/app/sdk/css/app-styles.css" rel="stylesheet">

<!-- include supporting files -->
<script type="text/javascript" src="/_sitescloud/renderer/app/apps/js/knockout.min.js"></script>
    <script type="text/javascript" src="/_sitescloud/renderer/app/apps/js/jquery.min.js"></script>

<!-- include the Sites SDK -->
<script type="text/javascript" src="/_sitescloud/renderer/app/sdk/js/sites.min.js"></script>
</head>
<body data-bind="visible: true" style="display:none; margin:0px; padding:0px;background:transparent;background-image:none;">
    <!-- ko if: initialized() -->


<div class="scs-component-settings">


<div>
      <!-- Layout -->
            <label id="formIdLabel" for="formId" class="settings-heading" data-bind="text: 'Eloqua Form'"></label>
      <select class="settings-select" id="formId" data-bind="typeahead: names, options: forms,
                       optionsText: 'name',
                       optionsValue: 'id',
                       value: formId,
                       optionsCaption: 'Choose...',
                       dropdown: {}">
            </select>
</div>


</div>



<div data-bind="setSettingsHeight: true"></div>


    <!-- /ko -->
    <!-- ko ifnot: initialized() -->


<div data-bind="text: 'waiting for initialization to complete'"></div>


    <!-- /ko -->
    <script type="text/javascript">

        // set the iFrame height when we've fully rendered
        ko.bindingHandlers.scsCompComponentImpl = {
            init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
                var body = document.body,
                    html = document.documentElement;

                SitesSDK.setHeight(Math.max(
                    body.scrollHeight,
                    body.offsetHeight,
                    html.clientHeight,
                    html.scrollHeight,
                    html.offsetHeight));
            }
        };

        // define the viewModel object
        var SettingsViewModel = function () {
            var self = this;

      self.formId = ko.observable();
            self.forms = ko.observableArray();

            var Form = function(name, id) {
          this.name = name;
          this.id = id;
      };

            // create rest of viewModel
            self.initialized = ko.observable(false);
            self.saveData = false;

            // Get custom settings
            SitesSDK.getProperty('customSettingsData', function (data) {
                //update observable
        self.formId(data.formId);
                self.initialized(true);
                self.saveData = true;
            });

            // save whenever any updates occur
            self.save = ko.computed(function () {
                var saveconfig = {
          'formId': self.formId()
                };

                // save data in page
                if (self.saveData) {
                    SitesSDK.setProperty('customSettingsData', saveconfig);
                }
            }, self);

            $.ajax({
                    url: "https://secure.p01.eloqua.com/API/REST/1.0/assets/forms?count=1000&orderBy=name%20ASC&depth=minimal",
                    dataType: 'json',
                    beforeSend: function (xhr) {
                            xhr.setRequestHeader('Authorization', '<strong>YOUR_AUTHORIZATION</strong>');
                    },
                    type: "GET",
                    async:false,
                    success: function(data,status) {
                            var elements = data.elements;
                            $.each(elements, function(key, element) {
                                self.forms.push(new Form(element.name,element.id));
                            });
                    },
                    error: function (text,status) { console.log('error ' + status) }
                    //complete: function(text,status) { alert('complete'); }
            });

        };

        // apply the bindings
        ko.applyBindings(new SettingsViewModel());
    </script>
</body>
</html>

 

Now we can do a quick test and include our custom component in a SCS page to see if settings panel is working as expected. This is my result:

eloquaformsettingscors

No forms are included in the dropdown…let’s have a look at the browser console:

corserror

We have faced a typical issue when developing in client-side mode, CORS problem. There are different possibilities to solve this issue, the first one, if we are the owners of the target system (Eloqua in this case), we can try to enable CORS for our specific SCS domain. If this is not possible or the target system has not this capability enabled, we can deploy a simple node.js application in some server we may have available that will act as a proxy, but adding the specific headers that are needed.

In my example as this is just in demo mode, I will opt for a easier way that is a browser extension that intercepts the response and add the appropriate header to avoid the CORS issue. I’m using Chrome and the extension is called “Allow-Control-Allow-Origin: *”, but there are others doing the same and there are also similar extensions for Firefox

chromeextension

Once we have our browser extension installed and configured to intercept only Eloqua calls (we don’t want to disturb other browsers calls, like the ones happening between Documents Cloud and the conversations component), we re-open our custom component settings panel and we can see that now we have the list of Eloqua Forms.

eloquaformsettings

We can test how the form selected is saved and if we reopen the settings, the dropdown selection is our selected form.

The next step, is the render part, which in this example is really easy as we just need to get the form id stored in the custom settings and make a REST call to Eloqua API and retrieve the form HTML to be displayed in the component (again the most interesting part is how to retrieve the Eloqua object from a REST call). Your final render.js file should looks like this:


/* globals define */
define(['knockout', 'jquery'], function (ko, $) {
'use strict';
// ----------------------------------------------
// Define a Knockout Template for your component
// ----------------------------------------------
var sampleComponentTemplate = '<!-- ko if: initialized -->' +
'<!-- ko if: htmlForm -->' +
'
<div data-bind="html: htmlForm">' +
'</div>
' +
'<!-- /ko -->' +
'<!-- /ko -->';

// ----------------------------------------------
// Define a Knockout ViewModel for your template
// ----------------------------------------------
var SampleComponentViewModel = function (args) {
var self = this,
SitesSDK = args.SitesSDK;

// store the args
self.mode = args.viewMode;
self.id = args.id;

// create the observables
self.formId = ko.observable();
self.htmlForm = ko.observable();

// handle initialization
self.customSettingsDataInitialized = ko.observable(false);
self.initialized = ko.computed(function () {
return self.customSettingsDataInitialized();
}, self);

//
// Handle property changes
//

self.updateCustomSettingsData = $.proxy(function (customData) {
self.formId(customData && customData.formId);
if(customData && customData.formId){
$.ajaxSetup({
headers : {
'Authorization' : '<strong>YOUR_AUTHORIZATION</strong>'
}
});
$.getJSON("https://secure.p01.eloqua.com/API/REST/1.0/assets/form/"+self.formId(),function(data) {
self.htmlForm(data.html);
}
);
}
self.customSettingsDataInitialized(true);
}, self);
self.updateSettings = function (settings) {
if (settings.property === 'customSettingsData') {
self.updateCustomSettingsData(settings.value);
}
};

// listen for the EXECUTE ACTION request to handle custom actions
SitesSDK.subscribe(SitesSDK.MESSAGE_TYPES.EXECUTE_ACTION, $.proxy(self.executeActionsListener, self));
// listen for settings update
SitesSDK.subscribe(SitesSDK.MESSAGE_TYPES.SETTINGS_UPDATED, $.proxy(self.updateSettings, self));

//
// Initialize customSettingsData values
//
SitesSDK.getProperty('customSettingsData', self.updateCustomSettingsData);
};

// ----------------------------------------------
// Create a knockout based component implemention
// ----------------------------------------------
var SampleComponentImpl = function (args) {
// Initialze the custom component
this.init(args);
};
// initialize all the values within the component from the given argument values
SampleComponentImpl.prototype.init = function (args) {
this.createViewModel(args);
this.createTemplate(args);
this.setupCallbacks();
};
// create the viewModel from the initial values
SampleComponentImpl.prototype.createViewModel = function (args) {
// create the viewModel
this.viewModel = new SampleComponentViewModel(args);
};
// create the template based on the initial values
SampleComponentImpl.prototype.createTemplate = function (args) {
// create a unique ID for the div to add, this will be passed to the callback
this.contentId = args.id + '_content_' + args.mode;
// create a hidden custom component template that can be added to the DOM
this.template = '
<div id="' + this.contentId + '">' +
sampleComponentTemplate +
'</div>
';
};
//
// SDK Callbacks
// setup the callbacks expected by the SDK API
//
SampleComponentImpl.prototype.setupCallbacks = function () {
//
// callback - render: add the component into the page
//
this.render = $.proxy(function (container) {
var $container = $(container);
// add the custom component template to the DOM
$container.append(this.template);
// apply the bindings
ko.applyBindings(this.viewModel, $('#' + this.contentId)[0]);
}, this);
//
// callback - update: handle property change event
//
this.update = $.proxy(function (args) {
var self = this;
// deal with each property changed
$.each(args.properties, function (index, property) {
if (property) {
if (property.name === 'customSettingsData') {
self.viewModel.updateComponentData(property.value);
}
}
});
}, this);
//
// callback - dispose: cleanup after component when it is removed from the page
//
this.dispose = $.proxy(function () {
// nothing required for this sample since knockout disposal will automatically clean up the node
}, this);
};
// ----------------------------------------------
// Create the factory object for your component
// ----------------------------------------------
var sampleComponentFactory = {
createComponent: function (args, callback) {
// return a new instance of the component
return callback(new SampleComponentImpl(args));
}
};
return sampleComponentFactory;
});

Let’s test if the form appears when we select it from the settings panel:

eloquaformworking

If we save and preview our site in a new browser window, we can interact with the form. In my case I’ve defined the form with three processing actions: update customer profile, send an specific email and then redirect the visitor to a specific page. I’ve created a hidden SCS page that is not visible in the navigation menu and this is the page URL that I’ve defined in Eloqua to redirect after form submission, but each flow can be completely different depending on your specific use case.

 

eloquaformfilled
Form Submission
eloquaredirection
Redirected to SCS hidden page
eloquaemail
Email received

The most useful learning of this example is how an Eloqua marketer can define specific campaigns and forms to be included in webpages and how a Sites Cloud Service contributor, can easily access to the Eloqua assets from a custom component and including the Eloqua object in a SCS page without a single line of code, just selecting the desired Form from the settings panel.

Advertisements

2 thoughts on “Building a custom Eloqua component with Sites Cloud Service

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s