Wednesday, April 17, 2013

More powerful buttons

Photo by Keoni Cabral

Up to version 4.1, Orbeon Forms had a few configurable buttons to specify what happens with form data:

  • The “Save” button saves data to the database.
  • The “Submit” button saves data and shows a dialog after saving (with options to clear data, keep data, navigate to another page, or close the window).
  • The “Send” (AKA “workflow-send”) button saves the data and then allows:
    • sending an email
    • sending form data to a service
    • redirecting the user to a success or error page

The reason these specific buttons came to be is mostly historical: as more functionality was needed over time, more buttons were added with properties to configure them. The distinction between “Submit” and “Send” in particular is arbitrary!

Since recently we’ve been asked for more options, we used the opportunity to implement a much more flexible system.

First, let’s start by saying that we don’t want to implement a full-fledged workflow engine: that’s something other products do better than Orbeon Forms. Instead the idea is to be able to specify very simple sequential processes to execute when the user presses a button.

We started with specifying a list of reusable actions:

  • validate: validate form data
  • save: save data via the persistence layer
  • success-message: show a success message
  • error-message: show an error message
  • pdf: create a PDF file from the data
  • email: send an email
  • send: send the data to an HTTP service
  • review, edit, summary: navigate to these Form Runner pages
  • navigate: navigate to an external page
  • visit-all: mark all controls as visited
  • unvisit-all: mark all controls as not visited
  • expand-all: expand all sections
  • collapse-all: collapse all sections
  • result-dialog: show the result dialog
  • captcha: trigger the captcha
  • success: complete the process
  • process: run a sub-process

Then we needed to:

  • specify which of these actions to run and in which order
  • decide what to do when they succeed or fail
  • decide how to associate them with buttons

We came up with a very simple syntax which you place in configuration properties. For example, the good old “Save” button is specified this way:

require-valid
then save
then success-message("save-success")
recover error-message("database-error")

Notice that there are action names, like save and success-message (require-valid itself is a sub-process which runs a number of steps and stops processing if the data is not valid), and two different combinators, then and recover. When an action succeeds, then is used to specify what is the following action. When an action fails, recover can be used to specify what action to do in that case.

So in the example above what you want to say is the following: start by validating the data, then in case of success save the data, and then if that’s successful show a success message. If saving has failed, then show an error message.

A process which just saves the data without checking validity and shows success and error messages looks like this:

save
then success-message("save-draft-success")
recover error-message("database-error")

Validating and sending data to a service looks like this:

require-valid
then send("oxf.fr.detail.send.success")

Some actions can take parameters. In the example above we point to properties to configure the send action. This means that, within a single process, you can have any number of send actions which send data to various services. This also allows you to have separate buttons to send data to different services. These two scenarios were not possible before.

We are keeping but deprecating these buttons:

  • save (which uses a property to determine whether to validate or not)
  • submit
  • workflow-send

And we introduce new buttons with predefined behavior:

  • save-final: validate and save to the db
  • save-draft: save to the db without validating
  • send: validate and send to a service

In fact all buttons can do the same tasks if they are configured appropriately! But by default the buttons above are preconfigured to do different tasks, for convenience.

So how do you can figure things? Say you want to specify a couple of buttons on your “acme/hr” form. Like before, you define a property:

<property
  as="xs:string"
  name="oxf.fr.detail.buttons.acme.hr"
  value="save-draft send"/>

This places “Save” and “Send” buttons on the page. Each button is automatically associated with processes of the same names (save-draft and send). These particular buttons and process names are standard, but we can override them specifically for our form. Again, this is done with a property:

<property
  as="xs:string"
  name="oxf.fr.detail.process.send.acme.hr"
  value='require-valid
         then pdf
         then email
         then send("http://example.org/")
         then navigate("/success")
         recover navigate("/failure")'/>

Button labels can be overridden as well, as was the case before:

<property
  as="xs:string"
  name="oxf.fr.resource.*.*.en.detail.buttons.send"
  value="Fancy Send"/>

All the configuration above for a button called send could have been done with an entirely custom button named foo.

We hope this shows a little bit what’s now possible! This feature will be available in Orbeon Forms 4.2.

No comments:

Post a Comment