Request Resources with multiple entitlement values using default GWT Widgets

Request Resources with multiple entitlement values using default GWT Widgets

Some time ago, I wrote a cool solution named “Read Resource Entitlement Values from User Application workflow”, presenting a way to handle multiple entitlement values from the user application, since NetIQ stuff does not allow to read it directly.

We worked a lot with resources having multiple entitlement values. For this reason we wanted to provide something better than what we did, in order to provide a simple interface for our users.

The lack of my previous cool solution was the impossibility in having a widget able to query dynamically the Identity Applications to retrieve values… you had to preload the entitlement values of a resource on a pre-activity mapping. I would prefer having something more simple and reusable like the GWT widget you can see in the user application dashboard.

defaultAssignResource

So we did it, we took the NetIQ widget and transferred it inside a PRD, like you can see from this screenshot.

AssignResourceMultipleValueOnPRDs

We tried this PRD even with the new IDM 4.5 Identity Manager Home (the new dashboard interface) and it works as expected.

Let's figure out how we did it.

First of all in our PRD request form we need a hidden field that recalls the standard widget named “ResourceAssignmentsTable”.

03_RequestForm

You have to create a hidden field of Control type HTML. In the HTML content of this field you have to put the instantiation of the entire resources assignment table.


<div>
<juice:UIControlGWT id='ResourceAssignmentsTable' fieldname='ResourceAssignmentsTable' controlType='ResourceAssignmentsTable' required='false' readOnly='false' suppressMetaDiv='false' />
</div>


Unfortunately, if you try to test the PRD, you would see that nothing is loaded (with a browser inspector, since it is a hidden field).

This is because the GWT framework has to be instantiated on your prd, inserting two external scripts.

/IDM/com.novell.srvprv.impl.gwt.header.header/com.novell.srvprv.impl.gwt.header.header.nocache.js

/IDM/com.novell.srvprv.impl.gwt.gwtControls/com.novell.srvprv.impl.gwt.gwtControls.nocache.js

If you would retry to load the widget, you will see that it still does not work, because of a problem in the timing of the events. The external script is loaded before the existence of the tag “<juice:UIControlGWT id='ResourceAssignmentsTable' ...”, so GWT even if loaded it is not able to transform our html content in a real widget.

So we added an inline script to reload a second time the GWT framework and to add new GWT interceptor able to transform our HTML field in a true standard widget. Here is the code able to instantiate inside a PRD the juice tag with id ResourceAssignmentsTable:

function loadScript(url, callback)
{
// Adding the script tag to the head as suggested before
var body = document.getElementsByTagName('fieldset')[0];
var script = document.createElement('script');
//script.type = 'text/javascript';
script.src = url;
// Then bind the event to the callback function.
// There are several events for cross browser compatibility.
script.onreadystatechange = callback;
script.onload = callback;

// Fire the loading
console.log("prepending");
body.insertBefore(script, body.firstChild);

}



function callGTWHeaderOnInjectDone(){
try{
var body = document.getElementsByTagName('body')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.setAttribute("defer", "defer");
script.innerHTML="com_novell_srvprv_impl_gwt_header_header.onInjectionDone('com.novell.srvprv.impl.gwt.header.header')";
body.appendChild(script);
}
catch (e){
console.log("error on appending GWT headers");
}
}

function callGTWControlsOnInjectDone(){
try{
var body = $('div.detaildialog')[0];
var script2 = document.createElement('script');
script2.type = 'text/javascript';
script2.setAttribute("defer","defer");
script2.innerHTML="com_novell_srvprv_impl_gwt_gwtControls.onInjectionDone('com.novell.srvprv.impl.gwt.gwtControls')";
body.appendChild(script2);
}
catch(e){
console.log("error on appenging second script");
}
}

