Wednesday, 28 August 2013

Summer Refresh of Saba Mobile Apps

It has been a busy summer for the Saba Mobile team. The full set of Saba mobile apps for Android and iOS, which includes Saba Cloud, Saba Meeting and Saba Anywhere, received new important updates.

The apps are available in the Google Play and Apple iTunes app stores.



Saba has refreshed the user experience of their apps to empower users to collaborate, share and consume their learning content anytime and anywhere on their mobile devices.

Here is a quick summary & highlights of some of the exciting enhancements:

  • Online & offline consumption of content on mobile
  • Ability to consume standards-compliant content on mobile devices: AICC, SCORM & Tin Can
  • Added support for multi-SCO and multi-module learning content
  • Ability to consume SCORM and file-based content online without having to download it first
  • Ability to play externally hosted content on mobile devices
  • Rock Star endorsements added to the activity stream
  • Ability to seamlessly launch Saba virtual classroom from Saba Learning App to either attend virtual sessions or launch session recordings


More information about this important release on the official Saba Blog.

Wednesday, 21 August 2013

Saba surveys in a portlet

Saba provides several portlets with the out of the box installation of the Learning Management System, but one missing and topping the list of requests from the customer community is a portlet for displaying surveys. If you have ever published a survey, you know how little accessible surveys are to end users: click on My Learning tab, then Evaluations & Surveys menu and again on Surveys, then eventually you see a list of your surveys. Not great, if what you need is to draw the attention of your student to a public survey. What's better page than the student's dashboard page then!

In my previous article, I've already addressed a specific case of integrating a third party survey tool in a portlet for publishing quick and anonymous surveys. In this article I'll present an easy technique for publishing the Saba built-in surveys in a portlet and make them accessible to end users in just a click!

Overview of Surveys

Saba provides a framework for obtaining feedback from learners in the form of surveys. A survey is a questionnaire with one or more questions that is used to gather feedback from specified respondents. Surveys can be assigned to both internal people (i.e. employees) and external people (i.e. clients). Designated respondents can always choose to accept or decline a survey.

The stages and roles involved in creation and delivery of a survey are:

  • Creation: People Administrators create a survey, attach a questionnaire, define the distribution list, and publish it. Survey is sent to participants in the distribution list for approval and assessment.
  • Progression: Raters accept the survey request and complete the survey or decline the survey request. A participant can also choose to remain anonymous, but answer the survey.
  • Completion: Saba automatically accumulates the assessment results and sends a survey closure notification to the owner after the expiry of the due date.

The Survey Management page displays a list of all surveys and their statuses. A survey can have the following statuses:

  • Draft: A survey is created and is saved for later edits, not published.
  • In Progress: A survey is published and sent to respondents.
  • Completed: A survey is closed.

Once a survey is published, it is available in the My Learning > Evaluations & Surveys tab of the Home module to learners designated as respondents for the survey. The respondents can choose to accept or decline the survey, and, if the former, then eventually take and complete it by answering all questions in the survey.

The Survey Portlet

The purpose of the survey portlet is to list In Progress surveys to nominated users directly in their home dashboard. Draft and completed surveys won't be displayed. A portlet should be used for providing a shortcut to a more structured and complete area of the Saba system, and it should not be used as a replacement for an existing functionality. For this reason, I decided to create a very specific version of the portlet with some reduced functionality (as compared to the regular page from where users can access surveys), but with the added benefit of fitting within the limited space of a portlet and offering a one-click access to published surveys.

The starting point of any new portlet, as we learned in my previous article, is to create a Portlet Page Manager. This is a Java class that extends the AbstractPortletPageManager interface and implements the PortletPageManager abstract class. By doing this, we have all the wiring ready, and what is left to do is simply to override the init method and specify the Saba WDK page that is responsible for displaying the actual portlet on the screen.

public class SabaSurveyPortletPageManager
   extends AbstractPortletPageManager
   implements PortletPageManager
{
   @Override
   protected void init() throws SabaException
   {
      registerPage("showDefaultDisplay",
         "/custom/portlets/surveysForRater.rdf");
   }
}

The fully qualified name of the SabaSurveyPortletPageManager class will be used for the PageManagerClass parameter when creating a new portlet in Saba.



The name of the controller page is /custom/portlets/surveysForRater.rdf. This is not coincidental. The regular Saba page that is used for displaying surveys to end users is /common/survey/surveysForRater.rdf. All I have done has been to simply make a copy of that page into the /custom/portlets folder of the alternative source code and then modify it to fit the purpose of the survey portlet.

