bookmark_borderHAPPY!!! first full featured xPage application productive!

I am happy to report that our first full featured (integrated workflow, cool typeAheads, fantastic validation, dynamic tables and many more features) is fully productive!

Thanks to all readers and commenters on my blog and all other bloggers out-there giving me help and insight into the deeper workings of the xPages.

Summarized main difficulties:

  • getting the validation to work the way we required (ie. without needing ExtensionLibrary tabbed tables, all fields should be validated, even if currently invisible and the validation should be customizable by the customer himself)
    • Solution was: not using validators at all, simply prevent the save with a condition on the action group in the save buttons, the condition is a “result” from our custom validator which sets JSF messages where appropriate
  • customized typeAheads (see this blog)
  • workflow integration (we can continue to use the LN client based code, which is important as the interface is now working on both “clients”, notes and web)
  • certain parts of the form stored in different documents (see this blog)
  • dynamic tables which lets the user add new rows to tables of data
  • simple yet full featured and customizable views, using the DynamicViewPanel with different filter options per selected view, customizable (via styles) view columns
  • fancy field help (see this blog)
Result:
What we now have is a full featured framework which will help us create future xPage applications in a fraction of the time spent on this one! As a matter of fact we could create a simple form/view db with full featured workflow within one day!!!!!!!!!!!!! Most of the work actually could even be “outsourced” to the customer, work such as “defining” field help, column customizations, validations etc.
Best features in xPages (imho):
  • CustomControls!!! change in one place an all occurrences are updated! Subforms in the old LN days were no match at all!!!
  • typeAheads
Worst feature in xPages (imho):
  • error messages from server: an HTML 500 error is of no use, sometimes I needed to dissect the whole app again to find where the problem was
  • OneUI: it’s a pain in the ass, but we did it, to figure out how to properly change certain GUI elements look and feel (such as typeAheads)
Recommendation:
  • don’t use JavaScript (or only to call backend Java code) !!!! debugging JavaScript is a pain in the ass where as debugging Java is so straight forward (see this blog) and the speed of the code execution is SOOOOOOOOOOOOOOOOOOOOO freaking fast!!!
  • use where ever possible “static” methods so you don’t need to instantiate the class, eg. our StandardUtil class has ONLY static methods such as: getKeywordValues, searchUser (for typeAhead), getSortedDocumentCollection, getEmptyDocumentCollection, hasAccess, hasRole etc.
I now need some rest. The past few weeks were mostly sleepless. Any recommendation for a nice “get-well-again” resort close to Switzerland are welcome 😉 ! (can be south Germany or Austria as well)

bookmark_borderINDIP: xPages isEditable() won’t work on panels with READ ACL! see my workaround

If you have workflow applications then it’s most likely that you wanna make certain parts of your xPage READonly for certain states (eg. such that an approver cannot change the details of the request he has to approve)

In old days we used access controlled sections. xPages gives us the possibility to add ACLs to panels, that servers (almost) the same purpose.

Why only almost? Cause all elements you put there to set data (buttons, links etc.) won’t be hidden even if you use currentDocument.isEditable().

Here’s my workaround:

on the Visible property of such a button etc.
instead of just:
currentDocument.isEditable()

use:
currentDocument.isEditable() && !YourClass.hasParentReadACL(getClientId(this.getId()))

now create a YourClass (we use our StandardUtil as it contains “standard” methods) and add this method:

public static boolean hasParentReadACL(String id) {
String[] components = id.split(“:”);
UIComponent parent = null;
String parentId = null;
int i = 2;
// the || is important as repeat controls within custom controls use the same id, so we have to
// get their parent as unique parent
while (null == parent || parent instanceof XspDataIterator) {
parentId = components[components.length – i++];
parent = JSFConnector.findComponent(parentId);
}

UIViewRoot root = FacesContext.getCurrentInstance().getViewRoot();
boolean hasACL = false;

while (root != parent) {
if (parent instanceof UIPanelEx) {
if (null != ((UIPanelEx) parent).getAcl()) {
hasACL = true;
break;
}
}
parent = parent.getParent();
}

return hasACL;
}

and voila all your additional buttons etc. are hidden if one of the parent panels has an ACL!
Of course you might change the code to really check for a READ ACL. We only use ACLs for READ so this check is at the moment superfluous.

ah.. a not so minor thing: we define the StandardUtil in a SSJS library to easy reference StandardUtil, in your case you may wanna use:
&& com.yourcompany.YourClass.hasParentReadACL()

bookmark_borderQUINDIP: format your dates from backend documents properly

we use a standard date format in our company: 2013-04-30 22:52

to accomplish this I use:

When you simply use a data binding this works like a breeze.
It’s a bit more complicate if you want to display a date/time from a backend document with Java

 this won’t work:

doc.getItemValueString(“fieldName”)
doc.getItemValue(“fieldName”).get(0)
doc.getItemValueDateTimeArray(“fieldName”).get(0)

