The In-Progress Learning Activity portlet that is available with Saba 5.x offers a compact view of your recent registrations to offerings and plans, but it lacks grouping courses by certification or curriculum when a registration is made for an offering of a course that is part of a certification or curriculum. Basically, the list of In-Progress activities displayed in the portlet is flat and sorted by date or alphabetically, as in the following picture.
But what if our training framework is strongly based on certifications for regulatory compliance, for example, and listing courses without a structure is simply not good enough? Or simply courses that form a more complex module have titles like a generic “Etiquette” that taken outside of its context (i.e. the certification) mean little to the student? What we want, in short, it’s something like the portlet in the next picture, a structured visualisation of course registrations.
A custom brand new portlet
How to turn the standard In-Progress portlet into the structured one, then? The answer is, guess what, to customise the original portlet and introduce grouping by retrieving the full structure of certification / path / module and courses.The layout of a portlet is controlled by a manager (a Java class) and a set of Saba pages. The manager class for the In-Progress portlet is called InProgressLearningActivitiesPortletManager, and you can find it in the com.saba.client.learning.blendedenrollment package of the Saba’s Java Application Archive (JAR).
Customising the manager class for the new portlet layout is very simple, as the full portlet framework is offered “for free” by the Saba API. What we need to do in our new Java class, which for convenience I’m going to call InProgressLearningActivitiesPortletManager2, is to extend the abstract manager AbstractPortletPageManager and implement the interface PortletPageManager. Then, simply define the init() method and register the Saba controller page (the one with .rdf extension) that is used to display the new portlet.
public class InProgressLearningActivitiesPortletManager2
extends AbstractPortletPageManager
implements PortletPageManager
{
protected void init()
throws SabaException
{
registerPage("showDefaultDisplay",
"/learning/portlet/inProgressLearningActivities2.rdf");
}
}
In the example above, the new controller page registered by the manager class is called inProgressLearningActivities2.rdf, and, as you can guess, it’s a copy of the inProgressLearningActivities.rdf page available out of the box in the system, with the necessary changes to reference the new portlet structure, as described in the next section.
Creating the structure
The controller page, in a pure MVC (Model – View – Controller) pattern, defines which page contains the model (i.e. the logic to retrieve data from the database and the list of widgets used for displaying it on the screen), which page is the view (i.e. the layout of all defined widgets to display), and the model object (i.e. the Java class with the actual business logic).This is an example of the inProgressLearningActivities2.rdf used for defining the new custom In-Progress portlet. Particularly relevant are the <wdk:model> tag that references the new model page with .xml extension, the <wdk:view> tag that references the new view page with .xsl extension, and the <wdk:modelObject> tag that references the fully qualified name of the model Java class.
<?xml version="1.0" encoding="UTF-8"?>
<?cocoon-process type="portlet"?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:wdk="http://www.saba.com/XML/WDK">
<rdf:Description id="41078" pageCategory="/learning/portlet">
<rdf:type resource="http://www.saba.com/XML/WDK/Control"/>
<wdk:version>1.0</wdk:version>
<wdk:model rdf:resource="inProgressLearningActivities2.xml"/>
<wdk:view rdf:resource="inProgressLearningActivities2.xsl"/>
<wdk:widgets rdf:resource="/wdk/xsl/widget/wdk_widgets.xsl"/>
<wdk:modelObject>com.saba.client.learning.blendedenrollment. InProgressLearningActivitiesModel2</wdk:modelObject>
<wdk:links> ... </wdk:links>
</rdf:Description>
</rdf:RDF>
Basically, we have a new set of Saba pages that defines the layout of the In-Progress portlet v2, a new portlet manager and a new model class. Where and how do we define the structure, then? That’s pretty simple: the structure of courses grouped by certification or curriculum (together: by education plan) is defined in the model class, and the layout of how they show up on the screen within the portlet is defined in the Table widget used by the Saba model and view pages to represent data in a tabular format.
Starting from the former point (the Table widget), its definition is in the model .xml page. The trick here is to receive some piece of information from the model class that tells me that the specific course being displayed in the portlet belongs to a certification or a curriculum. If so, then indent its visualisation on the right.
<wdk:table name="simpleTable">
<row path="InProgressLearningItems/LearningItem">
<column>
<xsp:logic>
// If the course is part of a certification or curriculum, indent the title in the display
if (WDKDomUtils.getNodeTextValue(wdkwidgetNode, "isInEducationPlan", wdkWidgetMaster.getXPathCache()).equals("true")) {
</xsp:logic>
<span class="indent"> </span>
<xsp:logic>
}
</xsp:logic>
You can read the code above in this way:
- Define the Table widget that displays course registrations in the portlet.
- For each row, the first column of the table contains a conditional logic depending on the value of the isInEducationPlan variable; this variable is defined in the model class (more later) and assumes value “true” if the course is part of a certification or curriculum, otherwise it is “false”.
- If true, add a <span> tag to the output for indenting the title of the course as it visually shows as belonging to a certification or curriculum. MVC-purely speaking, the HTML layout should be defined in the view .xsl page, but limitations in the Saba’s implementation of the MVC pattern allow for HTML code to be specified in the model page instead (purists are horrifying here now…).
The portlet model
Now that we have defined where to visualise the certification and course structure, we need to prepare this structure and assign the correct value to the isInEducationPlan variable. This variable is the key for building the structured layout; as said, when its value is “true”, which happens only for courses that belong to a certification, the portlet will display the course title indented under the certification itself. When “false”, the record to display is a certification itself (hence, no indenting is required) or is a loose course not part of any education plan.Adding the isInEducationPlan variable to the XML output that the model class transfers to the model page is a matter of “visiting” a new <isInEducationPlan> tag for each processed record. If the record is a course and it belongs to an education plan (i.e. either a certification or a curriculum), the value of isInEducationPlan is true, else it is false. It sounds complicated, and… it is :-). But let’s try to make it simple by first looking at the implementation code below. A few pointers: a model class should extend the abstract class AbstractModelObject and implement the doExecute() method. This method is invoked by the model page by passing the HTTP servlet request, which represents the context where the page operates, and an XML visitor, which is used to build the output information returned to the model page. Adding a new XML tag to the output is simply a matter of visiting a new tag and writing its value, by using the visit() method of the visitor object.
public class InProgressLearningActivitiesModel2
extends AbstractModelObject
{
public void doExecute(HttpServletRequest req, IXMLVisitor visitor)
{
BlendedEnrollmentViewUtil util = new BlendedEnrollmentViewUtil();
Collection items = util.getBlendedViewItems();
Iterator itemIter = items.iterator();
while (itemIter.hasNext()) {
BlendedAbstractDetail itemDetail = (BlendedAbstractDetail)
itemIter.next();
// item is a course and belongs to an education plan
if (itemDetail instanceof BlendedEnrollmentDetail &&
isInEducationPlan(itemDetail)) {
visitor.visit(null, "isInEducationPlan", "true");
}
else {
visitor.visit(null, "isInEducationPlan", "false");
}
}
}
}
Read the code above as follows:
- The BlendedEnrollmentViewUtil class provides a utility for retrieving In-Progress registrations for a given user as a collection of BlendedAbstractDetail objects.
- The code iterates over the collection and if the current item is an instance of the BlendedEnrollmentDetail object, i.e. it’s a registration to an offering for a course AND the course is part of an education plan, it creates an isInEducationPlan tag and assigns the value “true”.
- Otherwise, for any other item type and for courses that are not part of an education plan, the isInEducationPlan tag contains the value “false”.
Adding the new portlet to the platform
To add the new portlet to the system, simply follow these steps:- In System Administration > General Configuration > Portlets, add a new portlet. The important parameter to set is PageManagerClass, which should be the fully qualified name (i.e. including the package name) of the personalised portlet manager Java class.
- From the Portals page, add the newly created portlet to the “mysaba” portal (this is the default portal for learners that is displayed after log in).
All done, enjoy it!
Lijepi pozdrav,
Stefano
Hi Stefano and thanks for illuminating a means of improving the In Progress Learning portlet!
ReplyDeleteAs we're hosted with Saba, are there options for building this without having full access or would Saba have to implement? We have Sys Admin access only.
Also, what are your thoughts on adding the ability to have a Launch button next to the courses within the Certifications or Curricula? It would eliminate an extra button click for learners and probably improve the overall usability of the system.
Thanks again!
Jon
Hi Jon,
ReplyDeletethanks for your comment.
Creating a new portlet is considered a customisation, and as such I don't think it is possible to install customisations on an on-demand environment hosted by Saba.
The only option possible that I see is using a mashup portlet to invoke Saba web services for showing the wanted information, provided there are web services for in-progress registrations. That's actually a good idea for my next article! :-)
The Launch button is already part of the in-progress portlet for WBT (in my screenshot in the article, see the case of the "Time Travelling Explained" course). For ILTs, the link displayed is "View Details", which brings you to the detail page of the ILT offering. For WBTs, the Launch link is displayed instead, at condition that there is a content associated with the WBT, and the One Click Learning functionality is on.
Regards
Hi Stefano and thanks for following up. I suspected we were looking at a customization to implement the improved In Progress Learning portlet, but it is something we're investigating. I also noticed after I had posted that the Launch button was visible, so apologies for not paying close attention.
DeleteI for one, would enjoy reading about your views on web services for inclusion in the mashup portlet. We're using it now and are looking for ways to enhance the UI to improve the user experience.
Thanks again!
Jon
Hi,
ReplyDeleteGreat Article. I too would be very interested in how web services might be used. This is so obvious, I'm suprised Saba hasn't implimented it.
Andrew
Hi Andrew, thanks for your comment.
ReplyDeleteAn article on consuming Web Services from within a portlet is on my to-do list. Is there any specific Web Service that you are interested to see integrated in Saba, so I can pick it for my examples?
Regards
Stefano
Really, rudimentary knowledge gap...
ReplyDeleteCould you provide more direction on finding the JAR? Is that outside the System Admin privileges? Your instructions (and explanations for why!) are so good (and the need is so high for this sorting view), I think we should try this.
If you look on the Saba Web Server, i.e. that server where the Saba application is installed, within the installation folder of the Saba platform (typically /SabaWeb), there is a lib sub-folder.
ReplyDeleteIn the lib folder you can find the saba.jar file. This archive contains most of the Java classes representing the Saba's API, business logic, widgets, portlets, etc.
I do not recommend modifying this file. You'd rather prefer to create a custom JAR file that contains your own Java classes, and deploy it along with the Saba Enterprise Archive (saba.ear) file. Instructions on how to deploy the Saba application are on your Installation Guide.
Please consider that this activity is outside of the Saba System Admin possibility. A server administrator with access to the Saba server can perform these actions. Typically, deploying the Saba application requires also some downtime, unless a fault-tolerant cluster is used.
This approach works only if you have your Saba installation on premise. If you are using the Saba Cloud solution, you have no possibility to add custom Java classes to the system.
Regards
I have a behind the firewall Saba 6 environment that I would like to install the improved “In-Progress Learning Activities 2” portlet.
ReplyDeleteI looked through the above guide and have a few questions and was wondering if you might help me out:
Can you give an example of the inProgressLearningActivities2.XSL file that I will need? (I am assuming the XML code is included above?)
Are the code examples you give above complete or are they just portions or the RDF and XML files that I would need to have in place?
Can you give me the name of the Saba manual that I would find instructions on how to deploy a “custom application” or custom code?
Thank you for any assistance you can provide!
Hi there,
Deletethe source code presented in the article is just a portion of the entire solution. It would take too much space to publish the full source code. I try to identify the key snippets of code that are relevant and specific to the topic of the article and assume that who reads it is familiar with creating customisations in Saba.
I'm happy to share with you more insights about customising the Saba software. A good starting point is to read the Saba Application Developer Guide.
For Saba 6.1, you can find this document on the Partner Community portal. The document is called sec61_application_developer.pdf.
I am also considering adding a new Downloads section on sabaguru.com with the complete source code packages. I'm currently working on it, it won't take too long! :-)
Regards
Stefano
Stefano,
DeleteThank you for this information. Your response helps me a lot. I'll see if I can get it going in my environment.
Respect and that i have a tremendous offer you: How Many Houses Have Been Renovated On Hometown exterior home renovation
ReplyDelete