Passing a SSJS function to a custom control

Introduction

I am building a custom control that mimics the viewPanel control but it is a repeat control which data is not a view but a managed bean that returns an arraylist of java objects (representing Notes documents).

I am using a Bootstrap table for display I in a previous post I have demonstrated how I can provide a JSON object to have flexibility in the columns I want to display and the values I want to display.

Next step is the option to provide custom actions to the custom control, in a way that the code under an action button in the custom control is provided via a property. So in one case hitting the button could print all selected documents, in the other case it could remove all documents from the database.

Step 1 – Creating an arraylist of unid’s

Key here is that I have an arraylist of unid’s to work with. The technique how to select documents in a repeat control was demonstrated by David Leedy in Notes in 9 episode 25. Instead of buttons I use a checkboxgroup:

So now I want from my xp:button in my custom control do something with this arraylist of unid’s…

Step 2 – set up the property definition

Next step is to setup the property in the property definition of the custom control:

Not the most common type of class and editor you use for a property.

Step 3 – setup the event handler for the button

In order to have the button to understand that the action to be performed is coming from a action property we need to specify that in the onClick event:

(Note: my property resides in the group property actionButton)

Step 4 – set up the SSJS  function you want to run

So now our button knows it’a action comes from a propert we need to write the SSJS function for the button. I have placed it in a SSJS script library. Here is an example to remove documents from a database:

Step 5 – Add the SSJS to the property

The last step is the most tricky one. In the property you are not allwed to provide any parameters or parentheses for the SSJS function. So our action property becomes as followed:

Result

As a result I have the following UI:

  • A custom control with a button which onClick action is provided via a property on that host xpage.

IBM Champion Nomination

Is this blog-article useful to you? Perhaps you can nominate me as IBM Champion.

Advertisements

A generic approach to display Notes data via a Bootstrap table

This week I became inspired by a question I noticed on Stackoverflow regarding collecting values from Java objects.

For a project we were discussing what to use for display “Notes View data”:

  • jQuery DataTables plugin and use a customRestService via a Java class as data provider for the JSON.
  • a Repeat control and display an Arraylist of Java objects.

Some of the participants liked the jQuery approach because it provides a lot of functionality out of the box (sorting, search, responsiveness,…) and their lack in knowledge regarding Java.

Others were questioning of the Repeat control approach would be flexible enough so we could display easy different sets of data with different number of columns.

So… to the drawing board.

Mostly in Notes views (e.g. via the View Panel control) one column serves as link (often the first) and all others just display data of all kind. In the DataTables plugin the display is flexible because you can define a custom render function for each column which is great. So you could display a button that will call a dialog to interact with the underlying document for example. However this custom render definition resides in a CSJS library so becomes part of the design.

I had already a custom control that could consume properties for the data and how to set a fixed set of columns. So the last part I needed to make flexible.

Because I have an arraylist of Java objects as source for my Repeat control I need to access the fields “on the fly” when rendering the column values. This turned out to be quiet simple because the fields are already in my Java object.

In my approach I provide the columns via a JSON object that could look as followed:

dyntable00

 

And for my columns I repeat it and collect the value from my underlying Java object:

dyntable01

Note: my code is not final, I would to define more types of data and the option to provide a custom render function for each column. So far I only have computation for string and date fields.

I also applied some basic functionality as Pager controls (top + bottom), a Page Sizer control, icon display and row numbering.

Here is what a result might look like:

dyntable001

Nothing special but now now I have just one custom control to display 80% of my tables/views.

Here are the GIST files for the custom control and its configuration xml file.

IBM Champion Nomination

Is this blog-article useful to you? Perhaps you can nominate me as IBM Champion.

Using the beanNamePicker dataProvider for a namepicker

For an application I needed to provide a namepicker with data from a NotesView. Unfortunately I could not use the default dominoViewNamePicker option as the data provider because the column to select from may not be a multi-value field or contain multiple fields. 😕

A search on google provided me an example how to use a beanNamePicker that will read the data from Domino Directories.

