Category: JavaScript Extension

SIRET / Credit Card Checking in Intelligent Advisor with Luhn

This post was originally going to be called SIRET checking in Intelligent Advisor but the notion of a SIRET number is an acronym famous I think only in France. What we are really talking about is validating anything that uses the Luhn Algorithm (aka Modulus 10 algorithm). There are so many uses, most of them well known and some surprising (customer survey codes at McDonalds, who knew!).

This algorithm is, according to a very well-known site:

From Wikipedia

“used to validate a variety of identification numbers, such as credit card numbers, IMEI numbers, National Provider Identifier numbers in the United States, Canadian Social Insurance Numbers, Israeli ID Numbers, South African ID Numbers, Greek Social Security Numbers (ΑΜΚΑ), and survey codes appearing on McDonald’s, Taco Bell, and Tractor Supply Co. receipts. It is described in U.S. Patent No. 2,950,048, filed on January 6, 1954, and granted on August 23, 1960.

The algorithm is in the public domain and is in wide use today. It is specified in ISO/IEC 7812-1.[1] It is not intended to be a cryptographically secure hash function; it was designed to protect against accidental errors, not malicious attacks. Most credit cards and many government identification numbers use the algorithm as a simple method of distinguishing valid numbers from mistyped or otherwise incorrect numbers.”

So it’s not surprising that Luhn Validation came up sooner rather than later in an Intelligent Advisor project. The implementation is possible in two ways I suppose : firstly, if you are already using RuleScript in the project (which you should not be doing for new projects, but maybe it is an existing one) then it is a really simple task to find a decent implementation of the algorithm in JavaScript and adapt it for your Project. Other algorithms that are more complex can be quite easily implemented that way (sigh, such a pity that RuleScript is no longer part of the future of OPA).

The second way to do it is to use a JavaScript extension for your Input Control and let the Validate key do the work. I suppose there may be a third approach, to try and implement the algorithm in natural language but I doubt it is possible or practical.

Anyway how could it be done. Simple, first get a decent implementation of the Luhn algorithm : for example this one is super fast. Then build out a standard Input Control Extension. Here is a very simple example with some comments below the code:

/*  Generated by the OPA Hub Website 15/09/2020 13:47
Generated Example of Custom Input Extension for Oracle Policy Automation
 I will remember this is for demonstration purposes only. 
*/
OraclePolicyAutomation.AddExtension({
    customInput: function(control, interview) {
        if (control.getProperty("name") == "xSIRET") {
            return {
                mount: function(el) {
                    var div = document.createElement("input");
                    div.id = "xSIRET";
                    div.value = interview.getValue("siret", "record", control.instance);
                    el.appendChild(div);
                },
                update: function(el) {},
                validate: function(el) {
                    if (luhnChk(document.getElementById("xSIRET").value) == true) {
						interview.setInputValue("siret",document.getElementById("xSIRET").value, "record", control.instance);
                        return true;
                    } else {
                        return 'The string fails the Mod 10 Test'
                    }
                },
                unmount: function(el) {
                    if (control.getProperty("name") == "xSIRET") {
                        var xSIRET = document.getElementById("xSIRET");
                        xSIRET.parentNode.removeChild(xSIRET);
                    }
                }
            }
        }
    }
})

var luhnChk = (function (arr) {
    return function (ccNum) {
        var 
            len = ccNum.length,
            bit = 1,
            sum = 0,
            val;

        while (len) {
            val = parseInt(ccNum.charAt(--len), 10);
            sum += (bit ^= 1) ? arr[val] : val;
        }

        return sum && sum % 10 === 0;
    };
}([0, 2, 4, 6, 8, 1, 3, 5, 7, 9]));

As you can probably tell, what matters above is the luhnChk functiion (from the resource linked above) and the validate key. In Input Controls, the Validate needs to either return a boolean (true means the validation has succeeded and the test is passed, false means the validation has failed) or as a simpler alternative, return a string – this is considered to be a failure (false) and the string is used to display the error message. If you choose the pure boolean option then it is up to you to signal to the user that the validation failed.

This version works on instances of an entity, so the entity name and instance are used to retrieve and populate the value of the attribute. So it looks like this when the validation fails:

Luhn Test Example

It is important to note that this example is string based, so if you are planning on testing something different, you will need to strip out spaces, and convert the content to text.

If you are interested the demo 20C Zip File (free of charge) is in the shop, search for Luhn.

Spell Checking in Interview Controls

The other day, I came across a question regarding Spell Check in Interview Controls. You know the sort of thing – this would be useful for Inputs, but especially for Text Area Controls where there may be a considerable amount of text that will need spell checking. There are today several approaches but one that stands out.

