XPages hack – visualizing checkboxes that fail validation

In an application I have added some CSS to highlight fields that fail validation. The css looks as followed:

Basically I an looking for the aria-invalid=true attribute on input elements. Works great but not on all input elements. For example re-styling of checkboxes requires some work. But what if you would re-style the element that contains the checkbox?

In the following example I have captured a checkbox control inside a table data cell of 1% width:

For the checkbox control I have defined a custom validator that gets only triggered when a certain element send the xpage to the server (an button control with id btnSendToCommittee)). When the validation fails the checkbox also gets the aria-invalid=true attribute.

To apply a visual indication if a certain checkbox fails I have setup a simple JS function:

This function scans the page for inputs with the aria-invalid=true attribute and for all matches checks if the found item is a checkbox. If so, the parent, containing element (the table data cell) will be applied some styling.

Besides a listing in the xp:messages control I also have a visual indicator for the checkbox on the location of the xpage:

I have not managed to write a single-line css selector therefor I still loop through the found inputs that match the aria-invalid=true attribute. Here your assistance comes in… 😉

Happy coding!

Sticky headers in Bootstrap from ExtLib

I received a request from a customer who would like to preserve the action buttons on an xpage when the user scrolls downs a very long form.

The application is already using the BS theme from the Extension Library with the sticky navbar option in use so this would implement a second ‘sticky element’.

Here is an example how the xpage looks like:

Note: I added some Lorem Ipsum content in stead of form elements for demo purpose, but you get the idea of a endless form to fill in.

So first I added a listener for the scroll event:

window.onscroll = function() {scrollFunction()};

The scrollFunction is as followed:

function scrollFunction() {
if (document.body.scrollTop > 50 || document.documentElement.scrollTop > 50) {
console.log('make SMALL')
$('.navbar').css({
'min-height': '32px',
'height': '32px'
});
$('.navbar-brand').css({
'height': '32px',
'padding-top': '5px',
'padding-bottom': '5px'
});
$('.navbar-nav > li > a').css({
'padding-top': '5px',
'padding-bottom': '5px'
});
$('#infoLanguage > div.dropdown').css({
'padding': '5px 5px'
});
$('#form-header').css({
'display': 'block',
'z-index': '-1'
});
$('#form-header-hidden').css({
'display': 'block',
'position': 'fixed',
'top': '30px',
'height': '30px',
'z-index': '996'
});
$('navbar').addClass('shrink');
} else {
console.log('make LARGE')
$('.navbar').css({
'min-height': '50px',
'height': '50px'
});
$('.navbar-brand').css({
'height': '50px',
'padding-top': '15px',
'padding-bottom': '15px'
});
$('.navbar-nav > li > a').css({
'padding-top': '15px',
'padding-bottom': '15px'
});
$('#infoLanguage > div.dropdown').css({
'padding': '15px 10px'
});
$('#form-header').css({
'display': 'block'
});
$('#form-header-hidden').css({
'display': 'none',
'position': 'fixed',
'top': '0px',
'height': '30px',
'z-index': '0'
});
$('navbar').removeClass('shrink');
}
}

I also added some additional styling for style and transition effects:

<style> 
	#form-header-hidden{ 
	width:100%; 
	background-color:white; 
	height:0px; 
	padding:5px 150px; 
	box-shadow: 0px 3px 5px 0px rgba(182,182,182,0.75); 
	transition: all 0s, opacity 0.5s linear;
} 

#form-header{ 
	transition: all 0s, opacity 0.5s linear;
}

.navbar{
	transition: all 0.5s;
}
.navbar-brand img{
    transition: all 0.5s;
}
</style>

So I basically make 2 form-header elements, on for ‘normal’ display when focus is on the top and one when the user starts to scroll down from a certain point. The form elements are basically the same but more adapted on the height of their container.

One thing I would like to note is that I had to place the ‘smaller’ sticky header under the navbar so it can inherit the same with as its parent. Otherwise I would look not nice with a bit of displacement.

I also noted a small shaking effect when I would use the visibility option to show/hide which form-header to display. Choosing here to play mess with the z-index property solves that issue with shaking when scrolling slowly in the beginning.

Here is the result when the user starts to scroll down. The form header becomes small but sticky under the navbar. The buttons to interact with the form are available where ever the user is in the form:

A small new feature in the app but I think it will bring a lot of user satisfaction! Happy coding 🙂

Fixing the Tooltip function in XPages #2