I created a GIST for the XPage: https://gist.github.com/PatrickKwinten/0bcc951a8eb1f5ef287c0c9b05a535a3 and for the Java class it uses: https://gist.github.com/PatrickKwinten/4aa15ea09717833bcc9fd1a941d9e5a8 .

Basically here is how you set it up:

<xe:namePicker id=”npDomino” for=”person1″>
<xe:this.dataProvider>
<xe:beanNamePicker dataBean=”org.wordpress.quintessens.demo.app.NamePickerDirectory”
loaded=”true”>
</xe:beanNamePicker>
</xe:this.dataProvider>
</xe:namePicker>

Key is that you return an object of type SimplePickerResult containing a list of objects of type <IPickerEntry>.

For my case I needed to go to a view where all the activities for users for the application where logged. Again I used a similar setup:

<xe:namePicker id=”npDomino” for=”person1″>
<xe:this.dataProvider>
<xe:beanNamePicker dataBean=”org.wordpress.quintessens.demo.app.NamePickerView”
loaded=”true”>
</xe:beanNamePicker>
</xe:this.dataProvider>
</xe:namePicker>

But now I go to a different class which I created a Gist for: https://gist.github.com/PatrickKwinten/c3580344a48704b3589251c0d5bbb8e5 .

Key difference is that my data source has become a Notes view and I check the type of column (columnValue instanceof String, ArrayList, Vector) because I have different type of fields and values in my first column.

So once you understand how you can extend the basic functionality within XPages you gain much flexibility and new opportunities with out of the box controls.

Please IBM provide us with more examples!!!

IBM Champion Nomination

Is this blog-article useful to you? Perhaps you can nominate me as IBM Champion.

 

 

XPages sufficient for line of business type of applications?

Hi there, currently I am following another Angular course since it seems to have become the leading development framework at work. So back to learning all the rules within Angular.

At the moment I am modernizing a Domino application with the help of XPages which:

  1. Implements Model-View-Controller architecture, mostly inspired by the guys at Pipelia since IBM never told us to do so.
  2. Is written in Java to support the MVC architecture and to have close integration with XPages runtime.
  3. Is using Expression Language wherever possible to avoid usage of SSJS.
  4. Uses the lifecycle of JSF in XPages at the max.
  5. To cover support for different devices I am using Bootstrap as front-end framework. So I miss some native behavior which I do not tend to cover-up.

So far so good and I think I have come quiet long in my project so I still dare to call it rapid application development.

The code-base has been reduced dramatically and all exotic upcoming JavaScript libraries from the early 2000 I have been able to replace with just XPages. With my latent UX skills and extending the out of the box Bootstrap I might now even call this application ‘sexy’ 🙂

I know I haven’t touched many areas discussed in the XPages community such as:

  • Websockets (I do not see a use-case yet).
  • Writing Java servlets (please pass me a demo NSF).
  • Watson services (cloud is still a sensitive topic).
  • set up micro-services with smartNSF and consume them in my Java code with an mapper library (requires changes in the environment).
  • Integration with IBM Connections.
  • Redefining my data with the help of a Graph DB.

Either I see little usage, it is not possible or there is no-one to guide me (the information is certainly not provided by the vendor).

So now back to Angular. Learning all these rules, technologies and new tools setup I was wondering what new technical options this framework will bring me at work. Reflecting on the type of customer-orders I receive I am wondering:

Is XPages not sufficient for most of your line of business apps?

Perhaps you have a though about this?

Happy development & enjoy your summer 🙂

Using Apache POI to create Presentations from your XPages application

Introduction

Apache POI is perhaps THE Java API for Microsoft Documents, if you do not live in the Microsoft bubble. It inspired people to build the Poi4XPages plugin to easily generate Word and Excel Files out of XPages (using data from Notes documents).

Yeah but Microsoft Office has so many other great types of documents you might think. Take for example PowerPoint presentations. Have you ever met a sales guy without one?

sample_layout

It turns out that it not that hard to use Apache POI to create powerpoint slides. I will describe how you can start developing your own solution in a few steps.

Step 1 – Download and install Apache POI

You have the link so download the library. While I was developing my application a new release (v3.15, September 21) came out so this project is alive an kicking.

