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”!

bookmark_borderINDIP/CRYFH: having legacy Lotus Script code do your work!

In our company we do have a wonderful, flexible, proven, stable own-built workflow engine, but it is written in Lotus Script code. baah..

I didn’t want to rewrite the whole thing in Java…

But you may now think: hey why does he not run an agent?

I tried, and I failed, how else!!! Always the way I understand things, they will never really work!

Luckily I then I found Kathy Browns article how to do it and it works!!!

Solution:

  • put some value into the NotesDocument to “control” your logic in the LS agent
  • run the agent with the document as parameter
  • write some field in the agent into your document: NO NEED TO SAVE THE DOCUMENT!!!
  • read the result from you NotesDocument in your xPage code and process it
My mistake was: I tried to update a field which was visible on the xPage, and here my approach failed. 
Or could I manually “refresh” the xPage after the agent has run? Seems to be maybe a “timing” issue, cause the value is there, it’s only just not displayed in the field on the xPage. Anyone?

How we use this technique with our workflow engine:
The engine can require a comment to be given for certain target state. But the target state I don’t know yet in the xPage, cause this logic is handled in LS. But with the technique above I can create an agent provide the target action (reject/approve or something else), calculate the target state and find out if for that state a comment is required. If so I can then call a dialog in the xPage where the user can enter his comment. In the agent which runs then, to process the workflow, I then simply read this comment field and process it. In our case it’s written to a workflow log and also included in the notification to the approver of the next state
Nathan: did you read my comment to your comment? I bow to you..