bookmark_borderBUG in Lotus Notes applications: saving documents with private folders (not stored on desktop) and folder references enabled

Long time no see, but stay tuned, there will be more updates again soon regarding the xPages development from my side.

This week we have a very nasty bug. When people tried to save documents they got an “You are not authorised to perform that operation” error message.

After a while fiddling around we figured: We copied&pasted documents we could save the newly copied documents. So where was the difference? We used TeamStudio Delta to figure out differences between the two documents. It turned out mostly just the “Folder References” fields.

I googled and surely enough found this bug report: http://www-01.ibm.com/support/docview.wss?rs=463&uid=swg21099783

It basically states that users do get this error when folder references are enabled and the documents are stored in private folders which are not stored on the desktop.

maybe this information helps anyone out there to not “desperate” if faced with a similar issue.

yours

Michael

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!