function setGWTControls(){
var myFieldSet = $("fieldset");
if (myFieldSet != undefined && (!loadedModule)){
loadScript("/IDM/com.novell.srvprv.impl.gwt.header.header/com.novell.srvprv.impl.gwt.header.header.nocache.js",console.log("gwt header loaded"));
loadScript("/IDM/com.novell.srvprv.impl.gwt.gwtControls/com.novell.srvprv.impl.gwt.gwtControls.nocache.js",console.log("gwt controls loaded"));
loadedModule = true;
}
var mycontrol = $("#AssignResourcAction");

if (mycontrol.attr('id') == 'AssignResourcAction'){
console.log("GWT ready");
stopTimer();
} else {
console.log("waiting for gwt to fill up controls...");
callGTWHeaderOnInjectDone();
callGTWControlsOnInjectDone();
if (typeof com_novell_srvprv_impl_gwt_header_header !== 'undefined') {
console.log("header: exist");
}
else { console.log("header: not exist");}

console.log("control: " + com_novell_srvprv_impl_gwt_gwtControls);
}
}

function stopTimer(){
clearInterval(myTimer);
gwtready = true;
}

var loadedModule = false;
var myTimer = setInterval(setGWTControls,500);



Since this point we focused in the first task, importing a default control inside the PRD environment.

Now it’s time to dig inside the control itself and understanding how to do something usable.

If you take a look at the “assign...” button of the ResourceAssignmentsTable control, it asks for a mandatory field “initial request description” and to choose the resource you want to ask for.

Inside a PRD, most of the time you prepare something for a specific group of users and you don’t want to allow them to modify any resource. So we focused our efforts in finding a way to pre-fill the first popup window named “assign resource”, even because the actions generated from the default OK button of the widget are not of any interest on a PRD, since the widget is thought to be used as a self service feature.

In the example shown before, we did a text field where you can choose for your resource. This text field has an ondblclick event attached responsible to call the hidden widget ResourceAssignmentsTable.

The code attached to ondblclick event is the following:

// start the resource lookup specifying three parameters
// 1) the resource name, took from the text field
// 2) the fieldname of the caller, in our example the field is named “SelectResourceName”
// 3) optional: the picklist name where return the selected entitlement values
// return a boolean value -> true when the lookup starts, false if GWT is not yet available
// when a lookup starts, the arrays entValues and entDisplay are filled
var startedSearch = searchResource(field.getValue(),'SelectResourceName','Result');


In another inline script, we handle the popup as described before, autofilling what we need, hiding what we don’t want, creating a new OK button to reuse the widget for different purposes. Here is the code of this script:

//timer to wait for results
var aTimer = undefined;
//boolen to trace the state of gwt framework
var gwtready = false;
//entitlement values selected with the widget
var entValues = new Array();
//entitlement display values selected with the widget
var entDisplay = new Array();

function searchResource(resname, callingfield,bagfield){
var result = false;
// if the GWT framework was loaded by the other inline script
if (gwtready){
bagfield = bagfield || "";
//show the “Assign Resource” popupt
$("#AssignResourcAction").click();
//remove the assign button
$('#AssignRevokeDialog').find('.ua-Button').first().remove();
//move the dialog near the caller field
var Xpos = $('#_' + callingfield).position().left;
var Ypos = $('#_' + callingfield).position().top;
$('#AssignRevokeDialog').css({top: Xpos, left: Ypos});
//add the "ok" button to confirm the entitlment values selection
var btnstring = "";
$('#AssignRevokeDialog').find('.ua-Button').parent().prepend(btnstring);
//remove the “”initial request description” field from the user interface
$("#initialRequestDescription").closest("tr").remove();
//remove the menu left as leftover from any call done previously (fix for a widget bug)
$("ul.dijitMenu").remove();
//call the dialog to show directly the entitlement values
aTimer = setInterval(controlSearchDialog,20,resname);
result = true;
} else console.log("gwt not ready yet, try again in a while");
return result;
}

function controlSearchDialog(resname){
var id = $("div[id^='widget__Resource']").attr("widgetid");
console.log("div.dijit widgetid=" + id);
var myWidget = dijit.byId(id);
//if we found the instantiated widget...
if (myWidget != undefined){
console.log("This is the resource we look for" + resname);
clearInterval(aTimer);
//call a widget juice method _startSearch using the name written on the caller text field
myWidget._startSearch(resname);
aTimer = setInterval(confirmSearch,20);
}
}