this will work:
doc.getItemValueDateTimeArray(“fieldName”).get(0).toJavaDate()

and just as a side note: I really feel stupid! it took me hours to figure out how to get it to work!!! and yet once I understood why it’ became so crystal clear

bookmark_borderYOUFEB: DynamicViewPanel cannot sort descending?

I have a been troubling why I can’t sort on certain columns. Again after a loooooooooooooooooong time I figured: hell what..

So I changed the sort direction of that column which I could not sort in the dynamicViewPanel. Just out of any other options, something as a “last resort”, or out of desperation.

And who believes it??? Finally I could sort?

So I figured: dynamicViewPanel cannot sort descending?

Anyone can confirm this? Sorting ascending and both directions works, descending nope!

Don’t know if it’s related to the DominoViewCustomizer..

where can I file a bug report?

bookmark_borderYOUFEB: do you want me to share an easy way to configure views in your xPages?

just recently I have found the DominoViewCustomizer to customize the views displayed with the Dynamic View Panel from the extension library.

Of course it was a cool idea but I needed a way to “customize” my views.

Is anyone interested in me sharing this whole “component”? Well it’s not really just one component, a few pieces play together.

If I get a decent amount of comments asking me to share it I will do so.

Just so you know what it would be about:

  • simple way to let users choose from available views
  • in an open view you can call “ColumnCustomizer” which lets you set all properties you can set on a view column
  • additionally some ideas how to add easy filtering on views

I think I would strip down my “framework” to just that piece of functionality 
and put some comments on how to use/implement it online.

I from my side would have question as well:

Does someone know if I can create a (repeat) control which has “computed properties” (eg. the label, or values to choose from) based on not just a simple string but on JavaScript which would be “evaluated” on the fly?

I do know how to evaluate JavaScript with Java, what I am missing though: How can I attach to a new (repeat) component dynamic JavaScript? Dynamic means: the JavaScript is not stored in the component but on a configuration document.

Still confused? An example:
I am looking for a way to fully “customize” my view filter!
I want to put some tags/attributes and some code onto my dynamic column view definitions.
The result would be a fully automated dynamic view filter! Think about dropdownboxes which get their values from the provided JavaScript

At the moment I do have to create in one custom control “hard coded” fields (for each view on a different panel which is rendered only for specific views) for filtering on specific columns. I would like that to be customizable!


bookmark_borderINDIP: sort a document collection in Java (Java heroes may skip this)

Update: the result of the blog below landed here. Thanks to Nathan Freeman, hihi, kudos to me???

In Lotus Script it was not a simple task to sort a document collection. In Java it sort of is.

Idea: use a comparator class, supply with an array of fields upon which the “sorting” should happen and let the Collections.sort do it’s magic:

Comparator class:

public class CollectionComparator implements Comparator<Document> {

String[] sortFields = null;

public CollectionComparator(String[] sortFields) {
this.sortFields = sortFields;
}

public int compare(Document doc1, Document doc2) {

try {
int compared = 0;
// loop all sortFields
for (String field : sortFields) {
Item item1 = doc1.getFirstItem(field);
Item item2 = doc2.getFirstItem(field);
switch (item1.getType()) {
case Item.TEXT:
case Item.AUTHORS:
case Item.NAMES:
case Item.READERS:
String val1 = doc1.getItemValueString(field);
String val2 = doc2.getItemValueString(field);
compared = val1.compareTo(val2);
if (0 != compared) {
return compared;
}
break;
case Item.NUMBERS:
Double d1 = doc1.getItemValueDouble(field);
Double d2 = doc2.getItemValueDouble(field);
compared = d1.compareTo(d2);
if (0 != compared) {
return compared;
}
break;

case Item.DATETIMES:

DateTime dt1 = item1.getDateTimeValue();
DateTime dt2 = item2.getDateTimeValue();
compared = dt2.timeDifference(dt1);
if (0 != compared) {
return compared;
}
break;
}

item1.recycle();
}
return 0;
} catch (NotesException e) {
Logger.logError(e);
}

return 0;
}

}

Calling the sort:
// fill a list with the documents
List<Document> docs = new ArrayList<Document>();
doc = dc.getFirstDocument();
while (null != doc) {
docs.add(doc);
doc = dc.getNextDocument(doc);
}

// sort the list
String[] sortFields = new String[] { “fieldName” }; // you can also supply multiple fields “field1”, “field2”
Collections.sort(docs, new CollectionComparator(sortFields));

// retrieve values
for (Document docSorted : docs) {
values.addAll(docSorted.getItemValue(“Values”));
}
All code samples and downloads are copyright Michael Zischeck and licensed under Apache License 2.0 

bookmark_borderQUINDIP: dynamic “access controlled sections” ;-) in xPages

Some people faced the same problem than me: they wanted something like the “access controlled sections” in plain old LN development.
Mark Leusink
Russell Maher
Jeremy Hodge