I added the following jar files to my webcontent/web-inf/lib folder and added them to the build path so they appear as referenced libraries:

ref_libraries

Step 2 – Write some code

In my application I want to use data that is stored in Notes documents in the presentation slides. I also want to import a template first, so I have some styling for the slides e.g. background image.

Note that I am not a PowerPoint export, so my template might be far from correct. I store the presentation in my NSF so in order to import it into my PPT object I am using Sven Hasselbachs Domino Napi Utility class. For this class I need to add the lwpd.domino.napi.jar file on my Domino server to the build path of my application,

With this in place I could write my Output class:

package org.quintessens;

import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Vector;

import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.servlet.ServletOutputStream;
import lotus.domino.NotesException;
import lotus.domino.Session;
import lotus.domino.Document;

import org.apache.poi.xslf.usermodel.SlideLayout;
import org.apache.poi.xslf.usermodel.XMLSlideShow;
import org.apache.poi.xslf.usermodel.XSLFHyperlink;
import org.apache.poi.xslf.usermodel.XSLFSlide;
import org.apache.poi.xslf.usermodel.XSLFSlideLayout;
import org.apache.poi.xslf.usermodel.XSLFSlideMaster;
import org.apache.poi.xslf.usermodel.XSLFTextBox;
import org.apache.poi.xslf.usermodel.XSLFTextRun;
import org.apache.poi.xslf.usermodel.XSLFTextShape;

import com.ibm.domino.xsp.module.nsf.NotesContext;
import com.ibm.xsp.webapp.XspHttpServletResponse;

import ch.hasselba.napi.NAPIUtils;

public class Output {

NotesContext nct = NotesContext.getCurrent();
Session session = nct.getCurrentSession();

public void createPptx(String docId) {
System.out.println(docId);
try {

Document doc = session.getCurrentDatabase().getDocumentByUNID(docId);
DateFormat dateFormat = new SimpleDateFormat(“yyyy/MM/dd HH:mm:ss”);
Date date = new Date();
System.out.println(dateFormat.format(date));
System.out.println(doc.getUniversalID());

XMLSlideShow ppt;
try {
ppt = new XMLSlideShow( NAPIUtils.loadBinaryFile( session.getServerName(),session.getCurrentDatabase().getFilePath(), “Duffbeers.pptx”));
//System.out.println(ppt.getProperties());
int slideCounter = ppt.getSlides().size();
System.out.println(“contains ” + slideCounter + ” number of slides”);
// first see what slide layouts are available :
System.out.println(“Available slide layouts:”);
for (XSLFSlideMaster master: ppt.getSlideMasters()) {
for (XSLFSlideLayout layout: master.getSlideLayouts()) {
System.out.println(layout.getType());
}
}

XSLFSlideMaster defaultMaster = ppt.getSlideMasters().get(0);
XSLFSlideLayout defaultLayout = defaultMaster.getLayout(SlideLayout.BLANK);

/*
* Add a slide based on TITLE_ONLY layout
*/
defaultLayout = defaultMaster.getLayout(SlideLayout.TITLE_ONLY);
XSLFSlide slide01 = ppt.createSlide(defaultLayout);
XSLFTextShape title01 = slide01.getPlaceholder(0);
title01.setText(doc.getItemValueString(“refName”));

/*
* Add a slide based on TITLE_AND_CONTENT layout
*/
defaultLayout = defaultMaster.getLayout(SlideLayout.TITLE_AND_CONTENT);
XSLFSlide slide02 = ppt.createSlide(defaultLayout);
XSLFTextShape title02 = slide02.getPlaceholder(0);
title02.setText(“Strength”);
XSLFTextShape body02 = slide02.getPlaceholder(1);
Vector < String > strength = doc.getItemValue(“refStrength”);
for (int i = 0; i < strength.size(); i++) {
body02.addNewTextParagraph().addNewTextRun().setText(strength.get(i));
}

/*
* Add another slide based on TITLE_AND_CONTENT layout
*/
XSLFSlide slide03 = ppt.createSlide(defaultLayout);
XSLFTextShape title03 = slide03.getPlaceholder(0);TutorialsPoint
title03.setText(“Weakness”);
XSLFTextShape body03 = slide03.getPlaceholder(1);
Vector <String> weak = doc.getItemValue(“refWeakness”);
for (int i = 0; i < weak.size(); i++) {
body03.addNewTextParagraph().addNewTextRun().setText(weak.get(i));
}

/*
* Add another slide based on TEXT layout
*/
defaultLayout = defaultMaster.getLayout(SlideLayout.TEXT);
XSLFSlide slide04 = ppt.createSlide(defaultLayout);
XSLFTextShape title04 = slide04.getPlaceholder(0);
title04.setText(“Link(s) of interest”);TutorialsPoint
XSLFTextShape body04 = slide04.getPlaceholder(1);
body04.clearText();

XSLFTextRun textRunLine2 = body04.addNewTextParagraph().addNewTextRun();
textRunLine2.setText(doc.getItemValueString(“refLinkLabel”));
XSLFHyperlink link04 = textRunLine2.createHyperlink();
link04.setAddress(doc.getItemValueString(“refLinkURL”));

// 0-based index of a slide to be removed
for (int i = 0; i < slideCounter; i++) {
ppt.removeSlide(0);
}

String filename = doc.getItemValueString(“refName”) + “.pptx”;

TutorialsPointFacesContext fc = FacesContext.getCurrentInstance();
ExternalContext ex = fc.getExternalContext();
XspHttpServletResponse response = (XspHttpServletResponse) ex.getResponse();

try {
ServletOutputStream writer = response.getOutputStream();
response.setContentType(“application/vnd.openxmlformats-officedocument.presentationml.presentation”);
response.setHeader(“Cache-Control”, “no-cache”);
response.setHeader(“Content-Disposition”, “inline; filename=” + filename);
response.setDateHeader(“Expires”, -1);
ppt.write(writer);
writer.flush();
writer.close();
fc.responseComplete();
} catch (Exception e) {
e.printStackTrace();
}

} catch (NotesException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}

doc.recycle();

} catch (NotesException e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
}
}
}

 

