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! πŸ™‚

Adding the optgroup tag quickly to your xp:combobox

Today I was working on modernizing an xpages application when I came up with a simple enhancement: adding the optgroup tag to a xp:combobox.

By default this option is no available in XPages and if you look around there are some suggestions from hard-coded to DOM modification but I rather looked for a proper JSF implementation.

The data for my combobox resides in a categorized Notes view in a solid NSF so I had two options: navigating through the NotesView via a ViewNavigator (probably next post) or a quick and dirty combo of a dbcolumn and dblookup.

In context with the combobox control the dbcolumn/lookup combo seems suitable πŸ™‚

Here is the simplified setup for my xp:combobox:

As data source I am calling a method on a managed bean. The method is as followed:

The result is nice and straight-forward:

If you questions or difficulties with the implementation, just give me call. Happy development πŸ™‚

Adding a GDPR message to my XPages app

GDPR or Cookies plugins are nothing new. A simple search will give you a quick overview which jQuery based plugins are available.

So I decided the one that looked nicest to me and implemented it my XPages app. It worked but not when I met a user who was using IE. No JS error or whatever so where to start debugging? 😦

When looking in the original code I noticed that a different version of jQuery was used than is available on or Domino V10 server. So I posted an idea for an upgrade on the HCL Products Ideas Portal.

In the end I tried the Customizable EU Cookie Notice Popup Plugin which uses the 2.1.1 version of jQuery which is the same version as on the Domino server. (Loading multiple versions jQuery makes Dojo complain).

But then the requirements of the project changes and the functionality should become more to highlight news or updates within the application so I had to add some additional functionality:

  • enable / disable news display/popup
  • option to steer location / appearance on screen
  • from / until dates, so a period when the message should be displayed.

I decided when the option to enable the display but leave the from and until dates empty the popup will be displayed as long as the session of the browser lasts.

So these settings are stored in a Notes – Configuration document and made available as session scope variables.

Also the content of the popup has to be easily editable so for this I use Notes – Keywords documents so I can have messages in multiple languages.

When the code was working I lifted it up into a custom control which I can now re-use across other applications. Not that the custom control contains custom properties (yet)

undefined

The function to set the cookie expiry date is as followed:

To set a java date in a scope variable can be a little tricky. I did it as follow:

But this is all the code I needed. The result is as followed:

Note: I have altered the JS file of the plugin to bootstrap it more and to make it responsive.

When I press the OK button a cookie is set that last a browser session (I left the from and until dates empty in my settings):


If you like this post then please support my idea for upgrading the jQuery version on Domino: https://domino-ideas.hcltechsw.com/ideas/DDXP-I-637

Happy development πŸ™‚

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.

Fiddling with jax-rs and getting frustrated

Today was probably not the best day for development. After getting the Jakarta EE project to work on the development server by a miraculous second signing of the plugins in the update site NSF and restart of HTTP on Domino (v10) my next step was to actually getting to get some code running.

Curious as I am I found some code samples here , here and here but after trying some of the samples in my NSF I keep on running on errors:

[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM: ERROR i18n RESTEASY002025: Unknown exception while executing GET /sample/counter
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM: org.jboss.weld.exceptions.AmbiguousResolutionException: WELD-001318: Cannot resolve an ambiguous dependency between: 
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:   - Managed Bean [class servlet.Sample] with qualifiers [@Any @Default],
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:   - Managed Bean [class servlet.Sample] with qualifiers [@Any @Default]
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at org.jboss.weld.manager.BeanManagerImpl.resolve(BeanManagerImpl.java:1164)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at org.jboss.resteasy.cdi.CdiConstructorInjector.construct(CdiConstructorInjector.java:66)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at org.jboss.resteasy.cdi.CdiConstructorInjector.construct(CdiConstructorInjector.java:73)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at org.jboss.resteasy.plugins.server.resourcefactory.POJOResourceFactory.createResource(POJOResourceFactory.java:67)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:309)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:439)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:229)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at org.jboss.resteasy.core.SynchronousDispatcher$$Lambda$95.0000000046F8EF20.run(Unknown Source)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:135)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at org.jboss.resteasy.core.SynchronousDispatcher$$Lambda$96.0000000046F8F8D0.get(Unknown Source)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at org.jboss.resteasy.core.interception.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:355)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:138)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:215)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:227)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:56)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:51)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at org.openntf.xsp.jaxrs.impl.FacesJAXRSServletContainer.service(FacesJAXRSServletContainer.java:96)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at javax.servlet.http.HttpServlet.service(HttpServlet.java:806)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at com.ibm.designer.runtime.domino.adapter.ComponentModule.invokeServlet(ComponentModule.java:600)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at com.ibm.domino.xsp.module.nsf.NSFComponentModule.invokeServlet(NSFComponentModule.java:1352)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at com.ibm.designer.runtime.domino.adapter.ComponentModule$AdapterInvoker.invokeServlet(ComponentModule.java:877)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at com.ibm.designer.runtime.domino.adapter.ComponentModule$ServletInvoker.doService(ComponentModule.java:820)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at com.ibm.designer.runtime.domino.adapter.ComponentModule.doService(ComponentModule.java:589)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at com.ibm.domino.xsp.module.nsf.NSFComponentModule.doService(NSFComponentModule.java:1336)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at com.ibm.domino.xsp.module.nsf.NSFService.doServiceInternal(NSFService.java:662)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at com.ibm.domino.xsp.module.nsf.NSFService.doService(NSFService.java:482)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at com.ibm.designer.runtime.domino.adapter.LCDEnvironment.doService(LCDEnvironment.java:357)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at com.ibm.designer.runtime.domino.adapter.LCDEnvironment.service(LCDEnvironment.java:313)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at com.ibm.domino.xsp.bridge.http.engine.XspCmdManager.service(XspCmdManager.java:272)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM: org.jboss.resteasy.spi.UnhandledException: org.jboss.weld.exceptions.AmbiguousResolutionException: WELD-001318: Cannot resolve an ambiguous dependency between: 
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:   - Managed Bean [class servlet.Sample] with qualifiers [@Any @Default],
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:   - Managed Bean [class servlet.Sample] with qualifiers [@Any @Default]
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at org.jboss.resteasy.core.ExceptionHandler.handleException(ExceptionHandler.java:257)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at org.jboss.resteasy.core.SynchronousDispatcher.writeException(SynchronousDispatcher.java:193)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:455)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:229)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at org.jboss.resteasy.core.SynchronousDispatcher$$Lambda$95.0000000046F8EF20.run(Unknown Source)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:135)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at org.jboss.resteasy.core.SynchronousDispatcher$$Lambda$96.0000000046F8F8D0.get(Unknown Source)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at org.jboss.resteasy.core.interception.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:355)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:138)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:215)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:227)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:56)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:51)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at org.openntf.xsp.jaxrs.impl.FacesJAXRSServletContainer.service(FacesJAXRSServletContainer.java:96)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at javax.servlet.http.HttpServlet.service(HttpServlet.java:806)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at com.ibm.designer.runtime.domino.adapter.ComponentModule.invokeServlet(ComponentModule.java:600)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at com.ibm.domino.xsp.module.nsf.NSFComponentModule.invokeServlet(NSFComponentModule.java:1352)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at com.ibm.designer.runtime.domino.adapter.ComponentModule$AdapterInvoker.invokeServlet(ComponentModule.java:877)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at com.ibm.designer.runtime.domino.adapter.ComponentModule$ServletInvoker.doService(ComponentModule.java:820)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at com.ibm.designer.runtime.domino.adapter.ComponentModule.doService(ComponentModule.java:589)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at com.ibm.domino.xsp.module.nsf.NSFComponentModule.doService(NSFComponentModule.java:1336)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at com.ibm.domino.xsp.module.nsf.NSFService.doServiceInternal(NSFService.java:662)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at com.ibm.domino.xsp.module.nsf.NSFService.doService(NSFService.java:482)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at com.ibm.designer.runtime.domino.adapter.LCDEnvironment.doService(LCDEnvironment.java:357)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at com.ibm.designer.runtime.domino.adapter.LCDEnvironment.service(LCDEnvironment.java:313)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at com.ibm.domino.xsp.bridge.http.engine.XspCmdManager.service(XspCmdManager.java:272)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM: Caused by: 
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM: org.jboss.weld.exceptions.AmbiguousResolutionException: WELD-001318: Cannot resolve an ambiguous dependency between: 
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:   - Managed Bean [class servlet.Sample] with qualifiers [@Any @Default],
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:   - Managed Bean [class servlet.Sample] with qualifiers [@Any @Default]
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at org.jboss.weld.manager.BeanManagerImpl.resolve(BeanManagerImpl.java:1164)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at org.jboss.resteasy.cdi.CdiConstructorInjector.construct(CdiConstructorInjector.java:66)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at org.jboss.resteasy.cdi.CdiConstructorInjector.construct(CdiConstructorInjector.java:73)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at org.jboss.resteasy.plugins.server.resourcefactory.POJOResourceFactory.createResource(POJOResourceFactory.java:67)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:309)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:439)
[14EC:0011-0484] 2020-02-12 21:24:57   HTTP JVM:  ... 23 more

Here is the code that I am running:

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 πŸ™‚

How do you organize your workspace?

Notes users differ in their tasks so my usage of the Notes workspace as a developer is completely different from a user that mainly works with documents within an application.

As always “With great power there must alsoΒ comeΒ — greatΒ responsibility” so the Notes client lets us completely free how to setup our workflow via the workspace.

My activities focus mainly on adding code, distribute these changes into several environments (from DEV to PRODUCTION) and also store this code in templates.

If you have similar activities, how do you organize your workspace?

Back with CWPPR0033E error

So after my previous postΒ I uninstalled Notes 9, installed Notes 10 with FP2 and installed the desired plugins. All installations worked like a charm.

But now after a couple of days working with DDE I wanted to install another plugin and BANG! I got the CWPPR0033EΒ  message again.

install_error

Anyone have a suggestion how to install a plugin other than the File – Application – Install process into DDE ?!?