//this method is able to autoselect the resource we looked up for and fire the entitlement values request
function confirmSearch(){
var idMenu = $("ul.dijitMenu").attr("widgetid");
if (idMenu != undefined){
clearInterval(aTimer);
//console.log("ul.dijitMenu widgetid=" + idMenu);
var myWidget = dijit.byId(idMenu);
myWidget.highlightFirstOption();
var option = myWidget.getHighlightedOption();
myWidget._setValueAttr({ target: option }, true);
}
}


//the function designed to bring back the selected entitlement values to your PRD request form
function getEntValues(bagfield){
// empty the arrays containing values and display values
entValues = [];
entDisplay=[];
$('#entParamValueListBox option').each(function(name, val) { entValues.push(val.value); entDisplay.push(val.text); });
//close the dialog
console.log($('#AssignRevokeDialog').find('.ua-Button').text());
//jquery click does not work, so we do a domelement click
$('#AssignRevokeDialog').find('.ua-Button')[0].click();
//if the parameter bagfield is present, set the values on that field
if (bagfield != ""){
var bag=JUICE.UICtrlUtil.getControl(bagfield);
bag.setValues(entValues,entDisplay,false);
}
}


This should be all you need to make use of the widget inside your prds… instead unfortunately it is not true.

When the first popup “Assign Resource” is loaded from the widget, the userapp loads a mandatory field named “Initial Request Description”. When you click submit on your form, the popup is no longer present and both the field. This does a mess with the request auto validation, so you would not be able to submit your form.

To manage this collateral effect, we worked on an interceptor able to avoid the default form validation. This interceptor is different depending on the fact if the widget is called on a request form or inside an approval activity.

If you are injecting the widget inside a request form, you have to write the following interceptor:

window.inv=function(invocation){
//this script replace the standard invocation.proceed() of an interceptor, so we can avoid to validate the required fields
var cU = JUICE.UICtrlUtil;
var evt=cU.prepareSubmit(evt);
var submitAction = SubmitAction1Click.prototype.constructor.toString();
submitAction = submitAction.split(",")[1].replace('"','');
submitAction = submitAction.replace('"','');
$('#uiform').attr("action", submitAction);
$('#uiform')[0].submit();
}

form.interceptAction( "SubmitAction", "around", window.inv)

For an approval activity you have to pass inside an hidden field the taskId and use a different approch to avoid the validation
window.inv=function(invocation){
var cU = JUICE.UICtrlUtil;
var evt=cU.prepareSubmit(evt);
$('#uiaction').attr("value","ApprovalAction1");
submitThenParent('JUICE.getControl(2)','"+ form.getValue("taskId") + "','approve');
};

form.interceptAction( "ApprovalAction", "around", window.inv);


This code is obtained studying a normal activity with browser inspector, changing the calls just to avoid a validation.

This work is the best way we found to obtain a default widget inside a PRD. I have to thank a lot Giorgio Canale for the invaluable help he gave in this work.

As you can see, in this cool solution a lot of things we wrote can change from one version of IDM to another. For this reason we are still hoping that NetIQ/MicroFocus in the future will provide a standard tool to do the same operations.
Labels (1)

DISCLAIMER:

Some content on Community Tips & Information pages is not officially supported by Micro Focus. Please refer to our Terms of Use for more detail.
Top Contributors
Version history
Revision #:
4 of 4
Last update:
‎2020-03-10 19:19
Updated by:
 
The opinions expressed above are the personal opinions of the authors, not of Micro Focus. By using this site, you accept the Terms of Use and Rules of Participation. Certain versions of content ("Material") accessible here may contain branding from Hewlett-Packard Company (now HP Inc.) and Hewlett Packard Enterprise Company. As of September 1, 2017, the Material is now offered by Micro Focus, a separately owned and operated company. Any reference to the HP and Hewlett Packard Enterprise/HPE marks is historical in nature, and the HP and Hewlett Packard Enterprise/HPE marks are the property of their respective owners.