If you look at the standard Surveys page, you will notice that the page displays survey information that we can hide in our portlet, to save space and reduce the clutter on the screen.
Specifically, we do not need the Current and Completed tabs, as we are only displaying In Progress surveys. Also, in the survey list, we probably can get along without Description, Created On and Created By. We also do not want the user to modify, print or export the list of surveys, so we can get rid of these links on the top right corner of the survey table.

This is the original page with all the mentioned columns and links:


And this is the layout of the portlet that we want to obtain:


Let's go in order then and remove the tabs on top that allow to switch between Current and Completed surveys. When doing this kind of page "surgery", I typically look first at the view page (the page with the .xsl extension) and see where that control is displayed:
Within the surveysForRater.xsl page I can easily spot these lines of a table row:

<tr>
   <td colspan="2">
      <xsl:apply-templates select="$wdkWidget[@name='surveyStatusMultiSelect']"/>
   </td>
</tr>

That's your widget. Copy the surveyStatusMultiSelect name, we need it later, and delete all the above lines from your view page. Job done!

Now let's find this control in the model page (the page with the .xml extension) and remove it from there as well. Search for "surveyStatusMultiSelect" in the surveysForRater.xml page until you find this line:

<wdk:multisection name="surveyStatusMultiSelect">

That's your multi-section widget. Feel free to remove the entire tag, including its content. And again job done!

Now the columns. To get rid of extra columns in the survey table, we have to look for a the FinderResult widget identified by the <wdk:finderResult> tag, within the <wdk:widgets> section.

<wdk:widgets>
  <wdk:finderResult name="surveyResults">

This is the widget that displays the list of surveys assigned to a user in a table format. There is a special instruction that you can use for removing an existing column, which is applied to the widget by entering as many <removeDisplayColumn> tags as needed. To remove the Description column, for example, just add the following XML tag to any place within the FinderResult widget (I typically add these instructions after the list of columns to display and before the definition of the sorting order, but the actual position is not really relevant).

<removeDisplayColumn>
  <name>description</name>
</removeDisplayColumn>

Repeat the same instruction also for the Created On and Created By columns and, congratulations, job done!

Lastly, we want to remove the links to print, export and modify the table. There are specific instructions for that, as documented in the Saba Application Developer's Guide, that you can apply by specifying the following simple tags:

<print>false</print>
<export>false</export>
<modifyTable>false</modifyTable>

Once again, job done, all changes have been applied to the model page.

The final touch

Are we done then? Well, yes and no. Publish these pages and launch a survey. You will see a pop-up like this one opening, containing all sections and questions of the survey.



When you submit (or exit) this pop-up, Saba refreshes the launching page (i.e. the page from where you clicked on the Launch link) to reflect any changes introduced by completing the survey (very likely, if you complete the survey, its status moves to Completed and therefore it disappears from the list of In Progress surveys in the portlet). If you launch the survey from within the portlet and then close the survey pop-up window, however, the launching page reloads but a wrong page is displayed. Actually, it's not a wrong page but it is the standard survey page that you typically find in My Learning > Evaluations & Surveys > Surveys. So what's happening is that the pop-up window doesn't know whether the survey has been launched from the standard page or from the portlet. Reason for that is that... ehm... unfortunately, the address of the launching page has been hard-coded in the view page (bad bad practice Saba, come on!) so we have to modify it to make it work for the portlet and have the dashboard page reloaded (and hence our survey portlet with it) rather than the standard survey page.

Look for this JavaScript code in the view page, that performs the refresh of the launching page:

function submitForm()
{
wdkFrameworkSubmit("/common/survey/surveysForRater.rdf");
}

As you can see, the full name of the /common/survey/surveysForRater.rdf page is specified, which, as we know, is the standard page for displaying surveys to users.
We want to replace it with our portlet page, but we cannot just specify the full name of the /custom/portlets/surveysForRater.rdf page here, otherwise we would reload only the portlet page outside of the dashboard portal. We do need to specify the portal page that, once refreshed, will reload all portlets in turn.

function submitForm()
{
  wdkFrameworkSubmit("/platform/presentation/portal/portalDriver.rdf");
}

That works, but yes, it still stinks of hard-coded page name in a view page, which is against the MVC principles (page navigation should only be responsibility of the controller page, and never of the view). A more elegant way to resolve this problem is to introduce a link reference in the controller page and have the view point to that reference rather than the full survey page name. But this is another story...

Wednesday, 14 August 2013

An Enhanced Survey Monkey portlet

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!

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.
  1. 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).
  2. 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.
  3. I want to inform users, with a user-friendly message, possibly supporting text formatting, on what to do when they complete the survey.
  4. 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.