The first option is to implement something in JavaScript – this has downsides for a couple of reasons. The dictionary for a given language, even compressed and specially structured, is going to be larger than average – the size of the file will make for slower processing. And the JavaScript will need to search this file. Thus, the performance might not be great. There are a couple of good toolkits that find innovative solutions, for example this one, but it will need work to build the apporopriate user interface.

The next option is to hand off some of the processing to the server-side. There are a number of spell check engines out there in open source that use PHP or ASP.NET for example. Some others use third-party servers or Google APIs that need payment. So for a simple spell check in Intelligent Advisor that sounds like a lot of effort.

The final spell check option is to leverage the browser itself. Most modern browsers support the spellcheck=”true” attribute value for inputs and text areas and lots of other text-content tags like <p> and <div>. So the simplest option is to switch on that attribute on any items you want to spell check. For example, add an invisible label extension to your Screen and use it scan all the relevant items on the page and add the spell check capability. For speed, the example below uses jQuery to select the relevant items for spell checking but you don’t need it of course, you can select using standard JavaScript too.

/*  Generated by the OPA Hub Website Code Generator 19/06/2020 18:27
Educational Example of Custom Label Extension for Oracle Policy Automation
I will remember this is for demonstration purposes only.
 */
OraclePolicyAutomation.AddExtension({
	customLabel: function (control, interview) {
		if (control.getProperty("name") == "xSpellCheckEnable") {
			return {
				mount: function (el) {
					console.log("Starting name:xSpellCheckEnable customLabel Mount");
					$(document).ready(function () {
						$("input[type='text'], textarea").attr('spellcheck', true);
					});
					console.log("Ending name:xSpellCheckEnable customLabel Mount");
				},
				update: function (el) {},
				unmount: function (el) {
					if (control.getProperty("name") == "xSpellCheckEnable") {
						console.log("Starting name:xSpellCheckEnable customLabel UnMount");
						console.log("Ending name:xSpellCheckEnable customLabel UnMount");
					}
				}
			}
		}
	}
})

With the above code in place, an input or text area will allow spell checking using the browser. Note the wavy line and the right-click:

Spell Check in IA

I’m not saying this is the perfect solution but there are some good and less good points:

  • No management of files needed
  • No user interface to create
  • Supported by the vast majority of browsers (see link above)
  • No server-side stuff to pay for
  • Applicable to pretty much any HTML tag

There are a few downsides

  • Usually defaults to the operating system / chosen language in the browser settings rather than the document language
  • Not the most exciting user interface
  • If you use multiple browsers you might end up with different word lists.

But sometimes, less is more. This example is free in the Shop. Happy Spell Checking!

Dynamic Input Mask JavaScript Extension

Dynamic Input Mask JavaScript Extension

This week I was lucky enough to be chatting to a reader when he shared a requirement for a dynamic mask. The exact concept was simple : when entering a telephone number, if the end user of the Interview types a number first, then the mask should default to the national format – for example (555) 1234-1234 – but if the end user types the international “+” prefix first then the mask should switch to a different one, for example +00 000 0000 0000 or whatever is appropriate. Outside of this example, there are frequently situations where more than one mask is needed, depending on some criteria or some fragment of data entry.

We should not forget of course that if all we want is a simple mask, then Intelligent Advisor has got it covered with the standard out of the box functionality. To implement a simple, static mask simply requires us to create an attribute of type Text and to select Masked Input from the Interview Ribbon before specifying the mask in the Input Mask dialog.

As an example, the following shows a Masked Input Control with a specific mask being implemented. In addition, you can see the Hint Text in the Interview tab shows the result in case you forget.

Clearly this is going to produce a good mask. But it cannot be changed dynamically, as it is part of the Control properties at runtime. So the possible solution is to use an Extension. In this case, a customInput. For the purposes of demonstration, we chose to use the jQuery Mask plugin. You can find more about it at the address : https://igorescobar.github.io/jQuery-Mask-Plugin/docs.html. The most interesting element is the fact that it supports dynamic masks. It uses the keypress event to enable the developer to check the character(s) and then change the mask. So this sounded like a great idea, and indeed proved quite straightforward. The code below shows that in fact there are several steps needed : define the two masks, and also handle the case when the area is blanked by the user.

Happily we found an old project where we had already used this dynamic input mask technique and adapted it for today.

Dynamic Mask

Here is the example code, of course you can get the Zip file from the Shop. There are some comments below.

/*  Generated by the OPA Hub Website Code Generator 30/11/2018 22:41
Educational Example of Custom Input Mask Extension for Oracle Policy Automation
I will remember this is for demonstration purposes only.
 */