In a previous post I wrote about the disfunction of the tooltip after validation with the Domino server. It turns out my gigia-form has a lot of fields with visibility properties set that are calculated after partial refresh. To have tooltip fixed for the fields that become visible during interaction with the user I needed to apply another patch.

From the xsnippet for the standby control I took the first part to set up the ability to subscribe to the different partial refresh events. Then I added some lines to subscribe to the partialrefresh-complete state:

The initToolTip function did not changed:

With this subscription the tooltip becomes applied to fields that become visible after validation (when the user continues to fill in the form).

The possibility to subscribe to partialrefresh states is also a use-case for other plugins I use, for example to automatically adapt textareas to user-input.

It’s a bug, it’s a pain, it’s an xp:checkBoxGroup!

Many where wondered why the xp:checkBoxGroup with the Bootstrap theme was transformed into a HTML table.

I have not heard so many complained that when you add a styleClass to it another class attribute is added and the browser simply ignores the second class attribute. Hell yeah, when you create a div element and add two class attributes DDE complains that a class attribute was already added for that element.

So:

xp:checkBoxGroup id=”checkBoxGroup1″ styleClass=”testBox”

becomes in your browser source

div class=”checkbox” id=”view:_id1:checkBoxGroup1″ class=”testBox”

and your browser renders:

div class=”checkbox” id=”view:_id1:checkBoxGroup1″

Save your day, Save the state of your repeat control

I am writing a demo application with a faceted search functionality. For displaying the “results” I am using a repeat control.

Now users want to switch back and forth between the “view” and the “document”. If you display the “results ” in a “view” with an “view panel” control you can apply a pager to that view and for that pager you can apply a “pager save state” control. So when you go from “results” xpage to the “document” xpage and back you land in the same state (same page in that view) and the user does not have to navigate again to the last page in the view panel control.

Now with the repeat control that is a different thing. The “pager save state” was not designed for it. Luckily there is an approach which will deliver you the same result. Add for your repeat control the property: first=”#{javascript:return (sessionScope.first != null)?sessionScope.first:0;}”. Than from the link (could be a button or whatever) in the repeat control add the following in the onClick event handler: sessionScope.put(‘first’,getComponent(“rpCollection”).first);. Then do your normal stuff (like redirect to a page). If you from that redirected page go back to the “results” page the repeat control will show the last set of items from where you left.

I hope it will save your day and your project. Happy coding! 🙂

RAD Table walker

For a project I received some very unclear description for a change. After a conversation I came up with the suggestion to rebuild the UI and present a Bootstrap table with add and delete buttons. Something what developers would call a table walker.

So how did I do this?

First I had to extend the Model object of my MVC model. The table repeats rows with persons details so I added:

The Person class is simplified as followed:

On my model object I also added getters and setters and and an add method:

Then I had to build the table with the following important elements:

  • a repeat control to represent my arraylist of Person objects
  • input fields bounded to the Person name and email
  • a button for each row to remove the Person from the arraylist
  • a button to display a new blank row to register a new Person

The code is not that long or complicated. I highlighted the important parts with bleeding yellow:

In the next phase I decided to replace the input field the name field with namespickers (by first name or by last name) wrapped as Bootstrap field add-ons. Here is the rough code:

This all resulted in a nice BS table with add / remove buttons:

Now I am just waiting for customer approval. That is mostly the longest part in Rapid Application Design.

Getting the Select2-to-Tree working in XPages

In a modernization project (increase browser compatibility) I needed a more simple solution for a custom value picker. The less design elements the merrier 🙂

Another highly desired feature was to have a search function within the picker because there are a lot of values to choose from.

So my initial though was to present the options in a listbox and wrap the select2 plugin around it. Well it turned out the customer found the categorization of options a need to have, including collapse and expand options (who has not grown up with twisties?).

Next attempt was to implement the Select2-to-tree plugin since it presents a hierarchy of links in a select2 object

Looks familiar like a Notes view right? Luckily my Notes view only got one level of categorization and my skills with transforming Notes view data into other objects came in handy. So in a short time I have my data providing REST service setup .

Here is the initiation script for the plugin:

Then I tried to send the selected value to the server to check the value but I was not able to. The XPage did not send a message but the page just frooze.

So to avoid to spend a lot of time on finding and fixing the bug I decided to run the alternative route and use SELECT element instead of the xp:listBox and write the value from csjs to the server via a RPC service (hence the change listener)