None of the articles quite well didn’t explain how to set those ACLs dynamically..

I also tried to get set READER or EDITOR via something like:
return (condition to check) ? “READER” : “EDITOR”

but it wouldn’t work, especially not if in the condition the currentDocument was used. So what next?

I tried with a Bean, and it worked very smoothly. I put the code into the Bean constructor, the bean itself is a request scope bean, as (for whatever reason) I could not get view scope beans to work:

UIPanelEx panel = null;
ACL acl = new ACL();
ACLEntry aclEntry = new ACLEntry();
if (!(dominoDoc.getValue(“wfCurrentState”).equals(“1”) || dominoDoc.getValue(“wfCurrentState”).equals(“5”))) {
aclEntry.setRight(“READER”);
aclEntry.setType(“DEFAULT”);
aclEntry.setFullName(“*”);
acl.addEntry(aclEntry);

panel = (UIPanelEx) JSFConnector.findComponent(“panelRequestor”);
panel.setAcl(acl);
panel = (UIPanelEx) JSFConnector.findComponent(“panelDetails”);
panel.setAcl(acl);
panel = (UIPanelEx) JSFConnector.findComponent(“panelApprover”);
panel.setAcl(acl);
}


dominoDoc is the document source and the code prevents a few panels from being edited in all but in 2 workflow states (1, 5)

Neat!

bookmark_borderFRULE!!!: 9h to find ignoreRequestParams=”true” needs to be set!!!

In continuation of this I faced another problem in my target database. The first time the dialog for the sensitive data was called, it worked just fine. All subsequent calls just failed. After a very long time (you know it………) I finally figured out: the dialog box was using the parent document in subsequent calls.. but why? And why did it work on my test application.

With help from a coworker I finally, by accident!, figured the only difference between test and my actual database was ignoreRequestParams=”true”!! On test this parameter was set and there it worked. I gave it a try, without actually knowing what I was doing. Why I didn’t know? That’s a rhethorical question, right? 😉 On the web I have been stumbling across some other blogs mentioning it, which might have been the reason why I, ghosted  as I was through the lack sleep, tried it out on test. I honestly don’t know how that parameter came there.

But what the heck… finally my approach worked and I could save all my special infos with special access rights to another document!

Anyone: Would it be possible for someone knowing in detail what this parameter is about to write about it? Would be highly appreciated.

What I then figured was also very interesting in itself: Even though the “events” for the data source were not “visible” in the events viewer they were available! Under all properties etc.. you will find it:

That’s by the way also the place where you can toggle the flag for “ignoreRequestParams”!
The cool thing with those events: my defined document datasource is acting like an xPage in itself! Means I can change fields, run my custom validation approach etc. to my liking. Really neat!

bookmark_borderFRULE: pay attention to access rights, especially when working locally!!!!

In one of our applications we need to store more “sensitive” data with less access rights than the normal documents. In Lotus Notes the parent/child paradigma is just about perfect for this task.

My idea was to have a dialog popup where the user could enter this sensitive data.

I did a test in a test database, with a few hiccups I got everything working. The hiccups were kind of related to the fact:

  1. how do I ensure that every dialog would go to the same child document
  2. how do I ensure that the dialog has the same document mode (read/edit) than the parent
1. I solved by presaving a child, getting it’s unid and adding it to the document source. That was per se not sooo complicated disregarding the fact that the functionality was located on a custom control.
2. Was a bit a weireder (german-english for “more weired”) issue. On the test db all worked fine. The dialog’s document source edit mode was put to the same as the parent document and that worked.
In the actual db where I needed this feature I built everything 100% identical, yet the dialogbox was never going to edit mode. By accident then i figured: if the fields are not bound to the document then they can be edited. Of course they wouldn’t be saved also. So what the heck was going on? Then this morning I had an “almost” revelation: it must be access rights!!! I was sure, not being at the computer, this must be related to insufficient access rights for “anonymous”. When I was back at my computer I was disappointed to find out: that was not the case! Same access rights for anonymous on both dbs.
So I did what I usually do when I’m clueluess: tried to explain it to someone “stupid” (stupid in the sense of having no idea what an xPage is).
And guess what: this time the demo effect worked the other way around! Instead of NOT working during demoing, it actually worked!
So I was happy to find out that my approach did work.
But what was the problem when it did not work?
First you must know: I almost 100% of the time work locally (so I can debug easily my Java code), and working at home i’m not even “online”!
Well.. turns out in the test application I had “Enforce consistent ACL” checked. In the actual application this was unchecked, but by accident during demo I was connected to the server (ie. location=online). So during the demo by some interesting feature I cannot explain the web server could, even though local, determine (cause I was online) the appropriate author access for anonymous, hence the whole thing worked!
Summary:
Do not forget to think about the fact that xPages ARE indeed still Lotus Domino applications with all their nice access rights. And if you work locally ensure that you have always checked “Enforce consistent ACL”!