In this example I am creating new 4 slides, with different layouts and content. You can be creative as you like and for example format text and present them as hyperlinks or include images.

Step 3 – Register a managed bean and call the code

With the java class Output written I can register it as a managed bean in the faces-config file:

<managed-bean>
<managed-bean-name>Presentation</managed-bean-name>
<managed-bean-class>org.quintessens.Output</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>

This makes it easy to call the code from anywhere. The only think I need to provide is an universal ID of a document:

<xp:button id=”button4″ value=”Download as Presentation” styleClass=”pull-right btn-primary”
style=”margin-right:5px;”><i class=”fa fa-slideshare” aria-hidden=”true”></i>

<xp:eventHandler event=”onclick”
submit=”true” refreshMode=”complete”>
<xp:this.action><![CDATA[#{javascript:Presentation.createPptx(ReferenceController.objectId);}]]></xp:this.action>
</xp:eventHandler>
</xp:button>

In my button the ReferenceController.objectId will provide the universal Id of a Notes document. I am using a controller class to get the value from a document, instead of a DominoDocument data-soruce.

With this in place. We are basically good to go! With just 1 click any sales guy can now create a corporate presentation from a Notes document and start spreading the word!

result

If you want to know more about Apache Poi, Powerpoint files and XPages I am happy to answer your questions. I can also recommend to read the XSLF Cookbook or the Apache Poi PPT tutorial on TutorialsPoint.

Add 20 years of experience to your workforce

You can 20 years of experience within IBM Notes and Web development to your workforce by hiring me.

Interested? Read my curriculum vitae on LinkedIn: http://www.linkedin.com/in/patrickkwinten and get in contact.

I am happy to work WITH you !

Reading files stored in the NSF webcontent folder

In an XPages application I am creating presentations (openxml presentations is perhaps a more correct defintion I guess).

Since I would like to use the template for house-styling I have to read it in from somewhere and use it in my code. I do not want to go for the option to read it from a server location, so after a while I ran into Sven Hasselbach’s NAPIUtils class available on GitHub.

If you search a bit further I found this handy description on Notesin9 how to add the lwpd.domino.napi.jar file to the build path of your application, which is required.

What the NAPIUtils class makes possible is to read files that reside inside your NSF e.g. the webcontent folder.

Just call something like

var fileIn:java.io.FileInputStream = NAPIUtils.loadBinaryFile(database.getServer(),database.getFilePath(),”your_file_in_webcontent_folder.correctextension”)

And you have a your file in a FileInputStream!

In my case I am using

var inputstream = NAPIUtils.loadBinaryFile(database.getServer(),database.getFilePath(),”Duffbeers.pptx”);
var ppt: XMLSlideShow = new XMLSlideShow(inputstream);

And I have my presentation template residing in my NSF in my XMLSlideshow object =)

So no more hustling and asking your administrators for a folder on your Domino server to read/write to.

Ofcourse, they probably have to change the java security settings, but that is another discussion. 😕

In some cases you do not want users to have access directly to an URL of a resource. In such cases you could read the file in and write it out in the browser. E.g.:

<?xml version=”1.0″ encoding=”UTF-8″?>
<xp:view xmlns:xp=”http://www.ibm.com/xsp/core”&gt;
<xp:this.beforeRenderResponse><![CDATA[#{javascript:importPackage( ch.hasselba.napi );

var exCon = facesContext.getExternalContext();
var response = exCon.getResponse();
var out = response.getOutputStream();
try {
response.setContentType(“application/octet-stream”);
response.setHeader(“Content-Disposition”,”attachment;filename=cv_patrick_kwinten.pdf”);
response.setHeader(“Cache-Control”, “no-cache”);
var fileIn:java.io.FileInputStream = NAPIUtils.loadBinaryFile(database.getServer(),database.getFilePath(),”Patrick_Kwinten_visualcv_resume.pdf”)
var buffer = new byte[10000];
var len;
while ((len = fileIn.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
} catch (e) {
// some basic error logging here…
} finally {
if (fileIn != null) {
fileIn.close();
}
if (out != null) {
out.close();
}
facesContext.responseComplete();
}}]]></xp:this.beforeRenderResponse>
</xp:view>

Happy development =)

Add 20 years of experience to your workforce

You can 20 years of experience within IBM Notes and Web development to your workforce by hiring me.

Interested? Read my curriculum vitae on LinkedIn: http://www.linkedin.com/in/patrickkwinten and get in contact.

I am happy to work WITH you !

A modern commenting view in XPages

Introduction

In the old days you a typical commenting feature was something with a form and a view. Typically you could read a list of comments in a historical (flat) order and on the bottom you had the form to enter your comment. Something like in the IBM Domino blog template.

domino-blog

With the rise of social and mobile it became more important to be able to comment-only so the form moved to the top and the list of comments became sorted in the opposite order (Youtube).

Discussions are still displayed in a thread but since threads are less friendly on a mobile device a more what’app like chat display. But these are more instant messaging apps, similar to Slack or IBM’s upcoming Toscana.

slack

My idea: welcome to the bubble

So what can/should we do to modernize the display of the comment feature in our Notes/XPages application?

Well mainly we still have a top level object (TLO) where people comment on. To stimulate people to comment the form should be close to this TLO. We can promote the interaction between a user and others and provide some form of “bubble speech” experience and (ofcourse) the option to “like” a comment.

That would bring us to something more like this:

social_comment

 

Notice on the left you see the comments I have made and on the right comments made by others. The latest comment is displayed first and mines are a bit more outstanding using a background-color.

Now how it’s made

To come to this solutions I am using the following technologies:

  • XPages
  • Java
  • SSJS
  • Bootstrap
  • PrettyTime
  • CSS
  • JavaScript / mention JS

Note that in a previous post I described how to add @mention autocomplete like Facebook, Twitter & Google+ to your XPages app. So I will not discuss in this post how to build the form to comment.

Here is a general outline of what we are going to build:

outline

Step 1 – basic structure; a repeater with a panel

step01

First I have a repeat control which is bound to an ArrayList containing JsonObjects. This approach you can find in my Bildr project on OpenNTF (shameless plug).

I also added an infinite scroll function. You can read about how to set that up in this post.

The panel has a computed styleclass. The style defintion “right” does nothing more than a  text-align: right. This makes sure the text in the content is … aligned right.

Step 2 – adding a user icon

Humans are visually oriented and idle so nothing wrong making our display attractive with a user icon.

step02

In this example I am just displaying some random icons, but if you have a corporate directory with user profiles containing icons you should embed them here.

The pull-left and pull-right classes are from Bootstrap. My comments are pulled to the left, comments from others to the right.

Step 3 – the media body

Next item that we add is a div with a media-body class. It contains out of 2 sections: the content of the comment (text written) and some meta-data (date, author). I also added a “like” link which is inspired by Thomas Adrian’s Intrapages app also available on OpenNTF.

step03

(Guess I don’t need the attributes here…). Here is the part of the meta-data:

step03b

First I display the Author name, only for comments written by others. In order to make the date more human friendly to read I am using PrettyTime. In the past I have blogged about how to use it.

For the remove link I am using the userbean to check if I can delete documents in the database and if the comment is mine: (@UserName() == obj.From) && (userBean.canDeleteDocs == true)

For the event I check and ask if I am sure that I want to remove the entry and then I simply remove it by UNID:

step03c

For the “like” button I would like to point to Adrian’s example in his Intrapages project. I made however the exeption to store the likes in a separate document, so not in the comment. By this way I can tighten my security model. Everybody has the role of [liker] and author access. So everybody can update a like document and not the original comment document.

The like function uses the cookie management principles described here. It stores the username:

userCookie = new javax.servlet.http.Cookie(“userid”, @UserName());

I have set the duration of the cookie as long as the session is active:

//Cookie.setMaxAge(-1) will make the cookie expire after the browser is closed
userCookie.setMaxAge(-1);

But beware that the cookie still exists when users logout and login with a different username-password combination !!

So I have a counter for the number of likes, and depending on my activity I have a like or unlike link.

Ready! Some CSS whistles

After we have added some more (basic) CSS we are done! As a result we have created something much more modern that the old school comments display as we know from the IBM Domino Blog template.

If you are interested and want to receive the details of this setup I welcome you to send me a message and we can exchange code.

Add 20 years of experience to your workforce

You can 20 years of experience within IBM Notes and Web development to your workforce by hiring me.

Interested? Read my curriculum vitae on LinkedIn: http://www.linkedin.com/in/patrickkwinten and get in contact.

I am happy to work WITH you !

 

 

 

 

Building a search function with DataTables plugin (IV)

Introduction

In the previous posts I demonstrated how to setup your application to generate a datatable component from your Notes view(s) and how to apply filtering & search capabilities to the columns.

In this post I will demonstrate how to build an external form to filter the datatable. I guess in most cases you want to provide some sort of search form and displayed separate from your table (left, right, top). For now we restrict the search form with input boxes.

Modifications

I have applied some modifications to the application. The complete and final code you can view in my Github repository.

Modifications:

  • java class that works as a custom service bean
  • javascript library for initializing datatable
  • xpage to display the datatable

Let’s add a form

The first thing we will do is by adding a Bootstrapped styled form to the xpage:

datatables04pre

Notice the following things:

  • The filter class for the input element
  • The data-column-index attribute for the input element

The filter class will be used to register events on:

$(‘.filter’).on(‘keyup change’, function () {
//clear global search values
db.search(”);
db.column($(this).data(‘columnIndex’)).search(this.value).draw();
});

The data-column-index attribute directs to the index of the corresponding column in the datatable.

With the form in place and the script updated we need to extend our java class so the values for the job title are included:

String job = String.valueOf(columnValues.get(10));
if (null!=job){
jo.put(“job”,job);
}

Let’s see the result

For example if I am looking for a person with firstname starting with Car.., from company Firm (something) acting as a host I get presented:

datatables04

Now with the initial search form in place we will extend it with features to improve it’s usability…