OraclePolicyAutomation.AddExtension({
	customInput: function (control, interview) {
		if (control.getProperty("name") == "xMask") {
			return {
				mount: function (el) {
					console.log("Starting name:xMask customInput Mount");
					var div = document.createElement("input");
					div.id = "xMask";
					div.value = control.getValue();
					el.appendChild(div);
					var oldVal;
					$('#xMask').on('keypress', function () {
						var val = this.value;
						if ((val != oldVal) && (val.length == 1)) {
							oldVal = val;
							if (oldVal == '+') {
								$('#xMask').mask('+999-9999-9999');
								$('#xMask').val(oldVal)
							} else {
								$('#xMask').mask('(999)-999-99999');
								$('#xMask').val(oldVal)
							}
						} else if (val.length == 0) {
							$('#xMask').unmask();
						}
					});
					console.log("Ending name:xMask customInput Mount");
				},
				unmount: function (el) {
					if (control.getProperty("name") == "xMask") {
						console.log("Starting name:xMask customInput UnMount");
						var xMask = document.getElementById("xMask");
						xMask.parentNode.removeChild(xMask);
						console.log("Ending name:xMask customInput UnMount");
					}
				}
			}
		}
	}
})

As you can see in this dynamic input mask demo, there is only really the mount to worry about. We use the keypress event and switch the mask based on the previous character pressed on the keyboard. And if no characters exist in the control we remove the mask ready for the next try. Here is a short video demonstrating the effect, and comparing to a static mask. The input control extension has not had any CSS formatting applied so it appears a little smaller than the standard. This can easily be changed.

video

Have a nice day! (the documentation about standard input mask functionality can be found online here).

Input Validation in an Input Extension – IBAN Validation

Input Validation in an Input Extension – IBAN Validation

A funny thing happened today – I came across a forum post that was talking about exactly what we had been describing to someone else the same day. Odd. These confinement measures play tricks with your mind. Anyway here is the scenario, it’s about input validation.

The customer needs to validate a banking account number in IBAN format in Oracle Policy Automation as part of an HTML Interview. What are the options for input validation?

There are several ways to handle this

  1. If you are just validating one country-origin IBAN numbers then you could certainly use Regular Expressions on your Input Text Attribute. A checksum rule would provide extra validation.
  2. Write a lot of Error or Warning Rules to achieve the same.
  3. But given there are many formats and many countries, if you need something bigger then you had better use some help!

There are standalone JavaScript libraries like this one, or you might use the jQuery Validator instead. It comes with IBAN validation as part of the additional methods supplied with it.  Since a lot of people are familiar with jQuery, and since Oracle Policy Modeling already uses it, we decided to opt for that solution for input validation.

We downloaded the validator and the additional files. we ensured that jQuery was accessible to the Project (in short, we clicked the Styles button in the Interview, then the Custom.. button and said OK before pasting the files in the folder). If this was a real-life case we would load the files in the correct order programmatically (jQuery, Validator, then additional stuff) but for this quick hack we renamed the files in alphabetical order to make sure they loaded like A, B and C.

We created an Input attribute, and named it something like this:

Input Validation - Create Attribute

Then we added it to the Screen and hooked up a custom property to make sure the code is executed for the IBAN element.

Input Validation Setup Screen

Then I fired up my trusty Code Generator and created a template Input Extension. I edited the Validate handler and this is the complete input extension. Of course this is the bare bones but it will do for a start :

//*  Generated by the OPA Hub Website 18/03/2020 08:06
Educational Example of Custom Input Extension for Oracle Policy Automation
I will remember this is for demonstration purposes only.
 */
OraclePolicyAutomation.AddExtension({
	customInput: function (control, interview) {
		if (control.getProperty("name") == "xIBAN") {
			return {
				mount: function (el) {
					console.log("Starting name:xIBAN customInput Mount");
					var div = document.createElement("input");
					div.id = "xIBAN";
					div.value = control.getValue();
					div.setAttribute("data-rule-iban", "true");
					el.appendChild(div);
					console.log("Ending name:xIBAN customInput Mount");
				},
				update: function (el) {
					console.log("Starting name:xIBAN customInput Update");
					console.log("Ending name:xIBANcustomInput Update");
				},
				validate: function (el) {
					console.log("Starting name:xIBAN customInput Validate");
					//errorplacement ensures the standard message is not visible
					var validator = $(".opa-interview").validate({
							errorPlacement: function (error, element) {
								return true;
							}
						});
					var returnvalue = validator.element("#xIBAN")
					if (returnvalue === true) {
						control.setValue(document.getElementById("xIBAN").value);
						return true;
					} else {
						return 'Your Message Here - THIS IS NOT A VALID IBAN'
					}
					console.log("Ending name:xIBAN customInput Validate");
				},
				unmount: function (el) {
					if (control.getProperty("name") == "xIBAN") {
						console.log("Starting name:xIBAN customInput UnMount");
						var xIBAN = document.getElementById("xIBAN");
						xIBAN.parentNode.removeChild(xIBAN);
						console.log("Ending name:xIBAN customInput UnMount");
					}
				}
			}
		}
	}
})



Input Validation

Input Extension – Summary