<select
	id="lbTest1"
	multiple="true"
	class="form-control">
</select>

The RPC service has nothing special, just setting a viewScope variable:

So that is basically it. With a minimum of design elements / coding I got my Notes view data in a treeview presented in a select2 component:

One item to be removed from my back-log. Happy development 🙂

Data for Bootstrap Treeview from a Notes View?

In a modernization project (increase of browser compatibility) I needed to find a solution for a list of links, categorized and sorted like a Notes View.

Since I already use Bootstrap as CSS framework I decided to check the following Bootstrap Treeview project: https://github.com/jonmiles/bootstrap-treeview .  I was satisfied with the following example(s):

treeview_ex.png

The initialisation is pretty simple:

snipp01

Now I just needed to set up a REST service to provide me the data. The service is setup on an XPage and bounded to a Java class:

snipp02

The Java class I will reuse for providing multiple data streams so it detects the different method parameters:

snipp03

As mentioned the data for the list comes from a Notes View. As you all know views entries have different characteristics which I have to bear in mind:

  • Documents can land anywhere in the view since it is categorized on a text field where the multiple levels can set individually eg Europe\\Sweden\\Stockholm or Afrika\\Johannesburg.
  • The view also holds links for other menus organized under a category so I decided to us a viewnavigator and start collecting data from a specific category.
  • For each entry in the view I have to check what type it is: category, document or total. The last one is not of my interest so I have to skip if that option occurs.
  • The columnindentlevel tells me all about where I am in the view in comparison with the columnindentlevel of the previous entry. Here are the scenarios:

// case 1: current indent level < columnindentlevel
// -> new category, propobably start situation
// case 2: current indent level = column indentlevel
// -> new category (sibling) but close the previous
// one first (just one level)
// case 3: current indent level > column indent
// level -> new category but closes the previous
// one(s) first. how many depends on difference curr
// and column level

Ofcourse categories behave different that documents.

So here is the code:

serv01.PNG
serv02
serv03.PNG

Some notes:

  • I have set the header of the REST service to text/html and the plugin needs a JavaScript object. Therefor capture my data response in an eval() method.
  • A target attribute is not provided by the plugin, so I add one myself. Categories have the # as href so based on that info I include a target attribute or not. I do this via a function:
snipp04
  • If the treeview is collapsed there is no anchor element for underlying list-items so you cannot add the target for all links.
  • Also when collapsing and expanding a category the added target attributes are gone. So for opening of every category you need to re-apply the href attribute for underlying anchors.
  • A category can be opened via a ‘twistie’ image or the text link so we need to register an action on these onclick events
snipp05
snipp06

The result is a nice looking ‘Notes View data-driven’ treeview with Bootstrap styling:

snipp07

Happy coding 🙂

PS. I noticed I have a lot of unused local variables in my code, you may clean that up 🙂

Debugging utility in XPages

Time to re-open an previous XPages project and add some new functionalities for the users. When I did this today I noticed some programming that I never wrote about before, so I do this now 🙂

In an application I wanted to monitor individual classes and write the findings to the console or OpenLog. All managed from a web UI 🙂

The printing method in this app is pretty simple:

public void printToConsole(String msg){
String callerClassName = getCallerCallerClassName();
if (debugClasses.containsKey(callerClassName)){
if (false != debugClasses.get(callerClassName)){
if (true == this.openLog){
OpenLogUtil.logEvent(new Throwable(), msg, Level.INFO, null);
}
else{
System.out.println(msg);
}
}
}
}

The getCallerClass method identifies which class is calling this method:

public static String getCallerCallerClassName() {
StackTraceElement[] stElements = Thread.currentThread().getStackTrace();
String callerClassName = null;
for (int i=1; i<stElements.length; i++) {
StackTraceElement ste = stElements[i];
if (!ste.getClassName().equals(KDebug.class.getName())&& ste.getClassName().indexOf(“java.lang.Thread”)!=0) {
if (callerClassName==null) {
callerClassName = ste.getClassName();
} else if (!callerClassName.equals(ste.getClassName())) {
return ste.getClassName();
}
}
}
return null;
}

Here is the KDebug class :

package com.wordpress.kwintessential.myproject.utils;

public class KDebug {
public static String getCallerClassName() {
StackTraceElement[] stElements = Thread.currentThread().getStackTrace();
for (int i=1; i<stElements.length; i++) {
StackTraceElement ste = stElements[i];
if (!ste.getClassName().equals(KDebug.class.getName()) && ste.getClassName().indexOf(“java.lang.Thread”)!=0) {
return ste.getClassName();
}
}
return null;
}
}

