In my previous post, I presented a simple usage of the Mashup portlet in Saba for displaying a Survey Monkey survey and collect anonymous responses. Although immediate to configure, I am not entirely happy with the result, as on completion of the survey, Survey Monkey displays a promotional page to the user. Also, once the response is collected, the portlet is no longer relevant to the user and it would be better hidden automatically.
So my new objective that I want to address in this post is to hide the portlet automatically and remove it from the user's dashboard as soon as he/she provides the response. In order to do that, I need to... invent a new bespoke portlet! C'est plus facile!
Eventually, as a System Administrator, I want to be able to enter this data in the portlet parameters as in the following screenshot:
The code now. I will call my new portlet "Survey Monkey" (no surprise here!) and define the portlet manager as follows:
public class SurveyMonkeyPortletPageManager
extends AbstractPortletPageManager
implements PortletPageManager
{
@Override
protected void init()
throws SabaException
{
registerPage("showDefaultDisplay",
"/custom/portlets/surveyMonkeyPortlet.rdf");
The surveyMonkeyPortlet.rdf page is the controller for the Saba WDK pages that represent the portlet itself. More about this and the model and view pages later in this article.
Where are the parameters? Within the init method, simply add these additional lines of code to register the three parameters. Unfortunately, parameter names cannot contains spaces, so play with upper and lower case letters (aka Pascal case) for making these names readable.
ParameterDetail url = new ParameterDetail("SurveyURL",
TypeInfo.createStringType());
registerParameter(url);
ParameterDetail par = new ParameterDetail("IframeParams",
TypeInfo.createStringType());
registerParameter(par);
ParameterDetail msg = new ParameterDetail("MessageXHTML",
TypeInfo.createStringType());
registerParameter(msg);
That's all we need for the Java portlet manager. As we know, the portlet manager references the controller page (RDF page) that is responsible for orchestrating the communication flow between the model (XML page) and the view (XSL page). The controller references the other two pages with these simple WDK tags:
<wdk:model rdf:resource="surveyMonkeyPortlet.xml"/>
<wdk:view rdf:resource="surveyMonkeyPortlet.xsl"/>
For the end user, the process starts when the portlet is assigned to the portal and made visible in the Dashboard upon login:
The workflows for these two processes are implemented in the Saba WDK pages with the help of a special portlet command class that facilitates the communication between the model page and the database. This page is not strictly necessary, as all the business logic can be contained in the model page, but it is a commodity for encapsulating repeating code into one single specialised class, as we will see later.
Java code can be injected in the model page by using the <xsp:logic> tag.
<xsp:logic>
SurveyMonkeyPortletCommand command =
new SurveyMonkeyPortletCommand();
</xsp:logic>
This simple line of code creates a new instance of the SurveyMonkeyPortletCommand class, to be used by the <wdktags:execute> tag for invoking the command class.
<wdktags:execute commandObj="command">
<param name="SurveyURL" expr="SurveyURL" type="String" mode="in"/>
<param name="IframeParams" expr="IframeParams" type="String" mode="in"/>
<param name="MessageXHTML" expr="MessageXHTML" type="String" mode="in"/>
<param name="actionKey" expr="actionKey" type="String" mode="in"/>
</wdktags:execute>
The command accepts the 3 portlet parameters, plus a fourth actionKey that I'll explain later. After execution, the command returns an XML model that is then transferred to the view for visualization to the end user.
The last section of the model identifies all widgets, i.e. visual controls, to display on the page. We only have one button to show to the user, to inform Saba the the survey has been completed. This is the Hide button that also triggers the action for automatically hiding the portlet from the user's dashboard without the user explicitly removes the portlet from the dashboard page. The tag for defining a button widget is <wdk:link> with type = button. On click, the portlet reloads and executes the command again, but this time, because of the actionKey parameter mentioned before, the outcome is different, as explained later for the command class.
The XML tags for defining the Hide button are as follows:
<wdk:widgets>
<wdk:link name="hideButton">
<id>hideButton</id>
<label>Hide</label>
<type>button</type>
<do>Completed</do>
</wdk:link>
</wdk:widgets>
The <do> tag is a shortcut for passing the actionKey parameter to the portlet.
<iframe id="iframeSurveyMonkey">
<xsl:attribute name="src">
<xsl:value-of select="params/surveyUrl" />
</xsl:attribute>
The section below the survey, with the message, instead is contained within a <div> tag. The message itself can contain HTML tags for formatting the text, hence the XSLT code for rendering this message on screen relies on the <xsl:copy-of> tag that performs a copy of the message including all existing tags. Adding the Hide button widget relies instead on the ordinary <xsl:apply-templates> tag for selecting the widget matching the indicated name.
<div class="sbListText">
<xsl:for-each select="params/message/*">
<xsl:copy-of select="."/>
</xsl:for-each>
<xsl:apply-templates select="$wdkWidget[@name='hideButton']"/>
</div>
The base class from which all commands should extend their definition is SabaWebCommand. The input parameters are to be defined in the constructor through the addInParam method.
public class SurveyMonkeyPortletCommand
extends SabaWebCommand
{
public SurveyMonkeyPortletCommand()
{
addInParam("SurveyURL", String.class,
"The URL of the survey");
...
The public method invoked by the model page through the <wdktags:execute> tag is doExecute. The signature of this method is as follows, and the way to obtain the value of the input paramenter is through the getArt method.
public void doExecute(HttpServletRequest request,
IXMLVisitor visitor)
{
String surveyURL = (String)getArg("SurveyURL");
...
Inside this method, according to the value of the actionKey parameter, two different actions are performed, as described. The visitParams method is used for building the XML document that is returned to the model page.
if (actionKey.equals("Completed")) {
hidePortlet("Survey Monkey");
visitParams(visitor, "", "", "", null);
}
else {
Map<String, String> params = getParams(iframeParams);
Node message = getContent(messageXHTML);
visitParams(visitor, surveyURL, params, message);
}
This was the opportunity to describe an end-to-end development of a custom portlet that integrates with an external system and interacts with the user for collecting information and communicating with the back-end using a portlet command.
Happy Monkey Regards!
Stefano
So my new objective that I want to address in this post is to hide the portlet automatically and remove it from the user's dashboard as soon as he/she provides the response. In order to do that, I need to... invent a new bespoke portlet! C'est plus facile!
A new Survey Monkey portlet
Adding a brand new portlet to Saba is not complicated at all, as explained in this article already. But before getting into the technical details, let's set a few requirements for the new portlet, that will guide us in the development of the necessary code.- I want to specify the URL of the survey without bothering with the HTML code for the iframe that eventually displays the survey in my portlet (as instead seen for the Mashup article in my previous article).
- I still want control on the size of the area where the survey is displayed within the portlet, so I need a way to enter the width and height values of this window.
- I want to inform users, with a user-friendly message, possibly supporting text formatting, on what to do when they complete the survey.
- This will trigger an action that hides the portlet from the user's dashboard only. The portlet will remain visible to all other users who have not yet completed the survey.
Eventually, as a System Administrator, I want to be able to enter this data in the portlet parameters as in the following screenshot:
The code now. I will call my new portlet "Survey Monkey" (no surprise here!) and define the portlet manager as follows:
public class SurveyMonkeyPortletPageManager
extends AbstractPortletPageManager
implements PortletPageManager
{
@Override
protected void init()
throws SabaException
{
registerPage("showDefaultDisplay",
"/custom/portlets/surveyMonkeyPortlet.rdf");
The surveyMonkeyPortlet.rdf page is the controller for the Saba WDK pages that represent the portlet itself. More about this and the model and view pages later in this article.
Where are the parameters? Within the init method, simply add these additional lines of code to register the three parameters. Unfortunately, parameter names cannot contains spaces, so play with upper and lower case letters (aka Pascal case) for making these names readable.
ParameterDetail url = new ParameterDetail("SurveyURL",
TypeInfo.createStringType());
registerParameter(url);
ParameterDetail par = new ParameterDetail("IframeParams",
TypeInfo.createStringType());
registerParameter(par);
ParameterDetail msg = new ParameterDetail("MessageXHTML",
TypeInfo.createStringType());
registerParameter(msg);
That's all we need for the Java portlet manager. As we know, the portlet manager references the controller page (RDF page) that is responsible for orchestrating the communication flow between the model (XML page) and the view (XSL page). The controller references the other two pages with these simple WDK tags:
<wdk:model rdf:resource="surveyMonkeyPortlet.xml"/>
<wdk:view rdf:resource="surveyMonkeyPortlet.xsl"/>
The workflows
This is the process that I want to implement as a user experience for a System Administrator in Saba:- The new Survey Monkey portlet is created in the system, using the SurveyMonkeyPortletPageManager class as portlet manager.
- Externally, a survey exists or is created in Survey Monkey.
- The survey's URL is obtained and entered in the SurveyURL parameter of the portlet, along with the expected width and height of the area where the survey is displayed within the portlet. The accepted format is: width="nn" height="mm" where nn and mm are whole numbers. Unit indicators like % (percentage of the portlet size) and px (pixels) can also be used, as in, for example: width="100%" height="300px"
- The portlet is assigned to "mysaba" portal for making it available to end users in their dashboard.
- The survey is then launched and visible to end users, who can complete it at their next login to the Saba system. Instructions on what to do on completion of the survey are indicated within the portlet (see separate process for the end user). Once the survey is closed, which happens when either all participants have responded, or when the survey expires, the System Administrator can remove the portlet from the portal and hide it from end users.
For the end user, the process starts when the portlet is assigned to the portal and made visible in the Dashboard upon login:
- The user completes the survey and follows the instructions post completion. Instructions are entered by the System Administrator in XHTML format (i.e. with support for full text formatting including bold, italics, links, etc.) in the MessageXHTML parameter of the portlet.
- A "Hide" button exists in the portlet for automatically removing the portlet from the user's dashboard. This does not effect any other users.
The workflows for these two processes are implemented in the Saba WDK pages with the help of a special portlet command class that facilitates the communication between the model page and the database. This page is not strictly necessary, as all the business logic can be contained in the model page, but it is a commodity for encapsulating repeating code into one single specialised class, as we will see later.
The Model
The structure of the model is very straightforward:- The header <wdk:head> contains the input parameters received by the portlet manager
- The form <wdk:form> contains the XML model as obtained from the portlet command
- The widgets section <wdk:widgets> contains the Hide button
Java code can be injected in the model page by using the <xsp:logic> tag.
<xsp:logic>
SurveyMonkeyPortletCommand command =
new SurveyMonkeyPortletCommand();
</xsp:logic>
This simple line of code creates a new instance of the SurveyMonkeyPortletCommand class, to be used by the <wdktags:execute> tag for invoking the command class.
<wdktags:execute commandObj="command">
<param name="SurveyURL" expr="SurveyURL" type="String" mode="in"/>
<param name="IframeParams" expr="IframeParams" type="String" mode="in"/>
<param name="MessageXHTML" expr="MessageXHTML" type="String" mode="in"/>
<param name="actionKey" expr="actionKey" type="String" mode="in"/>
</wdktags:execute>
The command accepts the 3 portlet parameters, plus a fourth actionKey that I'll explain later. After execution, the command returns an XML model that is then transferred to the view for visualization to the end user.
The last section of the model identifies all widgets, i.e. visual controls, to display on the page. We only have one button to show to the user, to inform Saba the the survey has been completed. This is the Hide button that also triggers the action for automatically hiding the portlet from the user's dashboard without the user explicitly removes the portlet from the dashboard page. The tag for defining a button widget is <wdk:link> with type = button. On click, the portlet reloads and executes the command again, but this time, because of the actionKey parameter mentioned before, the outcome is different, as explained later for the command class.
The XML tags for defining the Hide button are as follows:
<wdk:widgets>
<wdk:link name="hideButton">
<id>hideButton</id>
<label>Hide</label>
<type>button</type>
<do>Completed</do>
</wdk:link>
</wdk:widgets>
The <do> tag is a shortcut for passing the actionKey parameter to the portlet.
The View
The view page offers two visual sections to the end user within the portlet:- A frame for displaying the survey.
- An section, that I decided to put just beneath the survey - but can be moved anywhere, for the instruction message to the user on what to do on completion of the survey. This section also contains the Hide button.
<iframe id="iframeSurveyMonkey">
<xsl:attribute name="src">
<xsl:value-of select="params/surveyUrl" />
</xsl:attribute>
The section below the survey, with the message, instead is contained within a <div> tag. The message itself can contain HTML tags for formatting the text, hence the XSLT code for rendering this message on screen relies on the <xsl:copy-of> tag that performs a copy of the message including all existing tags. Adding the Hide button widget relies instead on the ordinary <xsl:apply-templates> tag for selecting the widget matching the indicated name.
<div class="sbListText">
<xsl:for-each select="params/message/*">
<xsl:copy-of select="."/>
</xsl:for-each>
<xsl:apply-templates select="$wdkWidget[@name='hideButton']"/>
</div>
The Command
To complete the description of this portlet, the last section is dedicated to the portlet command. As said before, the command is instantiated and executed by the model page, which passes several parameters to the command class. Specifically, according to the value of the actionKey parameter, two completely different actions are performed:- actionKey does not have any value, i.e. it's an empty string: the command class parses the URL, width, height and message parameters and builds the XML model response. The portlet displays the survey and the user message.
- actionKey is equals to Completed: the command class hides the portlet by leveraging the services of the PortalClientManager class, which is part of the Saba API, and then returns an empty model to the portlet (at this point the model is irrelevant as the portlet has been removed from the user's dashboard).
The base class from which all commands should extend their definition is SabaWebCommand. The input parameters are to be defined in the constructor through the addInParam method.
public class SurveyMonkeyPortletCommand
extends SabaWebCommand
{
public SurveyMonkeyPortletCommand()
{
addInParam("SurveyURL", String.class,
"The URL of the survey");
...
The public method invoked by the model page through the <wdktags:execute> tag is doExecute. The signature of this method is as follows, and the way to obtain the value of the input paramenter is through the getArt method.
public void doExecute(HttpServletRequest request,
IXMLVisitor visitor)
{
String surveyURL = (String)getArg("SurveyURL");
...
Inside this method, according to the value of the actionKey parameter, two different actions are performed, as described. The visitParams method is used for building the XML document that is returned to the model page.
if (actionKey.equals("Completed")) {
hidePortlet("Survey Monkey");
visitParams(visitor, "", "", "", null);
}
else {
Map<String, String> params = getParams(iframeParams);
Node message = getContent(messageXHTML);
visitParams(visitor, surveyURL, params, message);
}
Conclusion
We put it all together and the end result is something similar to the following screenshot: a fully integrated bespoke portlet for Survey Monkey in Saba.This was the opportunity to describe an end-to-end development of a custom portlet that integrates with an external system and interacts with the user for collecting information and communicating with the back-end using a portlet command.
Happy Monkey Regards!
Stefano
Hi Stefano,
ReplyDeleteI am very new to the SABA world and I have been looking for a way to retrieve the logged in user name in a SABA portlet.
Could you please tell me if that is at all possible?
Thanks and Regards,
Abhijit