« Back to home

User friendly edit buttons with XPages

I wrote my first full application using XPages, the new IBM Domino web development environment based on Java Server Faces (JSF). It’s been quite a brutal learning curve, even given that I already knew both Java and JavaScript in some depth. Some things that are trivial ‘the old way’ (in Notes) are harder on the web with XPages.

For example, when you try to edit a document and don’t have permission, the Notes client will give you an error message. Old-style web applications built in Domino will demand that you authenticate, and eventually give you some sort of error message as well. But XPages… It just sits there. You click the button, nothing happens.

I know from experience that when clicking a button doesn’t result in any visible activity, the first thing the user does it contact me to say that the application is broken. The XPages default behavior was clearly unacceptable.

One approach you might consider is to make the Edit button only appear if the user has permission to edit the document. There are two problems with that approach though: a technical problem and a usability problem.

The technical problem is that it’s quite hard to work out whether a given user can edit a given document. You have to check the database access control list to see if the user is at least an editor (with permission to edit any document). If not, you have to access the document and pull out the author list field, then examine it entry by entry for the user’s ID.

Except that’s not quite good enough either, because author list fields can also include groups and roles, so really you need to check the user’s group and role membership as well. So for every value in the author field, you’ve got to check one of three arrays. And that’s assuming you know the name of the author list field; for a general solution, you have to pull all the fields from the document, examine each one to see if it’s an author list field, and if so then run all its values through the list checking.

So, technically it’s messy and slow. And there’s the usability problem too: controls which appear and disappear apparently at random, or which move around, tend to degrade the user’s trust in the system. You could show a disabled edit button, but that’s not much of an improvement, because it doesn’t tell the user why they can’t edit something.

In general, the web applications I build are supposed to be managed by the users themselves; typically the answer to “Why can’t I edit X?” is “Ask John Smith to add you to the permissions list for your location”, or “You need to do Y first”. I want the application to be able to communicate that problem resolution information directly, so I don’t get a support ticket (or worse, a message in my personal inbox).

So, how to do such a thing with XPages? It turns out to be surprisingly simple.

Start by creating your button. Add the usual Simple Action to switch the appropriate document to Edit mode. You should end up with code something like this:

<xp:button value="Edit" id="button1" 
 rendered="#{javascript:!document2.isEditable();}">
  <xp:eventHandler event="onclick" submit="true" 
   refreshMode="partial" refreshId="editPanel">
    <xp:this.action>
      <xp:changeDocumentMode mode="edit" var="document2"/>
    </xp:this.action>
  </xp:eventHandler>
</xp:button>

Note the use of the rendered property to hide the button if the document is already in edit mode. Note also the use of partial refresh to target a panel which contains all the editable controls bound to document fields, rather than refreshing the entire page.

Now for the cunning part. Domino lets you attach an actionGroup to the eventHandler instead of an action. If you do so, you can then add multiple actions to the button, and they will be executed serially. To do this via the GUI, just add a second simple action to the root of the event actions of the control. Pick the ‘Execute script’ simple action, and add some JavaScript code:

getComponent('editFailWarning').setRendered(!document2.isEditable());

The final step is to add a component somewhere, give it an ID of editFailWarning, give it a rendered property of false, and make it contain your error message. You can use any XSP element for the purpose — a div, a computed field, or even just an xp:paragraph.

Here’s the code for the end result:

    <xp:paragraph id="editFailWarning" rendered="false">You cannot edit this
     document until you are added to the FooAppDocumentEditors group in the 
     system directory. Contact Kevin Mitnick for assistance.</xp:paragraph>
    <xp:button value="Edit" id="button1" 
     rendered="#{javascript:!document2.isEditable();}">
      <xp:eventHandler event="onclick" submit="true" 
       refreshMode="partial" refreshId="bundlePanel">
        <xp:this.action>
          <xp:actionGroup>
            <xp:changeDocumentMode mode="edit" var="document2" />
            <xp:executeScript>
              <xp:this.script>javascript:
    getComponent('editFailWarning').setRendered(!document2.isEditable());
              </xp:this.script>
            </xp:executeScript>
          </xp:actionGroup>
        </xp:this.action>
      </xp:eventHandler>
    </xp:button>

When the user clicks the Edit button, XPages first attempts to switch to edit mode via a simple action. Since this can fail silently, the second simple action immediately checks the isEditable() method of the document to see if the mode switch succeeded. Depending on the result, it either shows or hides the error message. Either way, a partial refresh is performed.

So from the user point of view, they click the Edit button, and a helpful error message pops up telling them (a) that they can’t edit, (b) why they can’t edit, and © who to contact for assistance.