debugClasses is declared as followed:

private HashMap<String,Boolean> debugClasses = new HashMap<String,Boolean>();

And it is set when I initiate my utility class that contains the printing method:

public void setDebugClasses(){

try {
Properties debugs = this.getDebugProperties();
Enumeration<String> enums = (Enumeration<String>) debugs.propertyNames();

while (enums.hasMoreElements()) {
Boolean boolean1 = false;
String key = enums.nextElement();
String value = debugs.getProperty(key);
boolean1 = Boolean.valueOf(value);
debugClasses.put(key, boolean1);
}
} catch (IOException e) {
OpenLogUtil.logError(e);
}

}

method getDebugProperties reads a properties file that is stored in the WebContent directory:

monitor

public Properties getDebugProperties() throws IOException {
InputStream input = FacesContext.getCurrentInstance().getExternalContext().getResourceAsStream(“monitor.properties”);
Properties debugs = new Properties();
debugs.load(input);
return debugs;
}

Note that I have to place the file in the WebContent folder and I could not place it as a Resource design element.

The monitor.properties file contains key values pairs e.g.:

com.wordpress.kwintessential.myproject.dao.DecisionDominoDAO=true
com.wordpress.kwintessential.myproject.dao.VisitorDominoDAO=false

So with this code above I can:

  • check which class is calling the print method
  • check if this class should be monitored (read: print data to the console or OpenLog)

So all great! But in my case I wanted to prevent that the design had to be opened and rebuild every time the monitor settings should be altered.

So in this project I created a web UI (XPage) to read and change the monitor.properties file:

<?xml version=”1.0″ encoding=”UTF-8″?>
<xp:view xmlns:xp=”http://www.ibm.com/xsp/core”&gt;

<xp:this.beforePageLoad><![CDATA[#{javascript:var file = param.get(“filename”)
if (null == file || file == “”){
ctx = facesContext.getExternalContext();
ctx.redirect(facesContext.getExternalContext().getRequest().getRequestURL() + “?filename=monitor.properties”);
}}]]></xp:this.beforePageLoad>
<xp:panel styleClass=”container”>

<h1>Debug <small> Monitor Java classes</small></h1>

FileName:
<xp:inputText
id=”inputTextFileName”
value=”#{fileBean.fileName}”
defaultValue=”#{param.filename}”
disabled=”true” />
<xp:br />
FileData:

<xp:inputTextarea
id=”inputTextarea1″
value=”#{fileBean.fileData}”
rows=”20″
cols=”80″ />
<xp:br />
<xp:panel styleClass=”btnBar”>
<xp:button value=”Load” id=”buttonLoad”
styleClass=”btn-primary”>
<xp:eventHandler event=”onclick” submit=”true”
refreshMode=”complete” action=”#{fileBean.loadData}”>
</xp:eventHandler>
</xp:button>
<xp:button value=”Save” id=”buttonSave”>
<xp:eventHandler event=”onclick” submit=”true”
refreshMode=”complete” action=”#{fileBean.saveData}”>
</xp:eventHandler>
</xp:button>
</xp:panel>
</xp:panel>
</xp:view>

Here is how it looks in a browser:

web ui initial.JPG

When I press the Load button the content of the monitor.properties file get’s loaded:

web ui loaded

And of course when I press the Save button the monitor.properties file get’s updated 🙂

Now where is the magic in this? I got two methods fileBean.loadData and fileBean.saveData.

Here is what the fileDataBean class looks like:

import java.io.IOException;
import java.io.Serializable;
import java.util.Properties;

import lotus.domino.NotesException;

import com.paulwithers.openLog.OpenLogUtil;