The basic idea is simple – when the Validate handler fires, pull the form and the input element and validate it as an IBAN number. If it passes, then there is no error message. Otherwise the validate fails and you can tell the user. You can have a more interesting message than this of course.

The Zip Archive is in the OPA Hub Shop. Have fun!

Table Headers : Tabular Layout Trick

Table Headers : Tabular Layout Trick

Updated 18th March 2020

[Update Start

Thanks to assiduous reader Steven Robert (see the comments) who reached out and pointed out some annoying side effects and  a requirement, now I get to revisit this topic after a while away. As luck would have it, I was working on a similar situation to the one Steven describes, and I had not been timely in updating this post. Thanks to him and here is the updated version of the concept, with explanations.

  1. As the original post mentioned, and Steven pointed out, the selector used in the example is unreliable
  2. Steven proposed a new selector and solved that issue, making it multi-column as well

But the downside is the lack of display when the table is first instantiated. We need something capable of reacting before the think cycle kicks in.

In order to  achieve something like this, our “payload” needs to be part of the table that is displayed automatically. So the obvious candidate in this case is the label extension, since the first column is actually a label anyway. You could extend this concept to include other controls, but  it would require more heavy lifting as you would probably end up, if you were unlucky, building an entire Entity Container. We covered that in the JavaScript Extensions book and it isn’t usually a short effort. Anyway, we have a label so we are cool.

Our label extension has a mount key which fires when the label is displayed. So if the table is displayed, the label code will kick in. So we can be ready as soon as the table is ready. Secondly, we could in theory add several label columns and have custom headers for each of them (you could of course achieve that using non-JavaScript techniques).

Here is the walkthrough based on the previous Project (with the credit cards and visa cards which are derived as a subset of the credit cards).

  1. Add a label in the row in the table and add custom properties. It should display whatever attribute is appropriate.
  2. Generate a standard label extension and edit it a bit.
  3. Add the custom properties to trigger the code when the label is displayed.
  4. Add another label if you want (I ran out of originality but added a second one for demonstration purposes).
  5. Fire it up in the Debugger, and then in the Browser.

I didn’t go all the way to deployment but it looks like it could be elevated into a viable concept. Also, I wasn’t a very good citizen as I didn’t do any checking to avoid my code running every time the label is instantiated – I would most likely check to see if the header already had the text I wanted to avoid setting it again.

And finally, of course if you want the Project Zip just leave a note in the comments!

Here is a walkthrough video. Hope it makes sense!

Update End]

Original Post :

There is always much discussion when users first discover the Interview tab. Let’s be honest – not all of the comments are exactly positive. It all feels a bit, well, basic.

There are a number of things that catch you out at first (and indeed, later). So let’s take a moment to study tabular layouts and a common issue.

For this example I’m going to use the same project (Credit and Visa Cards) as the previous post, since that gives us two entities to work with.

Tabular Layout

Let’s consider that you want to display both entities using tabular layout. You create a Screen and set them both to tabular display. But let’s assume that you want to display the Visa Card with a couple of specifics. You want to include the provider of the credit card. So let’s set that up as a Value List and use an attribute on the Credit Card, and infer it on the Visa Card:

Table Headers Tabular Layout Trick 1So, with that now done, we want to display the Visa Card provider in the Entity Collect (as it is an inferred entity, we cannot use an Entity Collect). But we want to display it as a label as you can see in this screenshot (we added a name to the attribute as you saw in step one so we can reference it in our Screen:

Table Headers Tabular Layout Trick 2Notice how we added a label and used that to display the text of the provider? Using a label ensures three things

  1. It is read-only
  2. If the user is tabbing from input to input, the cursor will not get stuck in that field
  3. It doesn’t look like a read-only input, just a label (which is what we want).

But the downside is that the label does not have a table header in that column, since the Interview designer only adds those for Inputs:

Table Headers Tabular Layout Trick 4

I find it a shame that we cannot put table headers in this “tabular” column, since in HTML a table should have column headers. In fact if we take a moment to inspect this table in the browser, we note that annoying, there is a table header in the table:

Table Headers Tabular Layout Trick 5

So, we need to get that table header populated with our chosen text. But how shall we do it? We don’t want to create an Entity Container extension, since that would mean we have to do the whole thing from top to bottom. So we only want a little tiny change. We have a couple of choices.

  1. Create a Style Extension for the Entity Collect
  2. Create a Label Extension for stealth modification

Let’s try the first option, since it reveals some interesting facts about Styling Extensions. Firstly, get ready by doing the following; change the text associated with your entity in the Interview by double-clicking where the rectangle is, and entering whatever text you would like to display in the missing header.

Then add a compound Styling Extension to your Project. Tabular Containers allow for nested styling, like this:

OraclePolicyAutomation.AddExtension({
style: {
tabularContainer: function (control) {
if (control.getProperty("name") === "xContainer") {
style: {
headerRow:
YOUR STUFF GOES HERE

}
}
}

}
});

Notice the “headerRow” is a child of “tabularContainer”. And notice the line that says YOUR STUFF GOES HERE. Now for an interesting fact about Styling Extensions. They behave, to a reasonable degree, just like Control Extensions. They are really one and the same thing – the main difference of course is the handlers that are exposed in Control and Interview Extensions.

Drop jQuery into your resources folder, and then replace YOUR STUFF GOES HERE with the following line:

$("#opaCtl4th0").text(control.getCaption());

Of course, the jQuery selector may be different for you but it is easy to find the “header” I illustrated in the previous screenshots. Open your Project in a real Browser (Ctrl+F5) for debugging and take a look at the results:

Final Header Result

Our Styling Extension has added the text to the header, drawing it from the Interview Screen Entity Container definition, and we have it where we want it. Of course, you could style it as well.

But it goes to show that Styling Extensions are really not very different to Control Extensions!

Back to Basics : Extensions #2

Back to Basics : Extensions #2

Following on from the previous post, we delve more deeply into the JavaScript Extensions world.

Interview Execution in a Browser

So how does an Interview Extension work? Let’s begin with some basic information about how your Oracle Policy Automation Interview runs in your Browser. If you happened to be viewing an Interview right now, and you were to open the Console (F12 in Google Chrome, or Microsoft Edge. Check your Browser documentation for the equivalent key or menu option), you might be able to view something like the following screenshot. Check the steps under the image as you may need to refer to them in your own case.

Extensions

In this screenshot I have launched a Project using the Debugger. Remember that if you hold down F5 while clicking the Debug button, you will open the Interview in the Debugger and in your default Browser.

  1. Your web server may of course be a different address.
  2. The web-determinations folder will not have the same numeric suffix as in this screenshot, indeed will most likely not have a suffix at all. This is a feature of the Debugging session.
  3. The js file is most likely in the staticresource folder, however if you are in a more integrated environment it may be in a different subfolder, or a different folder altogether. But it will be present.
  4. The contents of the js file can be read more easily by selecting (in Google Chrome in this case) the option to pretty print the code.

Interviews.js

This file is the foundation of the Interview experience provided by Oracle Policy Automation. It contains all the code necessary to make the user experience function correctly. Inside this file, however, there is a built-in capacity to accept extensions that change the behaviour of the Interview.

In your Console, search in the file for the following text – “customLabel:” (without the quotation marks, but with the colon). You should find one instance of that text, as shown in the screenshot below.

Extensions

  1. Search for the text
  2. Find the text in the file.

Take a moment to perform a second search in the same file, for the text shown below. Use the screenshot as your guide.

  1. Ensure you are looking at interviews.js
  2. Search for this text
  3. View the style definition for textInputStyle.

Accepted Extension Types

Notice in the first example, that customLabel is only one of a series of items in the first list. These are the recognized types of Control extension that we, as Oracle Policy Automation Project workers, are permitted to develop.

In the second search you found that there was a style defined for controls called textInputs. Although not quite as obvious perhaps as the first example, an Oracle Policy Automation Project might want to override the Style(s) used in a Project, in order to comply with corporate guidelines for example: and this system will help us do just that. Style Extensions use keywords in the same way to indicate which elements you wish to style.

About Extensions

It is not important at this stage to understand how these extensions are created or used. It is, however fundamentally important to understand that you will be extending Oracle Policy Automation Interviews by adding one or more of these acceptable extensions, and that they will be run in the browser in the same way as the standard JavaScript is already. These interviews can then be better adapted to your IT environment. As an example, read how Styling Extensions enable integration visually with Oracle Content & Experience Cloud.

More on this subject, with some worked examples, shortly.

Back to Basics – What are Extensions?

Back to Basics – What are Extensions?

Styling Extensions

Introduced in Oracle Policy Automation November 2016 release, a styling extension allows a designer to create styling rules and logic using JavaScript and Cascading Style Sheets. These files are contained within the deployed Oracle Policy Automation project, and should be used only if you have exhausted all of the built in Styling possibilities offered as standard in the Interview tab, Styles dialog.

Control Extensions

A Control represents a single item placed on a Screen during the design of an Interview. Typically these controls might be used by the user to enter data (for example, a Control with a Calendar attached to enter the date of birth of the applicant) or to organize data on the Screen (for example, a Container that allows for a better layout of several other items). At run-time, each Control is translated into HTML and associated JavaScript code. Introduced initially at the same time as the styling extensions mentioned in the previous paragraph, although they have grown more numerous over time.

Control Extensions Example

They are generally referred to by a name such as customLabel or customContainer which identifies the type. In a later post you will learn the origin of these names.

Evolution

Interview behavior Extensions have gradually gained in functionality, and also in terms of which Control types are available for customization. There are different kinds of extension for different needs.

Scope

If Controls represent items grouped on a Screen (and therefore on a page, as far as most Interview users are concerned), with Extensions you can also customize the real estate around the pages – whether it be the navigation styling, of a custom footer or header, or even a completely new navigation system. So extensions can be for a single Control, or for a complete Navigation system.

Naming Convention

Technically speaking, all of the examples mentioned above are called Interview Extensions in the official documentation, and they are divided into styling extensions and control extensions as you will discover. You can find the reference material at the following URL at the time of writing .

Architecture

Before looking at the different types available to us, it pays to review the architecture of the Interview and gain understanding as to how they function within it. Coming next…

Stunning Infographics with Oracle Policy Automation and Easelly #1

Stunning Infographics with Oracle Policy Automation and Easelly

Oracle Policy Automation and Easelly : Once in a while I come across something in the Oracle Policy Automation universe that just makes me go “Wow!!!”. And today’s post is like that. I was working on a demonstration the other day of a fairly familiar concept : a  retail audit or “store visit”.

Visits to retailers, whatever product you are selling, are pretty often rushed affairs, probably squeezed in a back room or just hunkering down on the corner of a work surface for a few minutes. It’s in the middle of the day, there are customers coming in and in spite of cordial relations, the retailer would really much rather be doing their job. And chances are, there are another 3 or 4 people coming in later to do the same thing as you.

Oracle Policy Automation is a real benefit in situations like this – you can perform what-ifs, demonstrate how the retailer is performing, simulate new scenarios to encourage and engage with them.

But when you’re fighting to get focus, and the room is airless and hot, you really need to be able to bring your A game. I’ve watched some amazing people doing this job, and they really impress how much they pack into the short time they are on the premises. But when they are gone, their message sometimes gets forgotten, mis-remembered or filed away. I started thinking how  Oracle Policy Automation could really leave a mark.

My first thoughts were to Oracle BI Publisher for a take-away or handout, and there are some excellent demonstrations out there, including in the Sample Projects (Healthy Eating). But I was still left wondering as to what might be a better solution. BI Publisher is in this case let down by it’s basic format and premise – Word. It is too linear, static and inflexible. You don’t use Word to create the most modern, web-friendly and engaging support materials. Then I had a bit of a brainwave.

Stunning Infographics with Oracle Policy Automation and Easelly

For a number of years I have been a user of Easelly – an online Infographics design platform. I’ve had great fun with it, using it to show results of surveys and I use their templates quite a lot when I need a framework for a new graphic. I’m not a designer, but I’m pleased with the easy learning curve and the results.

Recently Easelly have started offering a REST API to their “pro users”. And this allows for the generation of PDF or PNG content on the fly. For example, you have data in Oracle Policy Automation and you insert it on the fly into your best infographic. Send it by mail, print it before you visit – it doesn’t matter how you do it but you will definitely want to get this in front of your customer. Pro accoun

Oracle Policy Automation and Easelly

ts are not expensive – $24 US Dollar a year.

Here is an example, with an infographic designed by Easelly. You can see it on the right hand side of the page. I think you would agree that something like that could be done in BI Publisher, but not without a very robust skill set. With Easelly however, a tool built for infographics, it is considerably simpler. Please note : I’ve used the Easelly platform since it was launched, and I like to think I know BI Publisher quite well. So I can compare the two fairly objectively – and for modern infographics, the learning curve and capabilities of Easelly mean I would choose it every time.

Custom Control and REST API Integration

And here is the video that shows it being generated live in Oracle Policy Automation. That’s what I can a showstopper graphic. The great thing is, with the Oracle Policy Automation Custom Control concept , adding this to the Interview is a matter of minutes.

The data, the labels – all that is coming from Oracle Policy Automation much in the same way as for BI Publisher and Forms. Only way better looking!  In the next article in this series I’ll show you how to integrate Oracle Policy Automation with Easelly using, in this case, a PHP-based Custom Label Control.

Importing Data into an Interview : Excel Example

Importing Data into an Interview : Excel Example

Readers will remember a while ago I explained briefly how to use Microsoft Excel to act as a Connection Datasource – in this overview article, followed by this one in a little more detail. Now we look at another challenge : Importing Data into an Interview.

Well, here comes another example of the ubiquitous nature of Microsoft Excel. The customer requirement was as follows:

Using a simple mechanism, let the user upload an existing Excel spreadsheet into the Interview. Parse the spreadsheet, read the data in it, create corresponding rows in an Entity. Let the user review the data but do not require any new data entry. There may be up to 250 rows of data to import. So how do you go about about Importing Data into an Interview?

So how can we face up to a challenge like that? We need:

  • An upload that isn’t a standard File Upload Group
  • A parsing mechanism to read Excel and extract the data in a given tab, or wherever
  • A custom Entity Collect to handle the data import / create the rows in a Screen

The shopping list above isn’t that long.

The File Upload is essentially an HTML 5 component to let the user select a file on their computer. We cannot access an arbitrary local path from JavaScript, so we need the user to point to the file they want to upload.

There are a number of JavaScript-based Excel parsers, including the excellent SheetJS js-xlsx which we used. It is capable of converting to and from Excel, which is no easy task when you consider that an Excel file is basically a Zip file with a bunch of complicated stuff inside it. The library can convert to HTML, CSV and magically (for our requirement) JSON. Awesome!

Plus, in a previous post we’ve also looked at the (large amount of) work required to build a Custom Entity Collect Extension. In fact when I was writing that article I was thinking, for goodness sake Richard, when do you think you will actually need to go to the trouble of building that Entity Collect Extension? Well, I’ve finally found a use for it – Importing Data into an Interview!

We need an Entity Collect Extension since we need some way of getting the Excel data into the Entity Collect, which ultimately means we need to do some work behind the scenes between the import of the data and the display of the Entity Collect. We need to rewire the Entity Collect temporarily so that it sucks our Excel data up, before we show it to the user so they can examine the results.

For the purposes of a raw demo, I unplugged all the other functionality (delete buttons, add buttons, etc.) and just concentrated on getting the data into the Entity Collect. There are how ever a few caveats. Once you get into the larger imports, at least in the Debugger, you can expect to see “concurrent record editing” errors. I’m trying to find out what the limit is exactly. But up to a few hundred I think it’s OK.

So let’s look at the items in turn.

File Upload and Data Load

{
					//console.log("Starting customInput Mount");
					var div = document.createElement("input");
					div.id = "myFile";
					div.type = "File";
					//div.value = control.getValue();
					el.appendChild(div);
					
					function handleFile(e) {
						var files = e.target.files,
						f = files[0];
						var reader = new FileReader();
						reader.onload = function (e) {
							var data = new Uint8Array(e.target.result);
							//console.log("In Change");
							var workbook = XLSX.read(data, {
									type: 'array'
								});

							worksheet = workbook.Sheets["YOURWORKSHEET"];
							jsonoutput = XLSX.utils.sheet_to_json(worksheet, {
						raw: true, header : 1
							});
						//console.log("Read " + jsonoutput );
						};
						
						reader.readAsArrayBuffer(f);	
						var completepath = $(':file').val();
							//console.log(completepath);
						interview.setInputValue("rest_filenameandpath", completepath);
					}
					
					
				var filedialog = document.getElementById("myFile");
					filedialog.addEventListener('change', handleFile, false);
					
					var completepath = $(':file').val();
						control.setValue(completepath);
						//console.log("Hello " + completepath);
						//console.log("Ending customInput Mount");
				}

Assuming you have a custom Input framework as your starting point, the above code will be in the mount. This will build an HTML5 file upload control, and attach an event handler. The code regarding Excel depends upon xlsx.full.min.js being in the resources directory. But that’s it. You’ve loaded the Excel file into a JSON object.

Entity Collect

The next step is to include a Custom Entity Collect in your project, and use the jsonoutput object (which you just created from the imported file above) in the mount of the Entity Collect to load the JSON into the Entity Collect. The following is an extract from the mount code

var numEntities = Object.size(jsonoutput);
					//console.log(numEntities);
					// Remove header row if the file has one
					jsonoutput.shift();
					// load records into the Entity Collect

					if (control.getRows() == 0) {
						for (j = 0; j < numEntities - 1; j++) {
							control.addNewRow();
							var mycurrentrecords = control.getRows();
							mycurrentrecords[j][0].setValue(jsonoutput[j][0]);
							mycurrentrecords[j][1].setValue(jsonoutput[j][1]);
							mycurrentrecords[j][2].setValue(jsonoutput[j][2]);
							mycurrentrecords[j][3].setValue(jsonoutput[j][3])
drawrows();

The end result is something like this:

Importing Data into an Interview

The File is loaded into the Entity Collect, and the contents displayed to the user. In my case I unhooked all the code related to modification (the onchange stuff from the original idea) and removed the add / delete buttons, since it was designed to just allow the user to see the loaded result, not modify it.

Importing Data into an Interview

If you want to have a look at the project, just download the very basic example here.

Business Process : Force PDF Download in OPA

Business Process : Force PDF Download in OPA

After last week’s business case put forward about Calendar Blackout dates, this week we can thank Arnaud D and Sylvie A from Switzerland for their excellent use case. This week we will be discussing the idea of letting a user progress to the end of an Interview, only if they have clicked to download a PDF file. So, can we Force PDF Download in OPA?

There is not really a technically feasible way to prove that a user actually read a PDF. But if we can force them to download it, at least we know they have gotten that far!

The business case is easily applied to many industries, for example where terms and conditions or a contract are generated and tacit approval needed before the user continues. Or, more simply, you need some way to encourage people to read the BI Publisher thing you spent all those weeks developing.

It is also a good opportunity for us to investigate the new custom Button extensions available in Oracle Policy Automation 19A. So this won’t work in 18D or earlier. The approach we are going to take will involve two different extensions. Let’s go!

Input Extension

The input extension is needed because we wish to capture the value of a Boolean attribute for our demonstration. Navigating forwards will only be allowed if this Boolean is true.

/*  Generated by the OPA Hub Website 29/03/2019 19:35
Educational Example of Custom Input Extension for Oracle Policy Automation
I will remember this is for demonstration purposes only.
 */
OraclePolicyAutomation.AddExtension({
	fullCustomInput: function (control, interview) {
		if (control.getProperty("name") == "xInput") {
			return {
				mount: function (el) {
					console.log("Starting customInput Mount");
					var div = document.createElement("input");
					div.id = "myInput";
					div.value = interview.getValue("my_flag");
div.setAttribute("style", "visibility: hidden");
el.appendChild(div);
console.log("Ending customInput Mount");
$(".opa-document").click(function(){
console.log("Clicked Link");
control.setValue(true);
});
},
update: function (el) {
console.log("Starting customInput Update");

console.log("Ending customInput Update");
},
validate: function (el) {
console.log("Starting customInput Validate");

console.log("Ending customInput Validate");
},
unmount: function (el) {
if (control.getProperty("name") == "xInput") {
console.log("Starting customInput UnMount");

interview.setInputValue("my_flag", $("#myInput").val());
var myInput = document.getElementById("myInput");
myInput.parentNode.removeChild(myInput);
console.log("Ending customInput UnMount");
}
}
}
}
}
})

So what’s going on here? Basically we are putting a click event on a PDF download. Notice line 17 : this adds a click event.  So as soon as the custom Input is loaded, an event is added to another control using jQuery. When the link is clicked, we update our flag to true. We use the style selector “.opa-document” to find the link. If you have more than one link, you will need a more precise method of finding it. Other than that, the custom input itself is invisible, and simply reads the current value of the flag into the control.

Button Extension

The other extension, for a nextButton, uses jQuery to display a dialog when you try and leave the screen before clicking on the link (the flag is false). Otherwise it lets you navigate without issues.

OraclePolicyAutomation.AddExtension({
	customNextButton: function (interview) {
		let button,
		caption;

		return {
			mount: function (el) {
				const screen = interview.currentScreen();

				button = document.createElement("div");
				button.setAttribute("title", "Print Your PDF First");
				button.setAttribute("class", "opa-back");
				button.setAttribute("role", "button");
				button.setAttribute("style", "	text-overflow: ellipsis; white-space: nowrap; line-height: 1; cursor: pointer; padding: 0.7em 1.2em; border: none; text-decoration: none; -webkit-appearance: none; flex: 0 0 auto; text-align: center; color: rgb(255, 255, 255); border-radius: 6px; background: rgb(20, 116, 191); font-size: 16px; font-style: normal; font-weight: normal; margin-left: 6px; visibility: visible; outline: none;");

				caption = document.createTextNode(screen.getNextCaption());
				button.appendChild(caption);

				var dialog = document.createElement("div");
				dialog.setAttribute("id", "dialog");
				dialog.setAttribute("title", "Warning");

				var dialogtext = "Please download your document first.";
				el.appendChild(dialog);


				button.onclick = function (evt) {
					evt.preventDefault();
					if (interview.getValue("my_flag") == true) {
						interview.submit();
					} else {
						$("#dialog").dialog();
						$("#dialog").text(dialogtext);
						console.log("Not Yet ");
						console.log("Flag Value is " + $("#myInput").val());
					}
				}

				el.appendChild(button);
			},
			update: function (el) {
				const screen = interview.currentScreen();

				caption.textContent = screen.getNextCaption();

				if (screen.hasNextButton())
					button.removeAttribute("disabled");
				else
					button.setAttribute("disabled", "disabled");
			},
			unmount: function (el) {}
		}
	}
});

So what’s happening? The early part of the mount is simply painting a nice button on the screen. We could of course not display the button at all, but the User Experience might be better if we display it always, just prohibit using it in certain circumstances. Starting around line 19 we create the dialog and make it ready for use.

Line 27 includes the button click routine which either displays the dialog, complete with a message, or just lets you navigate as normal.

The other lines in the unmount are just taken from the example in the online help, where if this is the last Screen, no next button is available. But our business case is based on it not being the last.

Force PDF Download : Animated GIF

In the animated GIF above, you can see what it looks like.

The Project Zip is in the shop. Have fun!

Worldwide
Logo by Southpaw Projects LLC