In order to accomplish all these requirements, we need a new portlet that supports 3 parameters for entering the survey URL, width and height and user message.
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:
  1. The new Survey Monkey portlet is created in the system, using the SurveyMonkeyPortletPageManager class as portlet manager.
  2. Externally, a survey exists or is created in Survey Monkey.
  3. 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"
  4. The portlet is assigned to "mysaba" portal for making it available to end users in their dashboard.
  5. 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:
  1. 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.
  2. A "Hide" button exists in the portlet for automatically removing the portlet from the user's dashboard. This does not effect any other users.
This step is necessary because there is no way in Saba to monitor the completion of the survey in Survey Monkey. The responses provided in the survey are sent to the Survey Monkey server from the page embedded in the portlet, with no interaction with the Saba system. Therefore, there is no possibility for the Saba system to know when the user actually completes the survey. The only option available is to let the user mark their survey complete by clicking on the Hide button explicitly.


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
Just a quick word about the form section, which is internally made of two subsection for the logic part (i.e. the Java code to invoke the portlet command) and the actual model.
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:
  1. A frame for displaying the survey.
  2. 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.
The frame for displaying the survey is actually an inline frame, whose properties are defined as attributes of the <iframe> tag by assigning the pertinent value as received from the model page. The XSLT code below defines the src attribute. The other attributes can be defined in the same way.

<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:
  1. 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.
  2. 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).
A few snippets of the command's class source code.
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






Thursday, 1 August 2013

A Survey Monkey portlet

Ever thought about submitting a quick anonymous survey to all your students in Saba and make it available in a portlet on the person's dashboard?
The Saba's own survey tool is not quite fit for the purpose, not last because it requires a content administrator in Saba to create the survey itself.
What we really want, instead, is a very simple and immediate survey that even your CEO can create and submit in minutes.

Tools like Survey Monkey (www.surveymonkey.com) suits perfectly the need and the good news is that you can integrate a survey in Saba and display it within a portlet in just minutes!

The Mashup portlet

All the wiring of the Survey Monkey survey with Saba is done in a mashup portlet. Mashup portlets display data retrieved from an external site using JavaScript and HTML.
Basically, a mashup portlet is a container of HTML and JavaScript code that can be embedded in a Saba portal. The page manager class is com.saba.client.portal.MashupPortletPageManager.
With this in mind, adding a new mashup portlet to an existing portal is as simple as an ABC (and therefore I won't bore you with the details).
For reference, the next picture shows the details of my Survey Monkey mashup portlet as configured in my environment.


Not in the picture, there are two extra parameters that can be configured for defining the content and layout of the mashup portlet:
  • JSFunc: This parameter defines the JavaScript expression that is executed on page load, i.e. when the portal completes loading the mashup portlet. Please note, the Saba 5.5 System Administration Guide reports an example of a JavaScript function specified for this parameter. Unfortunately, this is not correct. The JSFunc parameter accepts immediate JavaScript commands and not functions. The good news is that... we don't need to configure anything here, the Survey Monkey survey will open automatically inside the portlet container, we don't need to specify any JavaScript action occurring after the portlet loads.
  • XHTML: This is where the HTML code goes, which defines the content and layout of the portlet. This must be a properly formatted XHTML code, so all tags should have an opening and a respecting closing tag (i.e. <p> and </p>). Also, attributes should be quoted (<div width=100> is not acceptable, it has to be <div width="100">). We will use this parameter to embed the Survey Monkey survey in our mashup portlet.

Putting a Survey Monkey in a portlet

Probably you have already used Survey Monkey in the past, so you already know how to create a survey in there. What you need to do now is simply obtaining the survey web link and use it for defining the survey to display in the portlet.

The survey web link comes in the form http://www.surveymonkey/s/XXXXXXX, where XXXXXXX is a 7-character code the uniquely identifies the survey, as in the picture below.


All we need to do now is to embed this survey within the mashup portlet. We do this by using an iframe configured as follows:

<iframe src="http://www.surveymonkey.com/s/6S9Y5Y5" width="100%" height="300px"></iframe>

The src attribute clearly references the survey web link. The iframe width is set to 100% so it can take all the horizontal extent of portlet. The height attribute instead is fixed to the minimum height you want to set for your survey showing inside the portlet's frame. 300 pixel is a reasonable size, but make your own tries to figure out which height best fits your survey.

Just add that piece of HTML to the XHTML property of the mashup portlet, save, add the portlet to a portal and voilĂ  job done!


There's margin for improvement, though. Once the survey is answered, Survey Monkey displays a promotional page instead. You may not want your users to see this page. Unfortunately, portlets cannot be assigned and removed on a user-by-user basis. The only workaround viable at the moment is that the user themselves removes the portlet from their dashboard, or for the administrator to wait until the survey closes and then remove the portlet from the portal.

An enhanced version of the Survey Monkey portlet will be presented in the next article, with better control on exactly this scenario when a user has completed the survey.

Regards,
Stefano