public class FileDataBean implements Serializable {

private static final long serialVersionUID = 1L;
private String fileName;
private String fileData;
private String dbPath;
private String dbServer;

private Utils utils;
Properties props;

public FileDataBean() throws NotesException{
utils = new Utils();
utils.printToConsole(this.getClass().getSimpleName().toString() + ” – FileDataBean() // constructor”);
try {
props = utils.getDataSourceProperties();
dbServer = props.getProperty(“server_namis_notesname”);
dbPath = utils.getSession().getCurrentDatabase().getFilePath();
} catch (IOException e) {
OpenLogUtil.logError(e);
}
}

public String getDbPath() {
return dbPath;
}

public void setDbPath(String dbPath) {
this.dbPath = dbPath;
}

public String getDbServer() {
return dbServer;
}

public void setDbServer(String dbServer) {
this.dbServer = dbServer;
}

public void setFileData(String fileData) {
this.fileData = fileData;
}

public String getFileData() {
return fileData;
}

public void setFileName(String fileName) {
this.fileName = fileName;
}

public String getFileName() {
return fileName;
}

public void loadData() {
this.fileData = NAPIUtils.loadFile(this.dbServer, this.dbPath, this.fileName);
}

public void saveData() {
NAPIUtils.saveFile(this.dbServer, this.dbPath, this.fileName, this.fileData);
}
}

The NAPIUtils looks as followed:

import java.io.InputStream;
import com.ibm.designer.domino.napi.NotesAPIException;
import com.ibm.designer.domino.napi.NotesDatabase;
import com.ibm.designer.domino.napi.NotesNote;
import com.ibm.designer.domino.napi.NotesObject;
import com.ibm.designer.domino.napi.NotesSession;
import com.ibm.designer.domino.napi.design.FileAccess;
import com.paulwithers.openLog.OpenLogUtil;

public class NAPIUtils {

/**
* loads a given WebContent file and returns the result as String
*
* @param serverName
* the server to use
* @param dbPath
* the database path
* @param fileName
* the file to load
* @return the file data as String
*/
static public String loadFile(final String serverName, final String dbPath, final String fileName) {
NotesSession nSession = null;
NotesDatabase nDatabase = null;
NotesNote nNote = null;
try {
nSession = new NotesSession();
// open database
nDatabase = nSession.getDatabaseByPath(serverName + “!!” + dbPath);
nDatabase.open();
// load existing data
nNote = FileAccess.getFileByPath(nDatabase, fileName);
// get Filedate and return String
InputStream is = FileAccess.readFileContentAsInputStream(nNote);

return convertStreamToString(is);
} catch (NotesAPIException e) {
OpenLogUtil.logError(e);
} finally {
// recycle NAPI objects
recycleNAPIObject(nNote, nDatabase, nSession);
}

return fileName;
}

/**
* loads a given WebContent file and returns the result as String
*
* @param serverName
* the server to use
* @param dbPath
* the database path
* @param fileName
* the file to load
* @param fileData
* the data of the file
*/
static public void saveFile(final String serverName, final String dbPath, final String fileName, final String fileData) {

NotesSession nSession = null;
NotesDatabase nDatabase = null;
NotesNote nNote = null;

try {
nSession = new NotesSession();
// open database
nDatabase = nSession.getDatabaseByPath(serverName + “!!” + dbPath);
nDatabase.open();
// load existing data
nNote = FileAccess.getFileByPath(nDatabase, fileName);
// store them to note
FileAccess.saveData(nNote, fileName, fileData.getBytes());
} catch (NotesAPIException e) {
OpenLogUtil.logError(e);
} finally {
// recycle NAPI objects
recycleNAPIObject(nNote, nDatabase, nSession);
}
}

/**
* converts an input stream to a string
*
* @param is
* the input stream to convert
* @return String
*/
static String convertStreamToString(java.io.InputStream is) {
java.util.Scanner s = new java.util.Scanner(is).useDelimiter(“\\A”);
return s.hasNext() ? s.next() : “”;
}

/**
* recycleNAPIObject helper method for recycling NAPI objects
*
* @param nObjects
* the NAPI objects to recycle
*/
static void recycleNAPIObject(NotesObject… nObjects) {
for (NotesObject nObject : nObjects) {
if (nObject != null) {
try {
(nObject).recycle();
} catch (NotesAPIException ne) {
OpenLogUtil.logError(ne);
}
}
}
}
}

From this class I use the loadFile and saveFile methods. Look at the import of the com.ibm.designer.domino.napi classes which enables me to access files within the NSF!

Hopefully this writing gives you some new ideas for your development projects. Happy coding 🙂

Quickly adding localization to an XPages app

I needed to quickly make an application available in multiple languages. Luckily I had all strings already gathered in a properties file. So the following things I needed to add:

  • A translated version of the strings properties file
  • Some sort of navigation to choose another language than the browser default one.

For the first step Google Translate was my friend. Step two was not so hard either:

context.setLocaleString(‘en’)
context.reloadPage()

So a couple of minutes work and a surprised customer 🙂

Capture